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); } } } }