Sometimes when you make a game you need to create and destroy a lot of objects of the same type over and over again. You can simply do this by making a prefab and instantiate/destroy this whenever you need to, however, doing this is inefficient and can slow your game down.
One way to get around this issue is object pooling. Basically what this means is that you have a pool (with or without a limit to the amount) of objects that you are going to reuse whenever you can to prevent unnecessary instantiating or destroying.
Below is an example of a simple object pool
public class ObjectPool : MonoBehaviour
{
public GameObject prefab;
public int amount = 0;
public bool populateOnStart = true;
public bool growOverAmount = true;
private List<GameObject> pool = new List<GameObject>();
void Start()
{
if (populateOnStart && prefab != null && amount > 0)
{
for (int i = 0; i < amount; i++)
{
var instance = Instantiate(Prefab);
instance.SetActive(false);
pool.Add(instance);
}
}
}
public GameObject Instantiate (Vector3 position, Quaternion rotation)
{
foreach (var item in pool)
{
if (!item.activeInHierarchy)
{
item.transform.position = position;
item.transform.rotation = rotation;
item.SetActive( true );
return item;
}
}
if (growOverAmount)
{
var instance = (GameObject)Instantiate(prefab, position, rotation);
pool.Add(instance);
return instance;
}
return null;
}
}
Let's go over the variables first
public GameObject prefab;
public int amount = 0;
public bool populateOnStart = true;
public bool growOverAmount = true;
private List<GameObject> pool = new List<GameObject>();
GameObject prefab
: this is the prefab that the object pool will use to instantiate new objects into the pool.int amount
: This is the maximum amount of items that can be in the pool. If you want to instantiate another item and the pool has already reached its limit then another item from the pool will be used.bool populateOnStart
: you can choose to populate the pool on start or not. Doing so will fill up the pool with instances of the prefab so that the first time you call Instantiate
you will get an already existing objectbool growOverAmount
: Setting this to true allows the pool to grow whenever more than the amount are requested in a certain timeframe. You are not always able to accurately predict the amount of items to put in your pool so this will add more to your pool when needed.List<GameObject> pool
: this is the pool, the place where all your instantiated/destroyed objects are stored.Now let's check out the Start
function
void Start()
{
if (populateOnStart && prefab != null && amount > 0)
{
for (int i = 0; i < amount; i++)
{
var instance = Instantiate(Prefab);
instance.SetActive(false);
pool.Add(instance);
}
}
}
In the start function we check if we should populate the list on start and do so if the prefab
has been set and the amount is bigger than 0 (otherwise we would be creating indefinitely).
This is just a simple for loop instantiating new objects and putting them in the pool. One thing to pay attention to is that we set all the instances to inactive. This way they are not visible in the game yet.
Next, there is the Instantiate
function, which is where most of the magic happens
public GameObject Instantiate (Vector3 position, Quaternion rotation)
{
foreach (var item in pool)
{
if (!item.activeInHierarchy)
{
item.transform.position = position;
item.transform.rotation = rotation;
item.SetActive(true);
return item;
}
}
if (growOverAmount)
{
var instance = (GameObject)Instantiate(prefab, position, rotation);
pool.Add(instance);
return instance;
}
return null;
}
The Instantiate
function looks just like Unity's own Instantiate
function, except the prefab has already been provided above as a class member.
The first step of the Instantiate
function is checking to see if there is an inactive object in the pool right now. This means that we can reuse that object and give it back to the requester. If there is an inactive object in the pool we set the position and the rotation, set it to be active (otherwise it could be reused by accident if you forget to activate it) and return it to the requester.
The second step only happens if there are no inactive items in the pool and the pool is allowed to grow over the initial amount. What happens is simple: another instance of the prefab is created and added to the pool. Allowing growth of the pool helps you in having the right amount of objects in the pool.
The third "step" only happens if there are no inactive items in the pool and the pool is not allowed to grow. When this happens the requester will receive a null GameObject which means that nothing was available and should be handled properly to prevent NullReferenceExceptions
.
Important!
To make sure your items get back into the pool you should not destroy the game objects. The only thing you need to do is set them to inactive and that will make them available for reusage through the pool.