ObjectPool - Grisgram/gml-raptor GitHub Wiki

Object pooling is a very important technique to save precious memory and time.

Imagine a shooting game, where hundreds of bullets are created, swoooshing over the screen for a fraction of a second and then explode or disappear. This would mean hundreds and hundreds of "Create" and "Destroy" events, permanent memory allocation and could throw your garbage collector into a serious depressive crisis. Don't do that.

Even with smaller amounts of instances, something like little critters that run around in your level to make it look more "alive" and disappear after some time or any imaginable "recurring" type of objects: Why should you always create new instances? Wouldn't it be better, to just "take them out of the scene until you need them again"? Without having the full overhead of creation and destruction every time?

This is, where object pools come in. Imagine them as a kind of storage box, where you put your objects in, and you take some out if you need them, and put them back in, when you do not need them now. You put them back in, you do not throw them into your trashcan. That's the difference.

Now, the object pools in raptor are super easy to use and cause no overhead for you as developer. You just use something else, instead of instance_create_layer.
GameMaker helps us here natively with its mechanic to "activate/deactivate" objects. This is, what the object pools use. Deactivated objects do not cost rendering time (in contrast to "parking" unneeded objects outside of the visible area). Deactivated objects receive no events, are not in the render loop, they keep existing and don't get deallocated. That's all. And that's good.

Using the object pools

There are two main methods, which will simply replace every instance_create_layer and instance_destroy calls in your game. That's all you need to do. Just replace the methods used and your objects are pooled.

pool_get_instance

This is the replacement for instance_create_layer and instance_create_depth.

pool_get_instance(pool_name, object, layer_or_depth_if_new)

You supply a pool name (more on that below), specify the object to take from the pool, and in case, a new instance is needed (no more objects of this type in the pool), you specify either a layer name or a number as depth as the third argument. If you specify a number here, instance_create_depth will be used internally, otherwise the object is created through instance_create_layer.
The third argument is either the name of a layer or the depth, where the instance shall be placed, if a new one is created.

Here's an example:

var next_bullet = pool_get_instance("Bullets", obj_bullet, "bullet_layer");

pool_return_instance

Instead of destroying your instance, you send it back to the pool with this function. It knows, from which pool it has been taken and will be returned to the very same pool.

pool_return_instance(instance = self)

Here's an example for the bullet above. Let's assume, we are in the "leaves room" or any other event, where the bullets shall stop rendering.

pool_return_instance(self);

That's it for the most simple usage of object pooling.

Object pool callbacks

But of course, there's more. Because at the moment, we have taken one very important step out of sight with these pools: The Code in the create event! What, if we need to set up things every time a bullet appears? If the create event does never run again, how can we do that?

The answer is quite simple: The object pools of raptor utilize two callback functions: onPoolActivate and onPoolDeactivate which you implement in your create event of the object.

Callback Replaces Invoked when
onPoolActivate Create Object is created OR reactivated from pool
onPoolDeactivate Destroy Object is removed from the scene

onPoolActivate

This callback is invoked every time, an object is taken out of a pool, even when a new object is created.
So, for pooled object, you put your initialization code in this function, and all is good. The exact timing of this callback is "just after it has been activated or created", it is already in the scene when this runs.

These values are applied to the object by default, before onPoolActivate gets invoked:
The x and y position of the object requesting this object from the pool. So, if you player object requests a new bullet from the pool it will by default be placed at the player object's position. That's why you may want to adjust that in your onPoolActivate callback.
If the requesting object can't be determined (self does not point to an object instance), no coordinates get applied.

[!IMPORTANT] onPoolActive also gets called, when a new instance is created!
This makes the function a complete replacement of everything, you would have done in the Create event of non-pooled objects. Just do your "Create" in this function.

Here's an example:

onPoolActivate = function() {
    PLAYER_OBJ.ammo--;
    x = PLAYER_OBJ.x - 30;
    y = PLAYER_OBJ.y - 30;
    image_alpha = 0.5;
    image_blend = c_red;
    // ...and a thousand other init things you may want to do
}

onPoolDeactivate

This callback occurs, whenever an object is put back into a pool. The exact timing is "just before the object gets deactivated", so it is still in the scene when this callback runs.

onPoolDeactivate = function() {
    global.achievements.bullets_fired++;
    image_alpha = 0;
}

REMEMBER: Both of these methods should be placed in the Create event of your object!


Object pool names

In theory, you can put all your pooled objects into a single pool. In reality, you might not want that. I suggest, that you create one pool (i.e. use a unique pool name) for each type of pooled object, like "Bullets", "Critters", "Sparks", ... etc.
The more different types of objects you put into a single pool, the longer it takes to find an object of the requested type. If all your pools only contain one single type of object, you run the pools at maximum performance, as every request can always take the first available object out of the pool, it never has to search through the pool to find an object of the requested type.

I can not recommend enough to use those object pools! They make a significant difference in the performance of your game.