A quick way to use ObjectPooling in Unity
Tags: unity3d
Published: 2024-02-12 18:09:14

In Unity, pooling is an essential method for reusing gameObjects.
This breaks the infernal loop of infinite destruction/creation.

The principle of object pooling is that, instead of destroying a gameObject, we deactivate it and put it in its initial state so that we can **reuse** it later.

For example, in a shooter, each explosion could be a gameObject that we **recycle**.

## How to use it on Unity.

Since version 2021, Unity has had a specific method for managing **gameObject pooling**: [ObjectPool](https://docs.unity3d.com/ScriptReference/Pool.ObjectPool_1.html).
This very efficient method has 7 parameters, see below for a real example:

```
using UnityEngine.Pool;

public class RocketManager : MonoBehaviour
{
	// prefab
	public Rocket prefabRocket;

	public ObjectPool<Rocket> PoolRocket { get; set; }
	
	public void Awake()
	{	
		PoolRocket = new ObjectPool<Rocket>(OnCreate, OnTake, OnReturn, OnDestroy, false, 10, 20);
	}
	
	private void OnCreate()
	{
		
	}
	
	private void OnTake(Rocket rocket)
	{
	
	}
	
	private void OnReturn(Rocket rocket)
	{
	
	}
	
	private void OnDestroy(Rocket rocket)
	{
	
	}
}
```
It's true that these parameters are optional except for the first one, but this can quickly become very complicated for the readability of your code.
Especially if 80% of the time your actions (the first 4 parameters) do the same thing all the time. 

A good starting point would be to create a service that allows us to avoid this **repetition**.

## The quick-to-use version

In my case, I use pooling 90% of the time on gameObjects with **their own Monobehaviour**.
The service will work even if your gameObject doesn't have a custom Monobehaviour, you'll just have less control over the configuration.  

To start we'll create an IRelease interface, which will have an implementable method for adding an action when the gameObject returns to the pool.
If it's implemented, its method will allow us to **reset our Gameobject** to its initial state, for example.

```
public interface IRelease
{
	void Release();
}
```

Then, to avoid the hassle of specifying parameters for each new gameObject we want to pool, we'll create a static C# class.

In my case, most of the time the **only parameters** I needed to change were the parameters for the initial and maximum number of gameObjects or the collectionCheck.

```
using UnityEngine;
using UnityEngine.Pool;

public static class BaseObjectPooling
{
	public static ObjectPool<T> GetDefaultPooling<T>(T prefab, int initial = 10, int max = 20, bool collectionChecks = false) where T : MonoBehaviour
	{
		return new ObjectPool<T>(() => GameObject.Instantiate(prefab), (T obj) => obj.gameObject.SetActive(true), OnRelease, (T obj) => GameObject.Destroy(obj), collectionChecks, initial, max);
	}

	private static void OnRelease(MonoBehaviour obj)
	{
		IRelease release = obj as IRelease;
		
		if (release != null)
		{
			release.Release();
		}

		obj.gameObject.SetActive(false);
	}
}
```

The code above shows that the instantiation, activation and deletion actions do nothing more than their primary actions.

There's just the OnRelease method, which will call an additional method if the IRelease interface is implemented.

## How it's used

We're going to use another MonoBehaviour to initialise our Pool system.
Note that I always instantiate my pools in the Awake method, and then I never use them until the Start method is called.
This ensures that my pool is ready and avoids errors.

In the example below, we're going to set up a Pool system for rockets, using a prefab as a model.
The Rocket class is a MonoBehaviour, and we invoke 10 rockets using the pools, associating a random colour with them.
The manager is a singleton to make the demonstration easier, but it's not compulsory.

```
using UnityEngine.Pool;

public class RocketManager : MonoBehaviour
{
	// prefab
	public Rocket prefabRocket;

	public ObjectPool<Rocket> PoolRocket { get; set; }
	
	public static RocketManager Instance { get; private set; }
	
	public void Awake()
	{
		Instance = this;
	
		PoolRocket = BaseObjectPooling.GetDefaultPooling<Rocket>(prefabRocket);
	}
	
	private void Start()
	{
		for (int i = 0; i < 10; i++)
		{
			var instance = PoolRocket.Get();
			instance.SetColor(GetRandomColor());
		}
	}
}
```

In this fictitious example, the rocket will have to return to its **Pool** when the rocket is destroyed.
This is illustrated by the "Explosion" method. 

```
public class Rocket : MonoBehaviour, IRelease
{
	public void SetColor(Color color)
	{
		// set color
	}

	public void Explosion()
	{
		// this ship is destroy, back to the pool
		RocketManager.Instance.PoolRocket.Release(this);
	}

	public void Release()
	{
		// base color
		SetColor(Color.gray);
	}
}

```
Well, the colours example doesn't really make sense, but it's there to show that Release can be used to reset a component.
This is not shown in this example, but it is possible to track undestroyed objects in a list.

This service isn't as functional as it could be, but it seems to me to be a **good approach** to using Pools quickly.
Feel free to customise it at home to make it more efficient =)

You can find more Unity tips and tricks on my [blog](https://blog.vector-focus.com).