I’ve written a generic object pooling class that should be able to pool instances of any class that inherits from UnityEngine.Object without making any changes to that class. I’ve also written some classes that simplify pooling for commonly used object types like GameObjects and MonoBehaviours, and an interface so that they can be used interchangeably after construction and new classes can be written easily. I’d like to put it on the Unify Community Wiki, but first I want to make sure that the code is as useful, concise and intuitive as it can be. Suggestions welcome.
sample use of GameObjectPool:
IPool<GameObject> goPool = new GameObjectPool(prefab, 40);
GameObject poolie = goPool.Get();
goPool.Return(poolie);
sample use of GenericPool with anonymous methods:
IPool<CustomType> customPool = new GenericPool<CustomType>
(
delegate() {return new CustomType();},
80,
delegate(CustomType customType) {customType.value = 0;},
delegate(CustomType customType) {customType.activated = false;},
delegate(CustomType customType) {customType.activated = true;},
);
sample use of GenericPool with an object to copy and method references:
IPool<CustomType> customPool = new GenericPool<CustomType> (customInstance, 80, Reset, Disable, Enable);
the code itself:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Linq;
public interface IPool<T> {
T Get();
void Return (T objectToPool);
void CopyTo (T[] array);
int Count{get;}
}
public class GenericPool<T> : UnityEngine.Object, IPool<T> where T : UnityEngine.Object {
protected HashSet<T> pool;
protected T original; //if provided, all pool objects will be copies of this object
protected Func<T> Constructor; //creates the pool objects from a given method.
protected Action<T> Recycler; //called on each object when it is returned to the pool. it should reset all changed values.
protected Action<T> Disabler;
//called on each object when it is returned to the pool. it should "hide" the object from anything that might try to
//interact with it, or keep the object from doing anything.
protected Action<T> Activator;
//called on each object when it is gotten from the pool. it should undo whatever the disabler did so that the object
//is able to act freely again.
public int Count {get { return pool.Count; }} //gets the current total number of objects in pool.
//create a pool containing copies of a given object.
public GenericPool (T original, int initialSize, Action<T> Recycler, Action<T> Disabler, Action<T> Activator) {
this.original = original;
this.Recycler = Recycler;
this.Disabler = Disabler;
this.Activator = Activator;
for (int i = 0; i < initialSize; i++)
{
pool.Add(MakeNew());
}
}
//create a pool of objects built by a passed-in function.
public GenericPool (Func<T> Constructor, int initialSize, Action<T> Recycler, Action<T> Disabler, Action<T> Activator) {
this.Constructor = Constructor;
this.Recycler = Recycler;
this.Disabler = Disabler;
this.Activator = Activator;
for (int i = 0; i < initialSize; i++)
{
pool.Add(MakeNew());
}
}
protected GenericPool () {
}
//creates a new instance of whatever object we're pooling
protected virtual T MakeNew () {
T copy;
if (original != null) //if the pool was provided an original objec to copy, copy it
{
copy = Instantiate(original) as T;
Disabler(copy);
}
else if (Constructor != null) //if a constructor was supplied instead, call it
{
copy = Constructor();
Disabler(copy);
}
else
{
copy = null;
Debug.Log("couldn't create object for pool because there was no original to copy and no constructor provided.");
}
return copy;
}
T IPool<T>.Get () {
T unPooledObject;
if (Count == 0)
{
unPooledObject = MakeNew();
}
else
{
unPooledObject = pool.First();
pool.Remove(unPooledObject);
}
if (Activator != null)
{
Activator(unPooledObject);
}
return unPooledObject;
}
void IPool<T>.Return (T objectToPool) {
if (Recycler != null)
{
Recycler(objectToPool);
}
if (Disabler != null)
{
Disabler(objectToPool);
}
pool.Add(objectToPool);
}
void IPool<T>.CopyTo (T[] array) {
pool.CopyTo(array);
}
}
public class GameObjectPool : GenericPool<GameObject> {
public GameObjectPool (GameObject original, int initialSize, Action<GameObject> Recycler = null) {
pool = new HashSet<GameObject>();
this.original = original;
this.Recycler = Recycler;
this.Disabler = Disable;
this.Activator = Activate;
for (int i = 0; i < initialSize; i++)
{
pool.Add(MakeNew());
}
}
private void Disable (GameObject go) {
go.SetActiveRecursively(false);
go.hideFlags = HideFlags.HideInHierarchy;
}
private void Activate (GameObject go) {
go.hideFlags = 0;
go.SetActiveRecursively(true);
}
}
public class ComponentPool<T> : GenericPool<T> where T : UnityEngine.Component {
protected GameObject attatchedObject;
public ComponentPool (GameObject attatchedObject, int initialSize, Func<T> Constructor = null, Action<T> Recycler = null) {
pool = new HashSet<T>();
this.attatchedObject = attatchedObject;
this.Constructor = Constructor;
this.Recycler = Recycler;
for (int i = 0; i < initialSize; i++)
{
pool.Add(MakeNew());
}
}
protected ComponentPool () {
}
protected override T MakeNew () {
T copy;
if (Constructor != null)
{
copy = Constructor();
}
else
{
copy = attatchedObject.AddComponent<T>();
}
return copy;
}
}
public class MonoBehaviourPool<T> : ComponentPool<T> where T : UnityEngine.MonoBehaviour {
public MonoBehaviourPool (GameObject attatchedObject, int initialSize, Func<T> Constructor = null, Action<T> Recycler = null) {
pool = new HashSet<T>();
this.attatchedObject = attatchedObject;
this.Constructor = Constructor;
this.Recycler = Recycler;
this.Disabler = Disable;
this.Activator = Activate;
for (int i = 0; i < initialSize; i++)
{
T copy = MakeNew();
copy.enabled = false;
pool.Add(copy);
}
}
private void Activate (T behaviour) {
behaviour.enabled = true;
}
private void Disable (T behaviour) {
behaviour.CancelInvoke();
behaviour.StopAllCoroutines();
behaviour.enabled = false;
}
}