As an Associate of Unity Asset Store, this website earns from qualifying purchases & contains affiliate links: check the footer for more info.

Object Pool & Object Pooling System in Unity

Object Pool is a software design pattern that allows you to create objects in advance (e.g., at the scene loading) and then reuse them, what has a positive effect on game performance for Unity Projects.

In this Tutorial, I will consider how Object Pool must work in Unity, and also several tools provided by Indie Developers (Publishers) in Unity Asset Store.

Unity Asset Store — Download Button

Object Pooling, Optimization and Performance in Unity

To prevent Garbage Collector issues (CPU Spikes) in games with many spawning and destroying objects, a method called Object Pooling can be used. Object Pooling refers to creating all necessary objects beforehand and disabling/enabling them when it necessary, instead of instantiating (Instantiate() function) and destroying (Destroy() function) objects during runtime.

Object Pool in Unity — Example in Game — Pooling System — Object Pooler — Pool Manager

This is often done by having an array containing disabled objects that are often used, e.g. bullets in FPS or units in a Strategy game. When a gun is fired, instead of spawning a new bullet and allocating memory for it, we take a game object from the list (object pool), move it to the right position & activate it.

When the bullet collides with something, instead of destroying it, we disable it and add it back to our object pool in default state to use it later.

This way, we can have many objects appear and disappear, without allocating any memory and causing issues with the Garbage Collector.

These objects can also be spawned beforehand during a loading screen and kept hidden until needed. This way they won’t cause performance issues when spawned during gameplay.


Unity Assets

Random Object Pooler

RandomObjectPooler.cs — independent script by Makaka Games which is used for Throwing Objects in Throw Control and for Enemies in AR Shooter — so, it’s a pretty universal solution.

The description below will be based on Throw Control: all Throwing Objects are created at the Scene Start from prefabs and used throughout the Scene Session.

Parameters

  • initPooledAmount — to regulate Object Count in the Scene at Start.
  • poolParent — parent object contains Throwing Object until Throw and after Reset. It’s useful when Dynamic Camera (FPS, AR): to follow camera movements before Throw.
  • isDebugLogging — to log key moments when Development Build.
  • positionAtInit — to instantiate Throwing Objects at Start outside the Game Area, to Avoid Collisions between Throwing Objects and with Other Physical Objects.
  • rotationAtInit — to set the object direction before appearing. It’s used in AR Shooter (docs) without Throwing System.
Prefabs

Prefabs of Throwing Objects are set in Unity Editor by default.

If you have dynamic gameplay and you need to change prefabs programmatically before initialization outside the Throwing System, use the next functions of ThrowControl.cs, and then activate the Game Object with RandomObjectPooler.cs component on it (Basketball (docs) uses such function due to different ball prefabs for AR & Non-AR modes):

/*
=============================================================
Unity Assets by MAKAKA GAMES: https://makaka.org/unity-assets
=============================================================
*/

public void SetPrefabBeforeInit(GameObject gameObject)
{
	randomObjectPooler.prefab = gameObject;
}

public void SetPrefabsBeforeInit(GameObject[] gameObjects)
{
	randomObjectPooler.prefabs = gameObjects;
}
Single Prefab
  • prefab — single object for instantiating. When it equals None then multiple prefabs are used instead. It’s relevant for temporary testing the target prefab when you are using multiple prefabs.
Multiple Prefabs
  • areRandomizedObjectsWhenCreating — when you need a random instance count of possible Throwing Object prefabs for every Scene Start.
  • areRandomizedObjectsWhenGetting — when you require a random Throwing Object instance for every Throw.
  • prefabs — multiple objects for instantiating.
Events
  • OnInitialized — it’s dispatched when all initial pool operations are completed.

C# Code of Object Pool

Random Object Pooler provides instantiating of all objects at start and disabling them by default.

/*
=============================================================
Unity Assets by MAKAKA GAMES: https://makaka.org/unity-assets
=============================================================
*/

using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;

[HelpURL("https://makaka.org/unity-assets")]
public class RandomObjectPooler : MonoBehaviour
{
    [Range(1, 30)]
    public int initPooledAmount = 7;
	public Transform poolParent = null;

    [Space]
    [SerializeField]
    private bool isDebugLogging = false;

    [Space]
    public Transform positionAtInit = null;
    public Transform rotationAtInit = null;

    [Header("Single (actual for Testing target prefab; None => Multiple)")]
    public GameObject prefab;

    [Header("Multiple")]
    public bool areRandomizedObjectsWhenCreating = false;
    public bool areRandomizedObjectsWhenGetting = false;

    public GameObject[] prefabs;

	[Header("Events")]
    [Space]
    public UnityEvent OnInitialized;

	[HideInInspector]
	public List<GameObject> pooledObjects = null;

    private List<GameObject> availableRandomizedObjects = null;

    private GameObject currentInstantiated = null;

    [HideInInspector]
    public List<MonoBehaviour> controlScripts;
	
    private MonoBehaviour controlScriptTempForRegistration;
    
    private System.Type controlScriptType;
	
	private void Start()
    {
        InitAndPopulatePool();
    }

    private void InitAndPopulatePool()
    {
        pooledObjects = new List<GameObject>();

        availableRandomizedObjects = new List<GameObject>();
        
        for (int i = 0; i < initPooledAmount; i++)
        {
            pooledObjects.Add(InstantiateObject(i));
        }

        OnInitialized?.Invoke();
    }

    public void InitControlScripts(System.Type type)
    {
        controlScripts = new List<MonoBehaviour>();

        controlScriptType = type;
    }

    private GameObject InstantiateObject(int index)
    {
        GameObject tempPrefab;

        if (prefab)
        {
            tempPrefab = prefab;
        }
        else if (areRandomizedObjectsWhenCreating)
        {
            tempPrefab = prefabs[Random.Range(0, prefabs.Length - 1)];
        }
        else
        {
            tempPrefab = prefabs[index % prefabs.Length];
        }

        if (tempPrefab)
        {
            currentInstantiated = Instantiate(
                tempPrefab,
                positionAtInit
                    ? positionAtInit.position
                    : tempPrefab.transform.position,
                rotationAtInit
                    ? rotationAtInit.rotation
                    : tempPrefab.transform.rotation,
                poolParent);

            currentInstantiated.SetActive(false);
            currentInstantiated.name = tempPrefab.name + index;
        }
        else
        {
            currentInstantiated = null;

            if (isDebugLogging)
            {
                DebugPrinter.Print("Throwing Object is not Assigned!");
            }
        }

        return currentInstantiated;
    }

    public GameObject GetPooledObject()
	{
        availableRandomizedObjects.Clear();

        for (int i = 0; i < pooledObjects.Count; i++) 
		{
			if (!pooledObjects[i])
            {
                //DebugPrinter.Print("GetPooledObject(): Create New Instance");

                pooledObjects[i] = InstantiateObject(i);

                return pooledObjects[i];
            }

            if (!pooledObjects[i].activeInHierarchy)
			{
                if (areRandomizedObjectsWhenGetting)
                {
                    availableRandomizedObjects.Add(pooledObjects[i]);
                }
                else
                {
                    return pooledObjects[i];
                }
            }    
		}

        if (areRandomizedObjectsWhenGetting
            && availableRandomizedObjects.Count > 0)
        {
            return availableRandomizedObjects[
                Random.Range(0, availableRandomizedObjects.Count - 1)];
        }
        else
        {
            if (isDebugLogging)
            {
                DebugPrinter.Print("GetPooledObject():" +
                    " All Game Objects in Pool are not available");
            }

            return null;
        }
	}

    /// <summary>
    /// For initial registration (cashing)
    /// and subsequent getting Control Script of GameObject
    /// </summary>
    public MonoBehaviour RegisterControlScript(GameObject gameObject)
    {
		controlScriptTempForRegistration = null;

		// Search of cached Control Script
		for (int i = 0; i < controlScripts.Count; i++)
		{
			controlScriptTempForRegistration = controlScripts[i];

			if (controlScriptTempForRegistration)
			{
				if (controlScriptTempForRegistration.gameObject == gameObject)
				{
                    //DebugPrinter.Print(i);

                    break;
				}
				else
				{
					controlScriptTempForRegistration = null;
				}
			}
			else // Game Object is null
			{
				controlScripts.RemoveAt(i);

                //DebugPrinter.Print("Remove null Control Script from List");
            }
        }

		if (!controlScriptTempForRegistration)
		{
			controlScriptTempForRegistration =
                gameObject.GetComponent(controlScriptType) as MonoBehaviour;

            //DebugPrinter.Print("Try to get Control Script");

            if (controlScriptTempForRegistration)
            {
			    controlScripts.Add(controlScriptTempForRegistration);

                //DebugPrinter.Print("Register New Control Script");
            }

        }

		return controlScriptTempForRegistration;
    }
}

The next manipulations with objects (disabling & enabling) must be executed outside the script above. E.g., if your objects in the pool contain control scripts, and you want to operate with them, then the Random Object Pooler can provide convenient operating for this.

The next example shows simplified using the objects outside the Random Object Pooler. Throw Control uses ThrowControl.cs for this task — main script that manages the Throw & all Throwing Objects on a scene through Object Pool.

/*
=============================================================
Unity Assets by MAKAKA GAMES: https://makaka.org/unity-assets
=============================================================
*/

using UnityEngine;
using System.Collections;

[HelpURL("https://makaka.org/unity-assets")]
public class PoolOperatingController : MonoBehaviour
{
	[SerializeField]
	private RandomObjectPooler randomObjectPooler;
	
	private GameObject gameObjectTemp;
	
	private ThrowingObject throwingObjectTemp;
	
	/// <summary>Call after pool initialization.</summary>
	public void InitThrowingObjects()
	{
		if (randomObjectPooler)
		{
			randomObjectPooler.InitControlScripts(typeof(ThrowingObject));
	
			for (int i = 0; i < randomObjectPooler.pooledObjects.Count; i++)
			{
				gameObjectTemp = randomObjectPooler.pooledObjects[i];
	
				if (gameObjectTemp)
				{
					throwingObjectTemp =
						randomObjectPooler.RegisterControlScript(
							gameObjectTemp) as ThrowingObject;
				}
			}
		}
	}

	private void GetNextThrow()
	{
		gameObjectTemp = randomObjectPooler.GetPooledObject();
	
		if (gameObjectTemp)
		{
			gameObjectTemp.SetActive(true);
	
			throwingObjectTemp = randomObjectPooler.RegisterControlScript(
				gameObjectTemp) as ThrowingObject;
	
			// next, any manipulations with the object's control script:
			// ...
			// ...
		}
	}
	
	private IEnumerator ResetPooledObject(ThrowingObject throwingObject)
	{
		if (randomObjectPooler.positionAtInit)
		{
			throwingObject.ResetPosition(
				randomObjectPooler.positionAtInit.position);
		}
	
		yield return new WaitForFixedUpdate();
	
		throwingObject.gameObject.SetActive(false);
	}
}

Random Object Pooler is a Part of the Next Unity Assets

Random Object Pooler is integrated with the next Complete Project Templates & Tools:

1. Football (docs) — Unity Asset in the sport arcade genre with realistic physics of football net, goal movement & target movement inside the goal, advanced scoring & audio systems.

Unity Asset Store — Download Button

2. Basketball Game (docs) — sport arcade with realistic physics of basketball net & ring, ring growing, hoop movement, advanced scoring & audio systems.

Unity Asset Store — Download Button

3. AR Shooter (docs) — first-person shooter: kill zombies & save your life with full immersion in the game.

Unity Asset Store — Download Button

4. Throw Control (docs) — highly customizable Advanced Throwing System for Unity.

Unity Asset Store — Download Button

Pool Boss

Pool Boss — the best pooling solution on Unity Asset Store that makes simple to avoid the costly Instantiate() and Destroy() calls that cause performance problems. All prefab types are supported — even particle systems.

Unity Asset Store — Download Button
Unity Pool Boss — Unity Asset of Pooling System — Pool Manager

One short line of code can despawn or spawn prefabs, or you can use the included Playmaker scripts!


Pool Kit

Pool Kit — Ultimate Object Pool Manager, next generation system for pooling, spawning and despawning with ZERO Garbage Collection which can be up to 77% faster than instantiating (~33% on average) and helps to make your game a smoother experience which is increasingly needed on platforms like mobile, AR and VR!

Unity Asset Store — Download Button
Unity Pool Kit — Unity Asset of Pooling System — Object Pooler

Pool Kit also thinks about how you work in Unity and comes with its powerhouse of a Spawner. This can be used to create enemies in strategy or tower defense game genres, special effects, weapon systems and more!


Game Examples with Object Pool




Support

First, read the latest docs online.
If it didn’t help, get the support.

Unity Assets

Support for Unity Assets

I am Andrey Sirota, Founder of Makaka Games and full-time Publisher on the Unity Asset Store. First, read the latest docs online. If it didn’t help, get the support.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Comment moderation is enabled. Your comment may take some time to appear.

Back to top button