-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Add entities pool #1954
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
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
src/components/scene/pool.js
Outdated
| 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
ngokevin
left a comment
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.
src/components/scene/pool.js
Outdated
| * @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
src/components/scene/pool.js
Outdated
| @@ -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
src/components/scene/pool.js
Outdated
| 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
tests/components/scene/pool.test.js
Outdated
|
|
||
| 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.
examples/test/pool/index.html
Outdated
| 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>';
}
});
docs/components/pool.md
Outdated
| |----------|--------------------------------------------------------------------------------------|---------------| | ||
| | 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 ? |
ngokevin
left a comment
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
examples/test/pool/index.html
Outdated
| 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.