Object Pooling in Unity – Improve Your Game’s Performance

Object Pooling is a design pattern that involves storing a group of pre-created objects for later reuse, thereby avoiding the constant creation and destruction of objects during game runtime. This approach helps eliminate memory management issues.

How Does Memory Work in Unity?

In Unity, memory management is handled by an automatic garbage collection system, known as the Garbage Collector (GC). When we create a new object in the game, for example, using the Instantiate() method, Unity allocates memory for it. This object occupies a certain amount of RAM, and as more instances are created, the game uses more resources. When an object is no longer needed—like a projectile in a game that has moved off-screen—we usually remove it by calling the Destroy() method.

Object Pooling Game

When we use Destroy() to remove an object, the memory occupied by that object becomes a candidate for garbage collection. At this point, the memory is not released immediately—it is marked as “to be removed,” but the actual freeing of resources only happens when the Garbage Collector (GC) is triggered. Here lies the problem: frequent operations of creating (Instantiate()) and destroying (Destroy()) objects cause the Garbage Collector to work more often. This leads to an undesirable effect known as GC Spikes — sudden, noticeable stutters or game freezes resulting from the intensive memory cleanup operations performed by the GC.

How Does Object Pooling Work?

Implementing Object Pooling involves creating a pool of objects that are activated and deactivated as needed. When an object is no longer needed, instead of destroying it, we simply deactivate it and return it to the pool so it can be reused.

Object Pooling Diagram

For example, if we’re developing a shoot’em up game, instead of creating new projectiles every time the player fires, we can have a pool of 50 projectiles that are reused whenever necessary. Similarly, in an endless runner game, we can use Object Pooling to efficiently spawn and recycle level segments that the player passes while running.

How to Implement Object Pooling in Unity?

Below is an example implementation of Object Pooling in Unity, which uses a generic class to manage objects of type Component. Using a generic class allows for the reuse of this code for different types of objects, adding flexibility. Additionally, this solution avoids costly GetComponent operations when accessing scripts, as the component is stored directly in the pool.

using System.Collections.Generic;
using UnityEngine;

namespace SpaceShooter.Utilities
{
    public class ObjectPool<T> where T : Component
    {
        private readonly GameObject prefab;
        private readonly Queue<T> pool = new Queue<T>();
        private readonly Transform parentTransform;

        // Constructor initializing the object pool
        public ObjectPool(GameObject prefab, int initialSize, Transform parent = null)
        {
            this.prefab = prefab;
            this.parentTransform = parent;

            for (int i = 0; i < initialSize; i++)
            {
                GameObject obj = Object.Instantiate(prefab, parentTransform);
                obj.SetActive(false);
                pool.Enqueue(obj.GetComponent<T>());
            }
        }

        // Method to return an object from the pool or create a new one if the pool is empty.
        // position - the position where the object will be placed.
        // rotation - the rotation the object should have upon retrieval.
        public T GetObject(Vector3 position, Quaternion rotation)
        {
            T objComponent;

            if (pool.Count > 0)
            {
                objComponent = pool.Dequeue();
            }
            else
            {
                GameObject obj = Object.Instantiate(prefab, parentTransform);
                objComponent = obj.GetComponent<T>();
            }

            objComponent.transform.position = position;
            objComponent.transform.rotation = rotation;
            objComponent.gameObject.SetActive(true);

            return objComponent;
        }

        // Method to return an object to the pool when it is no longer needed
        public void ReturnObject(T objComponent)
        {
            objComponent.gameObject.SetActive(false);
            pool.Enqueue(objComponent);
        }
    }
}

After implementing ObjectPool<T>, the next step is to create a new class that will manage the object pool for a specific scenario in the game, such as for projectiles. In this class, we initialize a new instance of the object pool, passing to its constructor the projectile prefab, the pool size (which determines the number of pre-created objects ready for use), and optionally, a parent object that will contain these projectiles in the scene hierarchy.

projectilePool = new ObjectPool<ProjectileController>(projectilePrefab, poolSize, parent);

Traditionally, in scenarios such as collisions, objects were removed using the Destroy() method. In an implementation using Object Pooling, instead of destroying objects, they should be returned to the pool, which is done using the ReturnProjectile() method. Similarly, instead of dynamically creating new objects with Instantiate(), we can now use the GetProjectile() method to retrieve an object from the pool.

public ProjectileController GetProjectile()
{
    return projectilePool.GetObject(projectileSpawnPoint.position, projectileSpawnPoint.rotation);
}

public void ReturnProjectile(ProjectileController projectile)
{
    projectilePool.ReturnObject(projectile);
}

Object Pooling is an extremely effective design pattern, especially in games that need to handle a large number of dynamic objects, such as projectiles, enemies, visual effects, or even UI elements. With the simplicity of implementation and flexibility provided by the generic ObjectPool<T> class, you can easily manage objects in the game, avoiding the issues associated with frequent creation and destruction of objects and intensive Garbage Collector operations.

In summary, Object Pooling is a must-have for any game developer striving to create a smooth and efficient game. Whether you’re developing a dynamic shooter or a mobile endless runner—implementing this design pattern can bring significant benefits. I encourage you to experiment with Object Pooling in your own projects and discover how much it can improve your game’s performance.


Unity Tutorials
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments