Skip to content
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

Merged
merged 10 commits into from
Oct 5, 2016
Merged

Add entities pool #1954

merged 10 commits into from
Oct 5, 2016

Conversation

dmarcos
Copy link
Member

@dmarcos dmarcos commented Sep 30, 2016

No description provided.

var laserEl;
var position;
if (evt.keyCode !== 32) { return; }
laserEl = el.sceneEl.components.pool.requestEntity();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about requestEntity('laser')?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does that mean?

Copy link
Member Author

@dmarcos dmarcos Sep 30, 2016

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>

Copy link
Member Author

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()

Copy link
Member

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}
},
Copy link
Member

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');
Copy link
Member

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

Copy link
Member

@ngokevin ngokevin left a 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', {
Copy link
Member

@ngokevin ngokevin Oct 1, 2016

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.

Copy link
Member Author

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

Copy link
Member

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';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intermediate vars necessary?

Copy link
Member Author

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;
Copy link
Member

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}
Copy link
Member

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.

Copy link
Member Author

@dmarcos dmarcos Oct 1, 2016

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.

Copy link
Member

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?

Copy link
Member Author

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) {
Copy link
Member

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

Copy link
Member Author

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);
Copy link
Member

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

Copy link
Member Author

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

Copy link
Member

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();
Copy link
Member

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) {
Copy link
Member

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

Copy link
Member Author

@dmarcos dmarcos Oct 1, 2016

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');
Copy link
Member

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() {
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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

Copy link
Member

@ngokevin ngokevin Oct 5, 2016

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 |
Copy link
Member

@fernandojsg fernandojsg Oct 4, 2016

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;
Copy link
Member

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();
Copy link
Member

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);
Copy link
Member

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

@fernandojsg
Copy link
Member

It's looking good to me, @ngokevin ?

Copy link
Member

@ngokevin ngokevin left a 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.
Copy link
Member

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
Copy link
Member

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() {
Copy link
Member

@ngokevin ngokevin Oct 5, 2016

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');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test dynamic?

@dmarcos
Copy link
Member Author

dmarcos commented Oct 5, 2016

I added a test for a dynamic pool and made the example screen message a component. I'm merging this....

@dmarcos dmarcos merged commit 1b3da9b into aframevr:master Oct 5, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants