So you want to have constant explosions, hundreds of rockets, bullets flying all over the place and general insanity in your game. Great, so do I! But this insanity comes at a cost if you don’t manage your resources properly. When you instantiate and destroy an object, you have to allocate memory, it sits there for a while and eventually the garbage collector comes by and releases it, IF it meets all of the requirements. Well that’s the sliced down version anyways. If you are interested in more reading on the whys, here are some links to additional reading.
This article discusses one technique for helping manage those resources for objects that might quickly go through that cycle. The article is three sections, what is object pooling, what to pool and how to implement object pooling.
Object pooling is actually a fairly simple strategy which is designed to reduce the number of times you allocate and de-allocate memory to boost performance. The central idea is that you have a list of objects that are pre-instantiated. When you need an object, you ask for it, and are returned an object whose properties you then re-set to what you need. When it is time to destroy or de-allocate that object, instead of calling Destroy(object); or similar, you simply set it to inactive, which automatically returns it to the pool of objects you can use. If your character can sit and shoot 100 bullets per second but at any time only 10 of them are ever on screen and can impact the game, you will probably have a pool of only 11 bullets and have the same affect of 100 bullets having been instantiated and destroyed every second without that overhead.
I have chosen to break down objects I pool into a few simple categories
When deciding if you should pool an object, also think about how often that object is active. If most of the time the object is inactive and it doesn’t churn a lot. Even a power up in which 100 rockets burst from your character and are then almost immediately de-allocated may not necessarily need to be pooled. You can potentially instantiate those objects when you need them, and then ditch them. If your game has 50 types of power-ups that you can ever only use one at a time, it probably doesn’t make sense to create pools of each one on startup.
Well the first thing you need is an object pool. Below is a sample object pool I have written and am currently using in code named “Project Spritzer”. Please note that lines 65-70 require you to have a Game State Manager that issues shrink events. If you do not have a Game State Manager that issues Shrink events, you can simply comment these lines out. Also note this is not thread safe. The comments in this sample should be fairly good at explaining what is going on, but here is a high level description. The object pool class is the management structure for individual object pools. This includes creation of the pool, retrieving objects, increasing the pool size and also shrinking the pool. If you have multiple object pools, they should be accessed through the object pool manager and not the individual pools.
* @Author: David Crook
* Use the object pools to help reduce object instantiation time and performance
* with objects that are frequently created and used.
/// The object pool is a list of already instantiated game objects of the same type.
//the list of objects.
//sample of the actual object to store.
//used if we need to grow the list.
//maximum number of objects to have in the list.
//initial and default number of objects to have in the list.
/// Constructor for creating a new Object Pool.
/// <param name="obj">Game Object for this pool</param>
/// <param name="initialPoolSize">Initial and default size of the pool.</param>
/// <param name="maxPoolSize">Maximum number of objects this pool can contain.</param>
/// <param name="shouldShrink">Should this pool shrink back to the initial size.</param>
//instantiate a new list of game objects to store our pooled objects in.
//create and add an object based on initial size.
i = 0; i < initialPoolSize; i++)
//instantiate and create a game object with useless attributes.
//these should be reset anyways.
GameObject nObj = GameObject.Instantiate(obj, Vector3.zero, Quaternion.identity)
//make sure the object isn't active.
//add the object too our list.
//Don't destroy on load, so
//we can manage centrally.
//store our other variables that are useful.
.maxPoolSize = maxPoolSize;
.pooledObj = obj;
.initialPoolSize = initialPoolSize;
//are we supposed to shrink?
//listen to the game state manager's event for all pools should shrink
//back to their initial size.
/// Returns an active object from the object pool without resetting any of its values.
/// You will need to set its values and set it inactive again when you are done with it.
/// <returns>Game Object of requested type if it is available, otherwise null.</returns>
//iterate through all pooled objects.
i = 0; i < pooledObjects.Count; i++)
//look for the first one that is inactive.
//set the object to active.
//return the object we found.
//if we make it this far, we obviously didn't find an inactive object.
//so we need to see if we can grow beyond our current count.
//Instantiate a new object.
GameObject nObj = GameObject.Instantiate(pooledObj, Vector3.zero, Quaternion.identity)
//set it to active since we are about to use it.
//add it to the pool of objects
//return the object to the requestor.
//if we made it this far obviously we didn't have any inactive objects
//we also were unable to grow, so return null as we can't return an object.
/// Iterate through the pool and releases as many objects as
/// possible until the pool size is back to the initial default size.
/// <param name="sender">Who initiated this event?</param>
/// <param name="eventArgs">The arguments for this event.</param>
sender, GameEventArgs eventArgs)
//how many objects are we trying to remove here?
objectsToRemoveCount = pooledObjects.Count - initialPoolSize;
//Are there any objects we need to remove?
(objectsToRemoveCount <= 0)
//cool lets get out of here.
//iterate through our list and remove some objects
//we do reverse iteration so as we remove objects from
//the list the shifting of objects does not affect our index
//Also notice the offset of 1 to account for zero indexing
//and i >= 0 to ensure we reach the first object in the list.
i = pooledObjects.Count - 1; i >= 0; i--)
//Is this object active?
//Guess not, lets grab it.
GameObject obj = pooledObjects[i];
//and kill it from the list.
The next thing I use is an Object Pool Manager, as I tend to have multiple pools of multiple types of objects. You probably will too. The object pool manager is a singleton class, which is where I centrally ask for objects, it finds the correct pool and returns an object from that pool. The pool manager simply keeps tabs on the various object pools and allows a central location for creation and access. The code for my object pooling manager is below.
* Use this singleton Object Pooling Manager Class to manage a series of object pools.
* Typical uses are for particle effects, bullets, enemies etc.
//the variable is declared to be volatile to ensure that
//assignment to the instance variable completes before the
//instance variable can be accessed.
//look up list of various object pools.
Dictionary<String, ObjectPool> objectPools;
//object for locking
/// Constructor for the class.
//Ensure object pools exists.
/// Property for retreiving the singleton. See msdn documentation.
//check to see if it doesnt exist
//lock access, if it is already locked, wait.
//the instance could have been made between
//checking and waiting for a lock to release.
//create a new instance
//return either the new instance or the already built one.
/// Create a new object pool of the objects you wish to pool
/// <param name="objToPool">The object you wish to pool. The name property of the object MUST be unique.</param>
/// <param name="initialPoolSize">Number of objects you wish to instantiate initially for the pool.</param>
/// <param name="maxPoolSize">Maximum number of objects allowed to exist in this pool.</param>
/// <param name="shouldShrink">Should this pool shrink back down to the initial size when it receives a shrink event.</param>
//Check to see if the pool already exists.
//let the caller know it already exists, just use the pool out there.
//create a new pool using the properties
ObjectPool nPool =
ObjectPool(objToPool, initialPoolSize, maxPoolSize, shouldShrink);
//Add the pool to the dictionary of pools to manage
//using the object name as the key and the pool as the value.
//We created a new pool!
/// Get an object from the pool.
/// <param name="objName">String name of the object you wish to have access to.</param>
/// <returns>A GameObject if one is available, else returns null if all are currently active and max size is reached.</returns>
//Find the right pool and ask it for an object.
Ok, so that’s good, but how do I actually use these classes now? Below is some sample code for creating an object pool for circle light, retrieving a circle light for my game “Project Spritzer” as well as returning it to the pool.
Here we create a brand new object pool of type RangeCookieLight, which is a prefab directional light that all of my players share. The initial size is 1, with a max of 3 and I want this to shrink down if necessary. I chose to pool this object because it is active 95% of the time, it just simply changes context based on who is using it, and there is only ever a maximum of 3 active at one time, though that is rare.
.RangeCookieLight, 1, 3,
Notice when we retrieve the object, we are referencing it by the GameObject.name attribute. If you look at the source code for the object pool manager, that is what it uses as the key for the dictionary. After we have it, we need to reset all properties that might impact the game and situation. I know the object is returned Active, but I set it active just in case.
.circleLight = ObjectPoolingManager.Instance.GetObject(
.circleLight.transform.rotation = Quaternion.Euler(90, 0, 0);
.circleLight.GetComponent<Light>().color = Color.white;
Since this is a turn based game, I no longer need the light at the end of the turn. Therefor the circle should be set to inactive and the next player can now use this same object by simply resetting the properties you can see above.
So in this article we covered why we would use object pooling, what object pooling is as well as how to implement it along with source code that you can copy/paste to begin using. I hope this was informative and helpful for everybody. Again feel free to comment if you find issues with the article or use the contact me section of the website to request new articles on things that are giving you difficulty.