daily pastebin goal
64%
SHARE
TWEET

Unity3D memory pool

mvaganov May 10th, 2015 (edited) 247 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // MIT license - TL;DR - Do whatever you want with it, I won't fix it for you!
  2. #define FAIL_FAST
  3. using UnityEngine;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6.  
  7. /// <summary>
  8. /// <para>Used for memory recycling of reference types, like GameObjects</para>
  9. /// <para>Example usage:</para>
  10. /// <para>MemoryPool&lt;GameObject&gt; memPool = new MemoryPool&lt;GameObject&gt;(); // construction</para>
  11. /// <para>memPool.Setup(
  12. ///             ()    => { return Instantiate(prefab); },
  13. ///             (obj) => { obj.SetActive(true);  },
  14. ///             (obj) => { obj.SetActive(false); },
  15. ///             (obj) => { Object.Destroy(obj);  }
  16. /// );</para>
  17. /// <para>GameObject gobj = memPool.Alloc();  // allocate object in pool</para>
  18. /// <para>memPool.Free(gobj); // deallocate object in pool</para>
  19. /// <para>MemoryPoolItem.Destroy(gobj); // deallocate object in pool OR Object.Destroy non-MemoryPool object (for GameObjects only)</para>
  20. /// </summary>
  21. public class MemoryPool<T> where T : class {
  22.         private List<T> allObjects = null;
  23.         private int freeObjectCount = 0;
  24.  
  25.         public delegate    T DelegateBeginLife();
  26.         public delegate void DelegateCommission(T obj);
  27.         public delegate void DelegateDecommission(T obj);
  28.         public delegate void DelegateEndLife(T obj);
  29.  
  30.         public DelegateBeginLife birth;
  31.         public DelegateEndLife death;
  32.         public DelegateCommission commission;
  33.         public DelegateDecommission decommission;
  34.  
  35.         /// <summary></summary>
  36.         /// <returns>True if Setup was called with all non-null methods</returns>
  37.         public bool IsFullySetup() { return birth != null && commission != null && decommission != null && death != null; }
  38.  
  39.         /// <summary>
  40.         /// Example usage:
  41.         /// <para>memPool.Setup(
  42.         ///             ()    => Instantiate(prefab),
  43.         ///             (obj) => obj.SetActive(true),
  44.         ///             (obj) => obj.SetActive(false),
  45.         ///             (obj) => Object.Destroy(obj)
  46.         /// );</para>
  47.         /// </summary>
  48.         /// <param name="create">callback function or delegate used to create a new object of type T</param>
  49.         /// <param name="activate">callback function or delegate used to activate an object of type T</param>
  50.         /// <param name="deactivate">callback function or delegate used to de-activate an object of type T</param>
  51.         /// <param name="destroy">callback function or delegate used to destroy an object of type T</param>
  52.         public void Setup(DelegateBeginLife create, DelegateCommission activate, DelegateDecommission deactivate, DelegateEndLife destroy) {
  53.                 birth = create; commission = activate; decommission = deactivate; death = destroy;
  54.         }
  55.  
  56.         /// <summary>Constructs and calls <see cref="Setup"/></summary>
  57.         public MemoryPool(DelegateBeginLife create, DelegateCommission activate, DelegateDecommission deactivate, DelegateEndLife destroy) {
  58.                 Setup(create, activate, deactivate, destroy);
  59.         }
  60.  
  61.         /// <summary> Be sure to call <see cref="Setup"/>!</summary>
  62.         public MemoryPool() {}
  63.  
  64.         /// <summary>Returns an object from the memory pool, which may have just been created</summary>
  65.         public T Alloc() {
  66.                 T freeObject = null;
  67.                 if(freeObjectCount == 0) {
  68. #if FAIL_FAST
  69.                         if(birth == null) { throw new System.Exception("Call .Setup(), and provide a create method!"); }
  70. #endif
  71.                         if(allObjects == null) { allObjects = new List<T>(); }
  72.                         freeObject = birth();
  73.                         allObjects.Add(freeObject);
  74.                         if(typeof(T) == typeof(GameObject)) {
  75.                                 GameObject go = freeObject as GameObject;
  76.                                 go.AddComponent<MemoryPoolItem>().SetPool(this as MemoryPool<GameObject>);
  77.                         }
  78.                 } else {
  79.                         freeObject = allObjects[allObjects.Count - freeObjectCount];
  80.                         freeObjectCount--;
  81.                 }
  82.                 if(commission != null) { commission(freeObject); }
  83.                 return freeObject;
  84.         }
  85.  
  86.         /// <summary>Which object to mark as free in the memory pool</summary>
  87.         public void Free(T obj) {
  88.                 int indexOfObject = allObjects.IndexOf(obj);
  89. #if FAIL_FAST
  90.                 if(indexOfObject < 0) { throw new System.Exception("woah, this isn't one of mine..."); }
  91.                 if(indexOfObject >= (allObjects.Count - freeObjectCount)) { throw new System.Exception("hey, you're freeing this twice..."); }
  92. #endif
  93.                 freeObjectCount++;
  94.                 int beginningOfFreeList = allObjects.Count - freeObjectCount;
  95.                 allObjects[indexOfObject] = allObjects[beginningOfFreeList];
  96.                 allObjects[beginningOfFreeList] = obj;
  97.                 if(decommission != null) { decommission(obj); }
  98.         }
  99.  
  100.         /// <summary>performs the given delegate on each object in the memory pool</summary>
  101.         public void ForEach(DelegateCommission action) {
  102.                 for(int i = 0; i < allObjects.Count; ++i) { action(allObjects[i]); }
  103.         }
  104.  
  105.         /// <summary>Destroys all objects in the pool, after deactivating each one.</summary>
  106.         public void DeallocateAll() {
  107.                 ForEach((item) => decommission(item));
  108.                 if(typeof(T) == typeof(GameObject)) {
  109.                         ForEach((item) => {
  110.                                 GameObject go = item as GameObject;
  111.                                 Object.DestroyImmediate(go.GetComponent<MemoryPoolItem>());
  112.                         });
  113.                 }
  114.                 if(death != null) { ForEach((item) => death(item)); }
  115.                 allObjects.Clear();
  116.         }
  117. }
  118.  
  119. public class MemoryPool : MonoBehaviour {
  120.         MemoryPool<GameObject> pool = new MemoryPool<GameObject>();
  121.         [Tooltip("An object to create and destroy a lot of"), ContextMenuItem("Create Another", "CreateAnother")]
  122.         public GameObject prefab;
  123.  
  124.         public void Init() {
  125.                 if(!pool.IsFullySetup()) {
  126.                         pool.Setup(() => Instantiate(prefab), (obj) => obj.SetActive(true), (obj) => obj.SetActive(false), (obj) => Destroy(obj));
  127.                 }
  128.         }
  129.  
  130.         public void Awake() { Init(); }
  131.  
  132.         public GameObject CreateAnother() {
  133. #if UNITY_EDITOR
  134.                 Init();
  135. #endif
  136.                 AssertInit(); return pool.Alloc();
  137.         }
  138.  
  139.         public void AssertInit() {
  140. #if FAIL_FAST
  141.                 if(!pool.IsFullySetup()) throw new System.Exception("Call Init() on this memory pool before using it");
  142. #endif
  143.         }
  144.  
  145.         public void Setup(MemoryPool<GameObject>.DelegateBeginLife create, MemoryPool<GameObject>.DelegateCommission activate,
  146.                 MemoryPool<GameObject>.DelegateDecommission deactivate, MemoryPool<GameObject>.DelegateEndLife destroy) {
  147.                 pool.Setup(create, activate, deactivate, destroy);
  148.         }
  149.  
  150.         /// <summary> Be sure to call <see cref="Setup"/>!</summary>
  151.         public MemoryPool() { }
  152.  
  153.         /// <summary>Returns an object from the memory pool, which may have just been created</summary>
  154.         public GameObject Alloc() { AssertInit(); return pool.Alloc(); }
  155.  
  156.         /// <summary>Which object to mark as free in the memory pool</summary>
  157.         public void Free(GameObject obj) { AssertInit(); pool.Free(obj); }
  158.  
  159.         /// <summary>performs the given delegate on each object in the memory pool</summary>
  160.         public void ForEach(MemoryPool<GameObject>.DelegateCommission action) { AssertInit(); pool.ForEach(action); }
  161.  
  162.         /// <summary>Destroys all objects in the pool, after deactivating each one.</summary>
  163.         public void DeallocateAll() { AssertInit(); pool.DeallocateAll(); }
  164.  
  165.         /// <summary>If the given GameObject belongs to a memory pool, mark it as free in that pool. Otherwise, Object.Destroy()</summary>
  166.         static public void Destroy(GameObject go) { MemoryPoolItem.Destroy(go); }
  167. }
  168.  
  169. [System.Serializable]
  170. public class MemoryPoolItem : MonoBehaviour {
  171.         private MemoryPool<GameObject> gameObjectPool;
  172.         public MemoryPoolItem SetPool(MemoryPool<GameObject> pool) { gameObjectPool = pool; return this; }
  173.         static private bool shuttingDown = false;
  174.         static public void SetShutdown(bool sceneIsEnding) { shuttingDown = sceneIsEnding; }
  175. #if FAIL_FAST
  176.         void OnApplicationQuit() { SetShutdown(true); }
  177.         void Start() { SetShutdown(false); }
  178.         void OnDestroy() {
  179.                 if(!shuttingDown) throw new System.Exception("Instead of Object.Destroy("+gameObject+"), call MemoryPoolItem.Destroy("+gameObject+")\n"
  180.                         +"When changing levels, call MemoryPoolItem.SetShutdown(true) first");
  181.         }
  182. #endif
  183.         public void FreeSelf() { gameObjectPool.Free(gameObject); }
  184.         /// <summary>If the given GameObject belongs to a memory pool, mark it as free in that pool. Otherwise, Object.Destroy()</summary>
  185.         static public void Destroy(GameObject go) {
  186.                 MemoryPoolItem i = go.GetComponent<MemoryPoolItem>();
  187.                 if(i != null) { i.FreeSelf(); }
  188.                 else { Object.Destroy(go); }
  189.         }
  190. }
RAW Paste Data
Top