
Object pooling is one of the easiest forms of optimization in Unity. Just recently we implemented the Factory Design Pattern which is related to today’s subject.
Object Pooling?
Let’s begin by explaining what Object Pooling is. So this is an optimization practice which is based on instance reusability. In Factory, we created a new instance of prefab, and later we destroyed it which is not an issue when we want to create just a few instances.
Problems start arriving when we create a lot of new instances, for example when shooting bullets from a gun, or we spawn a lot of special effects.
“What problems?” you might ask?
Without digging into much detail, you might run into troubles with memory fragmentation, which might lead to a game crash and of course performance issues as instantiating new objects is resource heavy, especially when creating a lot of new instances!
With Object Pooling, we don’t have that issue, as we create objects once and only so many as we need. It gets rid of the performance and memory issues.
Implementation
Implementation of object pooling is reasonably easy, but it might differ from what I’m presenting here. My implementation is basing on the factory design pattern with just a few extensions to it. As in the case of Factory example, I’m going to present to you two versions of the implementation.
First is just a regular with a single purpose.
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// Basic object pool implementation /// </summary> public class ObjectPool : MonoBehaviour { // Reference to prefab. [SerializeField] private PoolableObject prefab; // References to reusable instances private Stack<PoolableObject> reusableInstances = new Stack<PoolableObject>(); /// <summary> /// Returns instance of prefab. /// </summary> /// <returns>Instance of prefab.</returns> public PoolableObject GetPrefabInstance() { PoolableObject inst; if (reusableInstances.Count > 0) { inst = reusableInstances.Pop(); inst.transform.SetParent(null); inst.gameObject.SetActive(true); } else { inst = Instantiate(prefab); } return inst; } public void ReturnToPool(PoolableObject instance) { instance.gameObject.SetActive(false); instance.transform.SetParent(transform); instance.transform.localPosition = Vector3.zero; instance.transform.localScale = Vector3.one; instance.transform.localEulerAngles = Vector3.one; reusableInstances.Push(instance); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// Poolable object used in regular object pool. /// </summary> public class PoolableObject : MonoBehaviour { // Pool to return object public ObjectPool origin; /// <summary> /// Prepares instance to use. /// </summary> public virtual void PrepareToUse() { // prepare object for use // you can add additional code here if you want to. } /// <summary> /// Returns instance to pool. /// </summary> public virtual void ReturnToPool() { // prepare object for return. // you can add additional code here if you want to. origin.ReturnToPool(this); } }
As you might notice, my instance has an additional function that returns it to the pool when it’s no longer needed.
However generic implementation of object pooling is a little more complicated as it requires the use of interfaces. But besides that, it’s easy to understand ?
/// <summary> /// Base interface for object pool used in poolable objects /// </summary> public interface IObjectPool { void ReturnToPool(object instance); } /// <summary> /// Generic interface with more methods for object pooling /// </summary> public interface IObjectPool<T> : IObjectPool where T : IPoolable { T GetPrefabInstance(); void ReturnToPool(T instance); }
/// <summary> /// Interface for poolable objects /// </summary> public interface IPoolable { IObjectPool Orgin { get; set; } void PrepareToUse(); void ReturnToPool(); }
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// Basic object pool implementation with generic twist /// </summary> public class GenericObjectPool<T> : MonoBehaviour, IObjectPool<T> where T : MonoBehaviour, IPoolable { // Reference to prefab. [SerializeField] private T prefab; // References to reusable instances private Stack<T> reusableInstances = new Stack<T>(); /// <summary> /// Returns instance of prefab. /// </summary> /// <returns>Instance of prefab.</returns> public T GetPrefabInstance() { T inst; // if we have object in our pool we can use them if (reusableInstances.Count > 0) { // get object from pool inst = reusableInstances.Pop(); // remove parent inst.transform.SetParent(null); // reset position inst.transform.localPosition = Vector3.zero; inst.transform.localScale = Vector3.one; inst.transform.localEulerAngles = Vector3.one; // activate object inst.gameObject.SetActive(true); } // otherwise create new instance of prefab else { inst = Instantiate(prefab); } // set reference to pool inst.Orgin = this; // and prepare instance for use inst.PrepareToUse(); return inst; } /// <summary> /// Returns instance to the pool. /// </summary> /// <param name="instance">Prefab instance.</param> public void ReturnToPool(T instance) { // disable object instance.gameObject.SetActive(false); // set parent as this object instance.transform.SetParent(transform); // reset position instance.transform.localPosition = Vector3.zero; instance.transform.localScale = Vector3.one; instance.transform.localEulerAngles = Vector3.one; // add to pool reusableInstances.Push(instance); } /// <summary> /// Returns instance to the pool. /// Additional check is this is correct type. /// </summary> /// <param name="instance">Instance.</param> public void ReturnToPool(object instance) { // if instance is of our generic type we can return it to our pool if (instance is T) { ReturnToPool(instance as T); } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// Base class for objects used in generic object pool /// </summary> public class GenericPoolableObject : MonoBehaviour, IPoolable { // Pool to return object public IObjectPool Orgin { get; set; } /// <summary> /// Prepares instance to use. /// </summary> public virtual void PrepareToUse() { // prepare object for use // you can add additional code here if you want to. } /// <summary> /// Returns instance to pool. /// </summary> public virtual void ReturnToPool() { // prepare object for return. // you can add additional code here if you want to. Orgin.ReturnToPool(this); } }
Fantastic! As we have both versions, now we can use them!
Example
This time we are going to attach out object pool to the spaceship and shoot lasers! We are going to use a similar object with a timeout on it and bullet spawner.
Let’s code it!
using UnityEngine; /// <summary> /// Object pool for prefabs of TimeoutObjects /// </summary> public class TimedObjectObjectPool : GenericObjectPool<TimeoutObject> { }
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// Timeout object. /// It return itself to object pool after provided timeout. /// </summary> [RequireComponent(typeof(Rigidbody2D))] public class TimeoutObject : GenericPoolableObject { // Time after which object will be destroyed [SerializeField] private float timeout = 2; // Saving enable time to calculate when to destroy itself private float startTime; // Reference to rigidbody used in spawner public Rigidbody2D Rigidbody { get; private set; } /// <summary> /// Unity's method called on object enable /// </summary> public override void PrepareToUse() { base.PrepareToUse(); startTime = Time.time; if (!Rigidbody) { Rigidbody = GetComponent<Rigidbody2D>(); } } /// <summary> /// Unity's method called every frame /// </summary> private void Update() { // Waiting for timeout if (Time.time - startTime > timeout) { // Returning object ReturnToPool(); } } }
using UnityEngine; /// <summary> /// Prefab spawner. /// Component uses object pool to get prefab instance every few moments. /// </summary> public class PrefabTimedSpawner : MonoBehaviour { // Spawn rate [SerializeField] private float spawnRatePerMinute = 30; // Current spawn count private int currentCount = 0; // Reference to used object pool [SerializeField] private TimedObjectObjectPool objectPool; /// <summary> /// Unity's method called every frame /// </summary> private void Update() { var targetCount = Time.time * (spawnRatePerMinute / 60); while (targetCount > currentCount) { // Setup prefab instace to shoot! var inst = objectPool.GetPrefabInstance(); inst.transform.position = transform.position; inst.transform.up = -transform.up; inst.Rigidbody.AddForce(inst.transform.rotation * (Vector2.up * 10), ForceMode2D.Impulse); currentCount++; } } }
You can notice that implementation is almost the same as with the Factory Design Pattern.
So how it’s look in Unity?
Scene hierarchy Game view
I’m shooting a ton of lasers, but you can see that thanks to object pooling I only created nine instances that are reused continuously! So much performance! ?
I hope you enjoyed this post and you found it useful. As always you can take a look at full source code available in my public repository here.
See you next time! ??
Just wanted to say thanks for this, I had object pooling set up in my game, but I wanted to implement a generic version so I didn’t have to call GetComponent on my pooled objects if they derived from the base Spawnable class.
I managed to get it all working, except figuring out how to make ‘Spawnables’ return themselves to the object pool, without having to create circular generic T references between Spawn and Pool.
I looked all over, but no solution was as brilliant as yours, you saved my ass Kudos!
Love to hear that! ❤️