Using the new Unity Pooling API to optimize performance
In Unity, we know we can create a new instance of a GameObject from a Prefab by calling
Instantiate() and destroy it later by calling, surprise,
Destroy(). That’s fine, nothing wrong with that. This is the way.
But if we need to allocate a large number of objects and destroy them in a short period of time, that would have an impact on performance. So here it comes Object Pooling to save the day.
Object Pooling is a creational pattern that allows us to pre-instantiate a set of objects and reuse them instead of allocating and destroying them on demand
We’re going to use the new Unity Pooling API, specifically the ObjectPool class, which uses a stack to hold a collection of object instances. Since it’s a generic class, our goal is also to create a class to manage it that we can reuse.
The problem is that the type we’re going to use needs a reference to the pool, so that we can release the object when we’re done with it by calling the ObjectPool’s
We don’t want every class to redefine the same method. The idea is instead to create a base class that derives from
MonoBehaviour, holds a reference to the pool and has a base implementation for the method we’re going to use to release the object.
ObjectPool is an implementation of the
IObjectPool interface, we’re going to use this type to hold the reference.
Program to an interface, not an implementation
Next, we’re going to create a class to manage the pool.
We’ll work with
GameObject type because it’s the base class for all the entities, but our class we’ll hold a reference to a Prefab that inherits from the Poolable class we defined earlier.
To create a new
ObjectPool instance, we need to define some callbacks to pass to its constructor:
CreatePooledItem, used to create a new instance when the pool is empty
OnTakeFromPool, called when the instance is taken from the pool
OnReturnedToPool, called when the instance is returned to the pool
OnDestroyPoolObject, called when the element could not be returned to the pool due to the pool reaching the maximum size
We also need to define a default capacity and a maximum size for the pool. The boolean value is for enabling collection checks.
Few things to notice here. When the object gets created, we assign the reference to the pool by getting its
Poolable component. We may perform some cleanup and state resetting when the object is taken from or returned to the pool, during the object’s lifecycle within
OnDisable(), or delegate the task to the class responsible for spawning the object.
Now, let’s create a class that derives from
Poolable and defines a projectile.
Let’s create a Prefab to attach this script to, together with a trigger collider.
Update, we release the object when is out of camera (Boundaries is just an enum I made that holds this point) by calling the function we inherit from the Poolable class. Same thing when the projectile hits another
GameObject tagged as “Enemy”.
Now we’re going to implement a simple spawner to fire the projectile. First, we need a reference to our pool, then we define an empty
GameObject as a parent transform to hold all the instances we’ll fire, keeping the hierarchy clean. We set the initial position and rotation inside the Spawn function.
All we have left now is to get a reference to the
SimpleSpawner component and call the
Spawn() function every time we want to fire with our player.
Feel free to play