using System;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Core.Utilities
{
///
/// Maintains a pool of objects
///
public class Pool
{
///
/// Our factory function
///
protected Func m_Factory;
///
/// Our resetting function
///
protected readonly Action m_Reset;
///
/// A list of all m_Available items
///
protected readonly List m_Available;
///
/// A list of all items managed by the pool
///
protected readonly List m_All;
///
/// Create a new pool with a given number of starting elements
///
/// The function that creates pool objects
/// Function to use to m_Reset items when retrieving from the pool
/// The number of elements to seed the pool with
public Pool(Func factory, Action reset, int initialCapacity)
{
if (factory == null)
{
throw new ArgumentNullException("factory");
}
m_Available = new List();
m_All = new List();
m_Factory = factory;
m_Reset = reset;
if (initialCapacity > 0)
{
Grow(initialCapacity);
}
}
///
/// Creates a new blank pool
///
/// The function that creates pool objects
public Pool(Func factory)
: this(factory, null, 0)
{
}
///
/// Create a new pool with a given number of starting elements
///
/// The function that creates pool objects
/// The number of elements to seed the pool with
public Pool(Func factory, int initialCapacity)
: this(factory, null, initialCapacity)
{
}
///
/// Gets an item from the pool, growing it if necessary
///
///
public virtual T Get()
{
return Get(m_Reset);
}
///
/// Gets an item from the pool, growing it if necessary, and with a specified m_Reset function
///
/// A function to use to m_Reset the given object
public virtual T Get(Action resetOverride)
{
if (m_Available.Count == 0)
{
Grow(1);
}
if (m_Available.Count == 0)
{
throw new InvalidOperationException("Failed to grow pool");
}
int itemIndex = m_Available.Count - 1;
T item = m_Available[itemIndex];
m_Available.RemoveAt(itemIndex);
if (resetOverride != null)
{
resetOverride(item);
}
return item;
}
///
/// Gets whether or not this pool contains a specified item
///
public virtual bool Contains(T pooledItem)
{
return m_All.Contains(pooledItem);
}
///
/// Return an item to the pool
///
public virtual void Return(T pooledItem)
{
if (m_All.Contains(pooledItem) &&
!m_Available.Contains(pooledItem))
{
ReturnToPoolInternal(pooledItem);
}
else
{
throw new InvalidOperationException("Trying to return an item to a pool that does not contain it: " + pooledItem +
", " + this);
}
}
///
/// Return all items to the pool
///
public virtual void ReturnAll()
{
ReturnAll(null);
}
///
/// Returns all items to the pool, and calls a delegate on each one
///
public virtual void ReturnAll(Action preReturn)
{
for (int i = 0; i < m_All.Count; ++i)
{
T item = m_All[i];
if (!m_Available.Contains(item))
{
if (preReturn != null)
{
preReturn(item);
}
ReturnToPoolInternal(item);
}
}
}
///
/// Grow the pool by a given number of elements
///
public void Grow(int amount)
{
for (int i = 0; i < amount; ++i)
{
AddNewElement();
}
}
///
/// Returns an object to the m_Available list. Does not check for consistency
///
protected virtual void ReturnToPoolInternal(T element)
{
m_Available.Add(element);
}
///
/// Adds a new element to the pool
///
protected virtual T AddNewElement()
{
T newElement = m_Factory();
m_All.Add(newElement);
m_Available.Add(newElement);
return newElement;
}
///
/// Dummy factory that returns the default T value
///
protected static T DummyFactory()
{
return default(T);
}
}
///
/// A variant pool that takes Unity components. Automatically enables and disables them as necessary
///
public class UnityComponentPool : Pool
where T : Component
{
///
/// Create a new pool with a given number of starting elements
///
/// The function that creates pool objects
/// Function to use to reset items when retrieving from the pool
/// The number of elements to seed the pool with
public UnityComponentPool(Func factory, Action reset, int initialCapacity)
: base(factory, reset, initialCapacity)
{
}
///
/// Creates a new blank pool
///
/// The function that creates pool objects
public UnityComponentPool(Func factory)
: base(factory)
{
}
///
/// Create a new pool with a given number of starting elements
///
/// The function that creates pool objects
/// The number of elements to seed the pool with
public UnityComponentPool(Func factory, int initialCapacity)
: base(factory, initialCapacity)
{
}
///
/// Retrieve an enabled element from the pool
///
public override T Get(Action resetOverride)
{
T element = base.Get(resetOverride);
element.gameObject.SetActive(true);
return element;
}
///
/// Automatically disable returned object
///
protected override void ReturnToPoolInternal(T element)
{
element.gameObject.SetActive(false);
base.ReturnToPoolInternal(element);
}
///
/// Keep newly created objects disabled
///
protected override T AddNewElement()
{
T newElement = base.AddNewElement();
newElement.gameObject.SetActive(false);
return newElement;
}
}
///
/// A variant pool that takes Unity game objects. Automatically enables and disables them as necessary
///
public class GameObjectPool : Pool
{
///
/// Create a new pool with a given number of starting elements
///
/// The function that creates pool objects
/// Function to use to reset items when retrieving from the pool
/// The number of elements to seed the pool with
public GameObjectPool(Func factory, Action reset, int initialCapacity)
: base(factory, reset, initialCapacity)
{
}
///
/// Creates a new blank pool
///
/// The function that creates pool objects
public GameObjectPool(Func factory)
: base(factory)
{
}
///
/// Create a new pool with a given number of starting elements
///
/// The function that creates pool objects
/// The number of elements to seed the pool with
public GameObjectPool(Func factory, int initialCapacity)
: base(factory, initialCapacity)
{
}
///
/// Retrieve an enabled element from the pool
///
public override GameObject Get(Action resetOverride)
{
GameObject element = base.Get(resetOverride);
element.SetActive(true);
return element;
}
///
/// Automatically disable returned object
///
protected override void ReturnToPoolInternal(GameObject element)
{
element.SetActive(false);
base.ReturnToPoolInternal(element);
}
///
/// Keep newly created objects disabled
///
protected override GameObject AddNewElement()
{
GameObject newElement = base.AddNewElement();
newElement.SetActive(false);
return newElement;
}
}
///
/// Variant pool that automatically instantiates objects from a given Unity gameobject prefab
///
public class AutoGameObjectPrefabPool : GameObjectPool
{
///
/// Create our new prefab item clone
///
GameObject PrefabFactory()
{
GameObject newElement = Object.Instantiate(m_Prefab);
if (m_Initialize != null)
{
m_Initialize(newElement);
}
return newElement;
}
///
/// Our base prefab
///
protected readonly GameObject m_Prefab;
///
/// Initialisation method for objects
///
protected readonly Action m_Initialize;
///
/// Create a new pool for the given Unity prefab
///
/// The prefab we're cloning
public AutoGameObjectPrefabPool(GameObject prefab)
: this(prefab, null, null, 0)
{
}
///
/// Create a new pool for the given Unity prefab
///
/// The prefab we're cloning
/// An initialisation function to call after creating prefabs
public AutoGameObjectPrefabPool(GameObject prefab, Action initialize)
: this(prefab, initialize, null, 0)
{
}
///
/// Create a new pool for the given Unity prefab
///
/// The prefab we're cloning
/// An initialisation function to call after creating prefabs
/// Function to use to reset items when retrieving from the pool
public AutoGameObjectPrefabPool(GameObject prefab, Action initialize, Action reset)
: this(prefab, initialize, reset, 0)
{
}
///
/// Create a new pool for the given Unity prefab with a given number of starting elements
///
/// The prefab we're cloning
/// The number of elements to seed the pool with
public AutoGameObjectPrefabPool(GameObject prefab, int initialCapacity)
: this(prefab, null, null, initialCapacity)
{
}
///
/// Create a new pool for the given Unity prefab
///
/// The prefab we're cloning
/// An initialisation function to call after creating prefabs
/// Function to use to reset items when retrieving from the pool
/// The number of elements to seed the pool with
public AutoGameObjectPrefabPool(GameObject prefab, Action initialize, Action reset,
int initialCapacity)
: base(DummyFactory, reset, 0)
{
// Pass 0 to initial capacity because we need to set ourselves up first
// We then call Grow again ourselves
m_Initialize = initialize;
m_Prefab = prefab;
m_Factory = PrefabFactory;
if (initialCapacity > 0)
{
Grow(initialCapacity);
}
}
}
///
/// Variant pool that automatically instantiates objects from a given Unity component prefab
///
public class AutoComponentPrefabPool : UnityComponentPool
where T : Component
{
///
/// Create our new prefab item clone
///
T PrefabFactory()
{
T newElement = Object.Instantiate(m_Prefab);
if (m_Initialize != null)
{
m_Initialize(newElement);
}
return newElement;
}
///
/// Our base prefab
///
protected readonly T m_Prefab;
///
/// Initialisation method for objects
///
protected readonly Action m_Initialize;
///
/// Create a new pool for the given Unity prefab
///
/// The prefab we're cloning
public AutoComponentPrefabPool(T prefab)
: this(prefab, null, null, 0)
{
}
///
/// Create a new pool for the given Unity prefab
///
/// The prefab we're cloning
/// An initialisation function to call after creating prefabs
public AutoComponentPrefabPool(T prefab, Action initialize)
: this(prefab, initialize, null, 0)
{
}
///
/// Create a new pool for the given Unity prefab
///
/// The prefab we're cloning
/// An initialisation function to call after creating prefabs
/// Function to use to reset items when retrieving from the pool
public AutoComponentPrefabPool(T prefab, Action initialize, Action reset)
: this(prefab, initialize, reset, 0)
{
}
///
/// Create a new pool for the given Unity prefab with a given number of starting elements
///
/// The prefab we're cloning
/// The number of elements to seed the pool with
public AutoComponentPrefabPool(T prefab, int initialCapacity)
: this(prefab, null, null, initialCapacity)
{
}
///
/// Create a new pool for the given Unity prefab
///
/// The prefab we're cloning
/// An initialisation function to call after creating prefabs
/// Function to use to reset items when retrieving from the pool
/// The number of elements to seed the pool with
public AutoComponentPrefabPool(T prefab, Action initialize, Action reset, int initialCapacity)
: base(DummyFactory, reset, 0)
{
// Pass 0 to initial capacity because we need to set ourselves up first
// We then call Grow again ourselves
m_Initialize = initialize;
m_Prefab = prefab;
m_Factory = PrefabFactory;
if (initialCapacity > 0)
{
Grow(initialCapacity);
}
}
}
}