I've made 2 object pooling classes -
MonoObjectPooler : MonoBehaviour
- can hold a single bucket of pooled objects of 1 type.
PolyObjectPooler : MonoBehaviour
- can hold multiple buckets of pooled objects of different types.
The object to pool information is store in a class PooledObject
/// <summary>
/// Information holder for a pooled object
/// </summary>
[Serializable]
public class PooledObject
{
[Tooltip(@"Name is used to differ the objects from one another")]
public string Name;
[Tooltip(@"What object should be created ?")]
public GameObject Object;
[Range(1, 10000)] [Tooltip(@"How much objects should be created ?")]
public int Amount;
[Tooltip(@"Can new objects be created in case there are none left ?")]
public bool CanGrow;
[Tooltip(@"False - objects must be created manually using Populate method
True - objects will be created automatically on awake")]
public bool CreateOnAwake;
}
Which is later used in the 2 object pooling classes.
MonoObjectPooler : MonoBehaviour
/// <summary>
/// Object pooler class which can hold a single type of objects.
/// </summary>
public class MonoObjectPooler : MonoBehaviour
{
/// <summary>
/// Object to be pooled.
/// </summary>
public PooledObject PooledObject;
/// <summary>
/// List to store all the objects that will be pooled.
/// </summary>
private readonly List<GameObject> pooledObjects = new List<GameObject>();
private void Awake()
{
//Create the pooled object if the CreateOnAwake variable is set to true.
if (PooledObject.CreateOnAwake)
{
Populate();
}
}
/// <summary>
/// Populates the list of pooled objects with PooledObjects.
/// </summary>
public void Populate()
{
//Clear the previous items in the list.
pooledObjects.Clear();
//Load the items again
for (int i = 0; i < PooledObject.Amount; i++)
{
GameObject obj = Instantiate(PooledObject.Object);
obj.SetActive(false);
pooledObjects.Add(obj);
}
}
/// <summary>
/// Returns a PooledObject.
/// </summary>
public GameObject GetPooledObject()
{
for (int i = 0; i < pooledObjects.Count; i++)
{
if (!pooledObjects[i].activeInHierarchy)
{
//we have an available object
return pooledObjects[i];
}
}
if (PooledObject.CanGrow)
{
//we ran out of objects but we can create more
GameObject obj = Instantiate(PooledObject.Object);
pooledObjects.Add(obj);
return obj;
}
//We ran out of objects and we cant create more
return null;
}
}
PolyObjectPooler : MonoBehaviour
/// <summary>
/// Object pooler class which can hold many different types of objects at one place.
/// </summary>
public class PolyObjectPooler : MonoBehaviour
{
/// <summary>
/// Array to store all the objects that will be pooled.
/// </summary>
public PooledObject[] ObjectsToPool;
/// <summary>
/// Dictionary which holds all the created objects i.e the one you get objects from.
/// </summary>
private readonly Dictionary<string, List<GameObject>> pooledObjects =
new Dictionary<string, List<GameObject>>();
/// <summary>
/// Dictionary to hold information about each separate pooled object.
/// </summary>
private readonly Dictionary<string, PooledObject> pooledObjectsContainer =
new Dictionary<string, PooledObject>();
private void Awake()
{
//Load all the required info into the dictionary.
foreach (PooledObject objectToPool in ObjectsToPool)
{
pooledObjects.Add(objectToPool.Name, new List<GameObject>());
pooledObjectsContainer.Add(objectToPool.Name, objectToPool);
//Create any pooled object with the CreateOnAwake variable set to true.
if (objectToPool.CreateOnAwake)
{
Populate(objectToPool.Name);
}
}
}
/// <summary>
/// Creates objects by a specified pooled object name.
/// </summary>
/// <param name="objectName"> The name of the pooled object. </param>
public void Populate(string objectName)
{
//Clear the previous items in the list.
pooledObjects[objectName].Clear();
//Load the items again
for (int i = 0; i < pooledObjectsContainer[objectName].Amount; i++)
{
GameObject obj = Instantiate(pooledObjectsContainer[objectName].Object);
obj.SetActive(false);
pooledObjects[objectName].Add(obj);
}
}
/// <summary>
/// Returns a GameObject by a specified pooled object name.
/// </summary>
/// <param name="objectName">The name of the pooled object.</param>
public GameObject GetPooledObject(string objectName)
{
for (int i = 0; i < pooledObjects[objectName].Count; i++)
{
if (!pooledObjects[objectName][i].activeInHierarchy)
{
//we have an available object
return pooledObjects[objectName][i];
}
}
if (pooledObjectsContainer[objectName].CanGrow)
{
//we ran out of objects but we can create more
GameObject obj = Instantiate(pooledObjectsContainer[objectName].Object);
pooledObjects[objectName].Add(obj);
return obj;
}
//We ran out of objects and we cant create more
return null;
}
}
The code is relatively short and I putted a lot of comments for each method so it should be easy to understand and follow the logic, any performance and code style improvements are welcome.