Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

1 decade ago by DaveVoyles

I'm trying to create object pools for my 2D shmup. I can't seem to wrap my head around the pooling though.

Once I reach the maximum number of bullets available in my pool (15), the game just stops firing. Would any of you mind taking a look at my code and seeing if there are any glaringly obvious problems with it?

ig.module(
'game.entities.BulletGenerator'
)
.requires(
'impact.entity',
'game.entities.bullet'

)
.defines(function () {

EntityBulletGenerator = ig.Entity.extend({
_wmIgnore: false,
_wmDrawBox: true,
_wmBoxColor: 'rgba(128, 28, 230, 0.7)',
_wmScalable: true,
lifetime: .5,
particles: 3,
colorOffset: 0,
size: { x: 8, y: 8 },
instances: [],
pool: [],
name: 'bulletGenerator',
maxInstances: 15,
spawnEntity: EntityBullet,
particleLifeTime: 1,
fadetime: 1,
isAlive: null,

init: function (x, y, settings) {
this.parent(x, y, settings);
this.idleTimer = new ig.Timer();
isAlive = true;
},

update: function () {
// Create a new bullet after timer expires
if (ig.input.pressed('altShoot')) {
this.createBullet();
}
},

createBullet: function () {
//console.log("create particle")
var instance;

// Sets X and Y position to spawn bullet at player
var x = ig.game.player.pos.x;
var y = ig.game.player.pos.y;

// As long as our instances is less than max, then spawn a new bullet
if (this.instances.length < this.maxInstances) {
instance = ig.game.spawnEntity(EntityBullet, x, y);

// Add new bullet to end of array, return length of array
this.instances.push(instance);
//TODO: What is the difference between these two? Ask Jesse
// Add new bullet to end of array, return length of array
this.pool.push(instance);
}

// If we've exceeded the max pool size,
else {
// Remove last bullet from array and return it
instance = this.pool.pop();
//TODO: What does this do?
// if (instance)
// instance.reset(x, y);
}
return instance;
},

/********************
* useObject
* Makes use of the object stored in the pool
********************/
useBullet: function (object, attributes){
// Set poolArr and entityType depending on which entity is being used
var poolArr = null;
var entityType = null;
var entity = null;
switch (object) {
case 'bullet':
poolArr = this.instances;
entityType = 'EntityBullet';
break;
}

// Get first entity in relevant pool
entity = this.instances[0];

// If the entity is not already in use...
if (entity.inUse === false) {

// Set any additional attributes
for (var propt in attributes) {
entity[propt] = attributes[propt];
}

// Initialize entity
entity.init();
entity.inUse = true;

// Move the now used entity to the end of the pool
this.moveArrElement(poolArr, 0, poolArr.length - 1);
}

// If the entity IS already in use, either the array is cluttered or there are no free entities left in the pool...
else {
// Loop through pool backwards
for (var i = poolArr.length - 1; i > 0; i--) {
entity = poolArr[i];
// If the entity is not in use, move it to the front of the array
if (!entity.inUse) {
this.moveArrElement(poolArr, i, 0);
}
}

//If no available entities were found, spawn a new entity.
//TODO: We don't want to spawn additional entities. Just keep re-using what we have in the pool
//if (!foundAvailableEntity) {
// ig.game.spawnEntity(entityType, 0, 0);
//}
this.useBullet(object, attributes);
}
},

/********************
* removeEntity
* Deactivates entity by setting inUse bool to false
********************/
removeEntity: function (entity) {
entity.inUse = false;
},


/********************
* clear
* Removes all entities, used to restart a level
********************/
clear: function () {
// For all bullets in the array
for (var i = 0; i < this.instances.length; i++) {
// Kill them
this.instances[i].kill();
}
// Set the total number of items in array to 0
this.instances.length = 0;
}
});
});

1 decade ago by drhayes

Hi there. Please wrap your code sample in a code block next time (##). It makes it easier to read and makes us better able to help. ( =

It looks like you&039;re calling #createBullet when the user is pressing altShoot. From your code, you probably want to call useBullet instead.

useBullet looks like it has some problems though:

* It&039;s using a method called #moveArrElement that doesn't exist.
* It calls itself at the end instead of returning the entity.
* The code at the beginning looks like this was meant to be more general, but now you&039;re only using it for bullets. I don't think you need to #object parameter at all.

Are you sure you need object pooling? Were you having performance problems related to making too many bullets? How many bullet entities were you making?

1 decade ago by DaveVoyles

Very informative post, thank you!

I didn&039;t realize that I needed to use # in order to post the code. Thank you!

I'm a knucklehead, and realized soon after that I need to use useBullet instead of CreateBullet as well. I'm working on that now.

Valid points there too, I'll work that up now and let you know what I come up with.

For the web version of my game (which I'm having some issue with the baking), I don't have much of a performance problem, but this is my win8 port, and performance takes a HUGE hit when a large number of bullets and particles are on screen at once.

Generally, I'd say "no", I don't need pooling, but I can see exactly where the performance issues are (specifically during the creation and killing of bullets and particles), and additionally I'd like to really have a solid understanding of object pooling.

Thanks again for your time; I'll work up these changes and let you know what I come up with!

1 decade ago by DaveVoyles

Here's what I have now, which is definitely more effective. I'm still getting spikes, however. I'm guessing it has to do with moving the bullets within the array, but I'm not quite certain.

The spikes are VERY brief, and on the performance monitor show as purple and pink (which I believe has to do with entities)

ig.module(
    'game.entities.BulletGenerator'
)
    .requires(
    'impact.entity',
    'game.entities.bullet'

)
    .defines(function () {

        EntityBulletGenerator = ig.Entity.extend({
            _wmIgnore: false,
            _wmDrawBox: true,
            _wmBoxColor: 'rgba(128, 28, 230, 0.7)',
            _wmScalable: true,
            lifetime: .5,
            particles: 3,
            colorOffset: 0,
            size: { x: 8, y: 8 },
            instances: [],
            pool: [],
            name: 'bulletGenerator',
            maxInstances: 15,
            spawnEntity: EntityBullet,
            particleLifeTime: 1,
            fadetime: 1,
            isAlive: null,

            init: function (x, y, settings) {
                this.parent(x, y, settings);
                this.idleTimer = new ig.Timer();
                isAlive = true;
            },

            update: function () {

            },

            /********************************************************
            * addToPool
            * Adds objects to the beginning of array
            ********************************************************/
            addToPool: function (entity, arr) {
                arr.unshift(entity);
                console.log("Bullet Generator - addToPool");
            },

            createBullet: function () {
                console.log("Bullet Generator - createBullet");
                var instance;

                // Sets X and Y position to spawn bullet at player 
                var x = ig.game.player.pos.x;
                var y = ig.game.player.pos.y;

                // As long as our instances is less than max, then spawn a new bullet 
                if (this.instances.length < this.maxInstances) {
                    console.log("Bullet Generator - createBullet IF");
                    instance = ig.game.spawnEntity(EntityBullet, x, y);

                    // Add new bullet to end of array, return length of array
                }

                    // If we've exceeded the max pool size,    
                else {
                    console.log("Bullet Generator - createBullet ELSE");
                    // Remove last bullet from array and return it
                    instance = this.instances.pop();
                    //TODO: What does this do?
                        if (instance)
                            instance.reset(x, y);
                }
                return instance;
            },

            /********************************************************
            * useObject
            * Makes use of the object stored in the pool
            ********************************************************/
            useBullet: function (entityType, attributes) {
                console.log("Bullet Generator - useBullet");

                var entity = null;

                // Get first entity in relevant pool
                entity = this.instances[0];

                // If the entity is not already in use...
                if (entity.inUse === false) {
                    console.log("Bullet Generator - useBullet IF");

                    //  Set any additional attributes
                    for (var propt in attributes) {
                        entity[propt] = attributes[propt];
                    }

                    // Initialize entity
                    entity.init();
                    entity.inUse = true;

                    // Move the now used entity to the end of the pool
                    this.moveArrElement(this.instances, 0, this.instances.length - 1);
                }

                    // If the entity IS already in use, either the array is cluttered or there are no free entities left in the pool...
                else {
                    var foundAvailableEntity = false;
                    
                    console.log("Bullet Generator - useBullet ELSE");
                    // Loop through pool backwards
                    for (var i = this.instances.length - 1; i > 0; i--) {
                        entity = this.instances[i];
                        // If the entity is not in use, move it to the front of the array
                        if (!entity.inUse) {
                            this.moveArrElement(this.instances, i, 0);
                            foundAvailableEntity = true;
                        }
                    }

                    //If no available entities were found, spawn a new entity.
                    //TODO: We don't want to spawn additional entities. Just keep re-using what we have in the pool
                    if (!foundAvailableEntity) {
                        ig.game.spawnEntity(entityType, 0, 0);
                    }
                    this.useBullet(entityType, attributes);
                }
            },

            /********************************************************
            * removeEntity
            * Deactivates entity by setting inUse bool to false
            ********************************************************/
            removeEntity: function (entity) {
                console.log(" Bullet Generator - removeEntity");
                entity.inUse = false;
            },


            /********************************************************
            * clear
            * Removes all entities, used to restart a level
            ********************************************************/
            clear: function () {
                // For all bullets in the array
                for (var i = 0; i < this.instances.length; i++) {
                    // Kill them
                    this.instances[i].kill();
                }
                // Set the total number of items in array to 0
                this.instances.length = 0;
            },

            /********************************************************
            * moveArrElement
            * Shifts items in the array
            ********************************************************/
            moveArrElement: function (array, old_index, new_index) {
                console.log('Bullet Generator - MmoreArrElement');
                if (new_index >= array.length) {
                    var k = new_index - array.length;
                    while ((k--) + 1) {
                        array.push(undefined);
                    }
                }
                array.splice(new_index, 0, array.splice(old_index, 1)[0]);
                return array;
            }

        });
    });

1 decade ago by DaveVoyles

Disregard that last message. I have a much cleaner (and 100% working!) version coming up shortly.

I just didn't want anyone to waste their time trying to resolve this when I've found the answer. Solution incoming.

1 decade ago by DaveVoyles

Here is my post about object pooling, which my code works 100% now:
http://davidvoyles.wordpress.com/2013/04/17/object-pooling-for-bullets-now-working-100/

And the code itself:

/*********************************************************************
 * BulletGenerator.js
 * Creates an array (pool) of bullets to be used by the player
 * with the intention of improved performance.
 *
 * The base of this code can be attributed to Liza Shulyayeva's object 
 * pooling tutorial: http://liza.io/a-first-try-at-object-pooling/
 * @copyright (c) 2013 Dave Voyles, under The MIT License (see LICENSE)
 *********************************************************************/

ig.module(
    'game.entities.BulletGenerator'
)
    .requires(
    'impact.entity',
    'game.entities.bullet'
)
    .defines(function () {

        EntityBulletGenerator = ig.Entity.extend({
            _wmIgnore: false,
            _wmDrawBox: true,
            _wmBoxColor: 'rgba(128, 28, 230, 0.7)',
            _wmScalable: true,
            size: { x: 8, y: 8 },
            instances: [],
            name: 'bulletGenerator',
            maxInstances: 15,

            /********************************************************
            * addToPool
            * Adds objects to the beginning of array
            ********************************************************/
            addToPool: function (entity, arr) {
                arr.unshift(entity);
            },


            /********************************************************
            * createBullet
            * Creates a bullet to be stored in array instance pool
            ********************************************************/
            createBullet: function () {

                var instance;

                // Sets X and Y position to spawn bullet at player 
                var x = ig.game.player.pos.x;
                var y = ig.game.player.pos.y;

                // As long as our instances is less than max, then spawn a new bullet 
                if (this.instances.length < this.maxInstances) {
                    instance = ig.game.spawnEntity(EntityBullet, x, y);

                    // Add new bullet to end of array, return length of array
                }

                    // If we've exceeded the max pool size,    
                else {
                    // Remove last bullet from array and return it
                    instance = this.instances.pop();
                }
                // Return a copy of the bullet, to be stored in the array
                return instance;
            },

            /********************************************************
            * useObject
            * Makes use of the object stored in the pool
            ********************************************************/
            useBullet: function (entityType, attributes) {
             //   console.log("Bullet Generator - useBullet");

                var entity = null;

                // Get first entity in relevant pool
                entity = this.instances[0];

                // If the entity is not already in use...
                if (entity.inUse === false) {
                    console.log("Bullet Generator - useBullet IF");

                    //  Set any additional attributes
                    for (var propt in attributes) {
                        entity[propt] = attributes[propt];
                    }

                    // Initialize entity
                    entity.init();
                    entity.inUse = true;

                    // Move the now used entity to the end of the pool
                    this.moveArrElement(this.instances, 0, this.instances.length - 1);
                }

                    // If the entity IS already in use, either the array is cluttered or there are no free entities left in the pool...
                else {
                    // We haven't found an entity which we can use
                    var foundAvailableEntity = false;           
                    console.log("Bullet Generator - useBullet ELSE");
                    
                    // Loop through pool backwards
                    for (var i = this.instances.length - 1; i > 0; i--) {
                        entity = this.instances[i];
                        
                        // If the entity is not in use...
                        if (!entity.inUse) {
                            // move it to the front of the array
                            this.moveArrElement(this.instances, i, 0);
                            // Now we no longer need to create a new entitiy (expensive to do so! )
                            foundAvailableEntity = true;
                        }
                    }

                    //If no available entities were found, spawn a new entity.
                    if (!foundAvailableEntity) {
                        ig.game.spawnEntity(entityType, 0, 0);
                    }                 
                }
            },

            /********************************************************
            * removeEntity
            * Deactivates entity by setting inUse bool to false
            ********************************************************/
            removeEntity: function (entity) {
                entity.inUse = false;
            },


            /********************************************************
            * clear
            * Removes all entities, used to restart a level
            ********************************************************/
            clear: function () {
                // For all bullets in the array
                for (var i = 0; i < this.instances.length; i++) {
                    // Kill them
                    this.instances[i].kill();
                }
                // Set the total number of items in array to 0
                this.instances.length = 0;
            },

            /********************************************************
            * moveArrElement
            * Shifts items in the array
            ********************************************************/
            moveArrElement: function (array, oldIndex, newIndex) {
                
                if (newIndex >= array.length) {
                    var k = newIndex - array.length;
                    while ((k--) + 1) {
                        array.push(undefined);
                    }
                }
                array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
                return array;
            }

        });
    });
Page 1 of 1
« first « previous next › last »