A Generic Object Pool for Unity3D

This is a tutorial for writing a generic object pool for game development in Unity3D.

What is an object pool?

An object pool is a design pattern where a set of initialized objects is kept ready for use. Rather than instantiating and destroying objects you request an available object from the pool or release an object back to the pool for reuse later.

Why use object pooling?

Most of us will encounter somewhere along the way developing games in Unity3D the performance hit by calling Instantiate() and Destroy() in large quantities. A object pool can mitigate this problem.

Rationale

There are a lot of great tutorials out there for writing your very first object pool and develop better understanding of the concept. However what you see in most tutorials are written in such a way that only capable of pooling GameObject derived classes as its members and tracks the availability of its members by MonoBehaviour.enabled field which in most situations I prefer to avoid.

The goal of this tutorial is to write a generic implementation of an object pool that is more reusable and applicable for a wider range of use cases.

What are generic types? A generic type is a placeholder that allows you to write classes or interfaces without directly specifying the type of its Fields Methods, and Parameters beforehand. This powerful feature of C# allows us to write more reusable code. Checkout Introduction to Generics for more information on generic type.

Lets get started

First of lets import System.Collections.Generic and create a generic Pool<T> class.

using System.Collections.Generic;

public class Pool<T> {
}

We want to add three fields, members which is a list of members of the pool, unavailable to check if a member is already in use and factory which will be responsible for creating new member instances.

public List<T> members = new List<T>();
public HashSet<T> unavailable = new HashSet<T>();
IFactory factory;

Don’t mind IFactory for now, we will come back to that later

Next up are methods, we need one for creating new members with the factory and adding it to the list of members.

T Create() {
	T member = factory.Create();
 	member.myPool = this;
	members.Add(member);
	return member;
}

Another method for allocation that returns an available member and if there are none available we should create a new one with the factory. Before returning a member it should be tracked as unavailable with the unavailable hash set.

public T Allocate() {
	for(int i = 0; i < members.Count; i++) {		 
		if(!unavailable.Contains(members[i])) {
			unavailable.Add(members[i]);
			return members[i];
		}
	}
	T newMembers = Create();
	unavailable.Add(newMembers);
	return newMembers;
}

And one last method for releasing that returns a member to its initial state and unmarks it as unavailable.

public void Release(T member) {
	member.Reset();
	unavailable.Remove(member);
}

Let’s get back to IFactory and create a IFactory interface. with a create method that returns a new instance of type T.

public interface IFactory<T> {
	T Create();
}

That’s it, now we can create a Factory<T> that implements this IFactory<T> interface.

public class Factory<T> : IFactory<T> where T : new() {
    public T Create() {
	return new T();
    }
}

This is cool and all but you must be thinking this won’t work for a MonoBehaviour derived class and you’re right. So let’s create another factory called MonoBehaviourFactory<T> for this use case.

using UnityEngine;

public class MonoBehaviourFactory<T> : IFactory<T> where T : MonoBehaviour {
    
    GameObject prefab;
    string name;
    int index = 0;

    public MonoBehaviourFactory() : this("MonoBehaviour") { }

    public MonoBehaviourFactory(string name) {
	this.name = name;
    }

    public T Create() {
        GameObject tempGameObject = GameObject.Instantiate(new GameObject()) as GameObject;
        
        tempGameObject.name = name + index.ToString();
	tempGameObject.AddComponent<T>();
	T objectOfType = tempGameObject.GetComponent<T>();
	index++;
	return objectOfType;
    }
}

This should get you pretty far but what if you need a pool of complex prefabs? Let’s make a copy of MonoBehaviourFactory<T> and rename it PrefabFactory<T>. The only change we’ll have to make is passing the prefab through the constructor and instantiate that instead of a new GameObject() like so.

using UnityEngine;

public class PrefabFactory<T> : IFactory<T> where T : MonoBehaviour {

	GameObject prefab;
	string name;
	int index = 0;

	public PrefabFactory(GameObject prefab) : this(prefab, prefab.name) { }

	public PrefabFactory(GameObject prefab, string name) {
		this.prefab = prefab;
		this.name = name;
	}

	public T Create() {
		GameObject tempGameObject = GameObject.Instantiate(prefab) as GameObject;
		tempGameObject.name = name + index.ToString();
		T objectOfType = tempGameObject.GetComponent<T>();
		index++;
		return objectOfType;
	}
}

This should cover most use cases and if not it should not be hard to write a factory that fits your needs.

Okay we’re almost there we still need an interface for the pool members to enforce them to have a reset method that restores a pool member to its initial state when we release a member back to the pool.

public interface IResettable {
	void Reset();
}

Now let’s make T of Pool<T> implement IResettable like so.

public class Pool<T> where T : IResettable {
...

That’s it, you now wrote your very own generic object pool.

Example

Let’s look at an example on how you could use Pool<T> and IFactory<T> for managing the spawning of enemies.

To use pool we need to make a class that implements the interface IResettable that we want to have in the object pool in this case Enemy.

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

public class Enemy : MonoBehaviour, IResettable {

	public event EventHandler Death;

	void Start() {
	}

	void Update() {
		if(Input.GetKeyDown(KeyCode.Space)) {
			OnDeath();
		}
	}

	public void Reset() {
		gameObject.SetActive(false);
	}

	void OnDeath() {
		Death?.Invoke(this, null);
	}
}

And we need a class that manages the Pool<T> and its members.

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

public class EnemyManager : MonoBehaviour {
	public GameObject prefab;
	Pool<Enemy> pool;

	void Start() {
		pool = new Pool<Enemy>(new PrefabFactory<Enemy>(prefab), 5);
	}

	void Update() {
		if(Input.GetKeyDown(KeyCode.S)) {
			Spawn();
		}
	}

	void Spawn() {
		Enemy enemy = pool.Allocate();

		EventHandler handler = null;
		handler = (sender, e) => {
			pool.Release(enemy);
			enemy.Death -= handler;
		};

		enemy.Death += handler;
		enemy.gameObject.SetActive(true);
	}
}

Wrapping Up

Thanks for taking the time to work through this tutorial. All the code we have written can be found on GitHub as a Unity3D package for the package manager at LiamEderzeel/Pool.

I hope you found this tutorial useful and helped you grasp a better understanding of object pools and writing generic code. I’d love to know how it helped you in developing your game.

Feel free to share a link to showcase your work in the comments. Questions, thoughts or improvements are always welcome too.

Leave a Reply