-
-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add entities pool #1954
Add entities pool #1954
Conversation
var laserEl; | ||
var position; | ||
if (evt.keyCode !== 32) { return; } | ||
laserEl = el.sceneEl.components.pool.requestEntity(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about requestEntity('laser')
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does that mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you want to have multiple pools you add multiple pool components and call requestEntity on them:
<a-scene pool__laser="mixin: laser; size: 10" pool__bomb="mixin: bomb; size: 10"></a-scene>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sceneEl.components.pool__laser.requestEntity()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aps sorry I didn't notice that pool
was the name of the component and not a global that will store all the pools. I mistook this use case with accessing directly the system.
schema: { | ||
mixin: {default: ''}, | ||
size: {default: 0} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add another parameter to force the requestEntity
to return a new entity even if there's no available entity on the pool, it will create a new one and pushed to the pool and return it
this.pool = []; | ||
this.pooledEls = []; | ||
for (var i = 0; i < this.data.size; ++i) { | ||
el = document.createElement('a-entity'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Takes this part out from the loop to a another function createEntity/pushNewEntity/...
, so it could be reused on requestEntity
when the previous described param is set to true and there's no available entity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember documentation once everything's ready to go.
* @member {array} pooledEls - Entities of the pool in use. | ||
* | ||
*/ | ||
module.exports.Component = register('pool', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Common stuff I see online refer to it as "Object Pooling" so consider entity-pool
to be more clear. I might've not immediately understood what a pool
component did, but entity-pool
I might've.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an application requires pools I expect to often see multiple instances pool__bullets
, pool__enemies
... If we rename pool
to entity-pool
you will end up with entity-pool__enemies
, entity-pool_bullets
, Is not that to verbose? @fernandojsg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Entity pool
is more clear than just pool
but due its nature, we'll have plenty of instances on a-scene therefore we'll have entity-pool__whatever
as @dmarcos said and it looks quite ugly, so I would choose to use just pool
|
||
init: function () { | ||
var el = this.el; | ||
var geometry = 'primitive: box; height: 2; width: 0.1; depth: 0.1'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intermediate vars necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes lines shorter. Easier to read
@@ -0,0 +1,103 @@ | |||
var debug = require('../../utils/debug'); | |||
var register = require('../../core/component').registerComponent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/register/registerComponent
schema: { | ||
mixin: {default: ''}, | ||
size: {default: 0}, | ||
dynamicSize: {default: false} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dynamicallySize
might differentiate it as a boolean. dynamicSize
sounds like it'd take a size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dynamicallySize is a mouthful. Adverbs should be banned from earth.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @ngokevin that dynamicSize
sounds like it expects a numeric value instead of boolean, but I don't like dynamicallySize
either, what about just dynamic
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... and to be correct you would have to pair it with a participle: dynamicallySized
} | ||
}, | ||
|
||
update: function (oldData) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i like all lifecycle handlers on top in order they're invoked, and then internal methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is that stated? Is not a policy difficult to understand, enforce and context dependent? Sometimes we sort things alphabetically and sometimes we don't. I recently realized that the order of variables or methods in a file does not help me much. On the other hand it introduces friction and sometimes you have to do weird contortions to conform to the policy like for instance when sorting the variables alphabetically.
var pooledEls = this.pooledEls; | ||
return function () { | ||
if (pooledEls.indexOf(this) === -1) { return; } | ||
playMethod.call(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line doesn't have test coverage
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not be too anal about 100% coverage
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small test to prevent potential bugs, good trade off (we've had plenty bugs that could've been caught with tests). At least test the below case of dynamically fetching from any empty pool.
warn('Requested entity from empty pool ' + this.name); | ||
return; | ||
} | ||
this.createEntity(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line doesn't have test coverage
/** | ||
* Used to return a used entity to the pool | ||
*/ | ||
returnEntity: function (el) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe releaseEntity
? returnEntity
sounds like it'll return an entity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually returns the entity to the pool. Release is a term used in memory allocation / deallocation which is the opposite idea of using a pool
|
||
suite('pool', function () { | ||
setup(function (done) { | ||
var el = this.sceneEl = document.createElement('a-scene'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do:
var sceneEl = helpers.entityFactory().sceneEl;
var mixinEl = helpers.mixinFactory('test', {material: 'color: red'}, sceneEl);
This will create <a-assets>
and <a-mixin>
for you.
var sceneEl = document.querySelector('a-scene'); | ||
var message = '<p>Left and right arrows to move and space bar to shoot</p><p>Used Pool Entities '; | ||
sceneEl.addEventListener('loaded', updateMessage); | ||
function updateMessage() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should encourage a component here:
- Don't have to wait for the scene to load. Lifecycle is handled for you
- Don't have to create your own RAF.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to see this right there on the index.html and not hidden in a component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can still define it in the html file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want it, it's a bit cleaner:
var message = '<p>Left and right arrows to move and space bar to shoot</p><p>Used Pool Entities ';
AFRAME.registerComponent('update-pool-message', {
schema: {type: 'selector'},
tick: function () {
var poolComponent = this.el.components.pool;
var poolSize = poolComponent.availableEls.length + poolComponent.usedEls.length;
var usedPoolEntities = this.el.components.pool.usedEls.length;
this.data.innerHTML = message + usedPoolEntities + '/' + poolSize + '</p>';
}
});
|----------|--------------------------------------------------------------------------------------|---------------| | ||
| mixin | Mixin used to initialize the entities of the pool. | '' | | ||
| size | the number of preallocated entities in the pool | 0 | | ||
| dynamicSize | the pool grows automatically if more entities are requested after reaching the current size | false | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dynamic
instead of dynamicSize
el = this.availableEls.shift(); | ||
this.usedEls.push(el); | ||
el.setAttribute('visible', true); | ||
return el; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we should call el.play()
by default before returning the entity as it would be the most common case, isn't it?
warn('Requested entity from empty pool ' + this.name); | ||
return; | ||
} | ||
this.createEntity(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about insert a warning message anyway even if dynamic=true
so the user could adjust the pool size better?
*/ | ||
createEntity: function () { | ||
var el = document.createElement('a-entity'); | ||
el.play = this.wrapPlay(el.play); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about adding el.pool = this;
so each entity could have a reference of its pool, and for example when a bullet collides with an object it could do this.pool.returnEntity(this)
without need to store a reference to the pool by hand
It's looking good to me, @ngokevin ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r+wc
* in a game where you want to reuse enemies entities. | ||
* | ||
* @member {array} availableEls - Available entities in the pool. | ||
* @member {array} useedEls - Entities of the pool in use. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
<a-scene pool="mixin: enemy; size : 10"></a-scene> | ||
``` | ||
|
||
## Properties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should probably document the methods as well since those are part of the API
var sceneEl = document.querySelector('a-scene'); | ||
var message = '<p>Left and right arrows to move and space bar to shoot</p><p>Used Pool Entities '; | ||
sceneEl.addEventListener('loaded', updateMessage); | ||
function updateMessage() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want it, it's a bit cleaner:
var message = '<p>Left and right arrows to move and space bar to shoot</p><p>Used Pool Entities ';
AFRAME.registerComponent('update-pool-message', {
schema: {type: 'selector'},
tick: function () {
var poolComponent = this.el.components.pool;
var poolSize = poolComponent.availableEls.length + poolComponent.usedEls.length;
var usedPoolEntities = this.el.components.pool.usedEls.length;
this.data.innerHTML = message + usedPoolEntities + '/' + poolSize + '</p>';
}
});
setup(function (done) { | ||
var el = helpers.entityFactory(); | ||
var sceneEl = this.sceneEl = el.parentNode; | ||
sceneEl.setAttribute('pool', 'mixin: test; size: 1'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test dynamic
?
I added a test for a dynamic pool and made the example screen message a component. I'm merging this.... |
No description provided.