Code

Code

I was recently working on a project requiring spawning and despawning lots of objects.  Obviously doing so is WAY less efficient than simply recycling existing objects, so I wrote a nice system to pool gameobjects.  The system is comprised of 3 parts:

  • There is a PooledObject component, which goes on any gameobject we wish to pool.
  • There is an IPooledComponent interface, which will go on any component requiring resetting when the gameobject is recycled.
  • Then there is the ObjectPooler itself, which provides a nice API for the rest of the codebase to fetch gameobjects from a pool.

The API allows you to just ask for a gameobject, and if it’s not a pooled object, it will add the PooledObject component, create a pool, and return one of the objects from the pool’s stack.  I wish to keep most of the code closed source for now, but I’ve provided a representative excerpt below.  This code is written for Unity in C#.

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

public interface IPooledComponent
{
	void ResetPooledComponent();
}

// Hello there internet stranger.  
// Unfortunately, I'd like to keep the rest of my source code to myself for the time being, so there is no more of this class to show you.
// I'd be happy to talk with you about it, you can contact me by email: dusty.hunsaker at gmail

public class ObjectPooler : Singleton<ObjectPooler>
{
	[SerializeField]
	private ObjectPool[] PredefinedObjectPools;

	private Dictionary<string, ObjectPool> ObjectPoolDict = new Dictionary<string, ObjectPool>();

	// Add serialized pools into dictionary when everything is initialized
	private void Awake()
	{
		if (PredefinedObjectPools.IsNullOrEmpty())
		{
			return;
		}
		for (int i = 0; i < PredefinedObjectPools.Length; i++)
		{
			ObjectPool pool = PredefinedObjectPools[i];
			if (pool == null)
			{
				Debug.LogWarning("Found a null pool when setting up on: " + name);
				continue;
			}
			AddPoolToDictionary(pool);
		}
	}

	/// <summary>
	/// Return a gameobject from the string type of the pool.
	/// </summary>
	/// <param name="pooledType">A unique string to identify the pool</param>
	/// <param name="newPosition">Optional param places the object at this position</param>
	/// <param name="newRotation">Optional param places the object with this rotation</param>
	/// <returns></returns>
	public GameObject GetPooledGameObject(string pooledType, Vector3 newPosition = default(Vector3), Quaternion newRotation = default(Quaternion))
	{
		if (ObjectPoolDict == null)
		{
			Debug.LogError("Can't get pooled object, because pools are null on: " + name);
			return null;
		}
		ObjectPool objectPool = null;
		if (TryGetPoolOfType(pooledType, out objectPool))
		{
			return objectPool.GetPooledGameObject(newPosition, newRotation);
		}
		Debug.LogError("Cannot find pool of type: " + pooledType);
		return null;
	}

	/// <summary>
	/// Returns an object in the same pool as the pooled object passed in.
	/// </summary>
	/// <param name="pooledObj">The component which identifies the pooled type</param>
	/// <param name="newPosition">Optional param places the object at this position</param>
	/// <param name="newRotation">Optional param places the object with this rotation</param>
	/// <returns></returns>
	// This looks a little weird, but we don't actually want the pooled object passed in,
	// we want one from the top of the stack, which may or may not be the one passed in.
	public GameObject GetPooledGameObject(PooledObject pooledObj, Vector3 newPosition = default(Vector3), Quaternion newRotation = default(Quaternion))
	{
		if (pooledObj == null)
		{
			Debug.LogError("Can't get a GameObject from a null PooledObject!");
			return null;
		}
		if (ObjectPoolDict == null)
		{
			Debug.LogError("Can't get pooled object, because pools are null on: " + name);
			return null;
		}
		ObjectPool objectPool = null;
		if (TryGetPoolOfType(pooledObj.PooledType, out objectPool))
		{
			return objectPool.GetPooledGameObject(newPosition, newRotation);
		}
		return CreateNewPool(pooledObj).GetPooledGameObject(newPosition, newRotation);
	}

	private bool TryGetPoolOfType(string poolType, out ObjectPool objectPool)
	{
		objectPool = null;
		if (poolType.IsNullOrEmpty())
		{
			return false;
		}
		if (ObjectPoolDict == null)
		{
			Debug.LogError("Can't get pooled object, because pools are null on: " + name);
			return false;
		}
		if (ObjectPoolDict.ContainsKey(poolType))
		{
			objectPool = ObjectPoolDict[poolType];
			return true;
		}
		return false;
	}

	private ObjectPool CreateNewPool(PooledObject templateObject)
	{
		if (templateObject == null)
		{
			Debug.LogError("Can't create pool out of null templateObject!");
			return null;
		}
		if (templateObject.PooledType.IsNullOrEmpty())
		{
			Debug.LogError("Can't create an object pool with a null or empty type!");
			return null;
		}
		ObjectPool newPool = new ObjectPool(templateObject);
		AddPoolToDictionary(newPool);
		return newPool;
	}

#if UNITY_EDITOR
	[SerializeField]
	private List<ObjectPool> EditorOnlyPoolViewer = new List<ObjectPool>();
#endif
	private void AddPoolToDictionary(ObjectPool objectPool)
	{
		if (objectPool == null)
		{
			Debug.LogError("Tried to add a null objectPool to the dictionary.  Not gonna let that happen.");
			return;
		}
		if (objectPool.PooledType.IsNullOrEmpty())
		{
			Debug.LogError("Can't create an object pool with a null or empty type!");
			return;
		}
		if (ObjectPoolDict == null)
		{
			Debug.LogError("Can't add pool to a null dictionary on: " + name);
			return;
		}
		if (ObjectPoolDict.ContainsKey(objectPool.PooledType))
		{
			Debug.LogError("Tried to add a new pool out of a type that already exists in the dictionary.  I can't let you do that, Dave.");
			return;
		}
		ObjectPoolDict.Add(objectPool.PooledType, objectPool);
#if UNITY_EDITOR
		EditorOnlyPoolViewer.Add(objectPool);
#endif
	}

	public void ReturnToPool(GameObject go)
	{
		if (go == null)
		{
			Debug.LogError("Can't return null GameObject to pool");
			return;
		}
		ReturnToPool(go.GetComponent<PooledObject>());
	}

	public void ReturnToPool(PooledObject pooledObj)
	{
		if (pooledObj == null)
		{
			Debug.LogError("Can't return a null PooledObject to pool");
			return;
		}
		ObjectPool objectPool;
		if (TryGetPoolOfType(pooledObj.PooledType, out objectPool))
		{
			objectPool.ReturnObjectToPool(pooledObj);
		}
	}
}