From 8c1203a04e73f5794ef049d8c68e27d679730b7a Mon Sep 17 00:00:00 2001 From: "DUBITLIMITED\\david.smithson" Date: Thu, 15 Mar 2018 10:36:31 +0000 Subject: [PATCH 1/3] Object pooling utility --- Utils/ObjectPool.cs | 161 +++++++++++++++++++++++++++++++++++++++ Utils/ObjectPool.cs.meta | 13 ++++ 2 files changed, 174 insertions(+) create mode 100644 Utils/ObjectPool.cs create mode 100644 Utils/ObjectPool.cs.meta diff --git a/Utils/ObjectPool.cs b/Utils/ObjectPool.cs new file mode 100644 index 0000000..6c35ac7 --- /dev/null +++ b/Utils/ObjectPool.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace DUCK.Pooling +{ + public abstract class PoolableComponent : MonoBehaviour + { + protected internal virtual void OnAddedToPool() + { + enabled = false; + gameObject.SetActive(false); + } + + protected internal virtual void OnRemovedFromPool() + { + gameObject.SetActive(true); + enabled = true; + } + + protected internal abstract void CopyFrom(PoolableComponent instance); + } + + public static class ObjectPool + { + private abstract class AbstractPool + { + public abstract Type ObjectType { get; } + + public abstract void Add(PoolableComponent obj); + } + + private class Pool : AbstractPool + where T : PoolableComponent + { + private Queue pooledObjects = new Queue(); + + public override Type ObjectType + { + get + { + return typeof(T); + } + } + + public T Remove() + { + var obj = (pooledObjects.Count > 0) + ? pooledObjects.Dequeue() + : null; + + if (obj) + { + obj.OnRemovedFromPool(); + } + + return obj; + } + + public override void Add(PoolableComponent obj) + { + if (obj != null) + { + obj.OnAddedToPool(); + pooledObjects.Enqueue((T)obj); + } + } + } + + private static Dictionary poolLookup = new Dictionary(); + + public static T Instantiate(T original = null) where T : PoolableComponent + { + T obj = null; + + if (PoolExists(typeof(T))) + { + obj = GetFromPool(); + + if (obj != null) + { + if (original != null) + { + obj.CopyFrom(original); + } + + return obj; + } + } + else + { + CreatePool(); + } + + return obj ?? GameObject.Instantiate(original); + } + + public static T Instantiate(string resourcePath) where T : PoolableComponent + { + T obj = null; + + if (PoolExists(typeof(T))) + { + obj = GetFromPool(); + } + else + { + CreatePool(); + } + + return obj ?? Resources.Load(resourcePath); + } + + public static void Destroy(PoolableComponent obj) + { + if (obj == null) return; + + if (PoolExists(obj.GetType())) + { + ReturnToPool(obj); + } + else + { + Destroy(obj); + } + } + + private static void CreatePool() where T : PoolableComponent + { + if (PoolExists(typeof(T))) return; + + poolLookup.Add(typeof(T), new Pool()); + } + + private static Pool FindPool() where T : PoolableComponent + { + if (!PoolExists(typeof(T))) throw new KeyNotFoundException(typeof(T).ToString()); + + return (Pool )poolLookup[typeof(T)]; + } + + private static bool PoolExists(Type type) + { + return poolLookup.ContainsKey(type); + } + + private static T GetFromPool() where T : PoolableComponent + { + return PoolExists(typeof(T)) + ? FindPool().Remove() + : null; + } + + private static void ReturnToPool(PoolableComponent obj) + { + if (!PoolExists(obj.GetType())) return; + + poolLookup[obj.GetType()].Add(obj); + } + } +} diff --git a/Utils/ObjectPool.cs.meta b/Utils/ObjectPool.cs.meta new file mode 100644 index 0000000..21949a1 --- /dev/null +++ b/Utils/ObjectPool.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: fd93943eb28cba74bb9c28d10409dabd +timeCreated: 1521045479 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 2e4b6b5eccd54231520a6becb3b054a9ea10ee20 Mon Sep 17 00:00:00 2001 From: "DUBITLIMITED\\david.smithson" Date: Fri, 16 Mar 2018 10:42:10 +0000 Subject: [PATCH 2/3] PR feedback: composition --- Utils/ObjectPool.cs | 55 +++++++++++++----------------------- Utils/PoolableObject.cs | 24 ++++++++++++++++ Utils/PoolableObject.cs.meta | 13 +++++++++ 3 files changed, 56 insertions(+), 36 deletions(-) create mode 100644 Utils/PoolableObject.cs create mode 100644 Utils/PoolableObject.cs.meta diff --git a/Utils/ObjectPool.cs b/Utils/ObjectPool.cs index 6c35ac7..a46bd11 100644 --- a/Utils/ObjectPool.cs +++ b/Utils/ObjectPool.cs @@ -4,38 +4,21 @@ namespace DUCK.Pooling { - public abstract class PoolableComponent : MonoBehaviour - { - protected internal virtual void OnAddedToPool() - { - enabled = false; - gameObject.SetActive(false); - } - - protected internal virtual void OnRemovedFromPool() - { - gameObject.SetActive(true); - enabled = true; - } - - protected internal abstract void CopyFrom(PoolableComponent instance); - } - public static class ObjectPool { - private abstract class AbstractPool + private interface Pool { - public abstract Type ObjectType { get; } + Type ObjectType { get; } - public abstract void Add(PoolableComponent obj); + void Add(PoolableObject obj); } - private class Pool : AbstractPool - where T : PoolableComponent + private class Pool : Pool + where T : PoolableObject { private Queue pooledObjects = new Queue(); - public override Type ObjectType + public Type ObjectType { get { @@ -43,7 +26,7 @@ public override Type ObjectType } } - public T Remove() + public T Get() { var obj = (pooledObjects.Count > 0) ? pooledObjects.Dequeue() @@ -51,25 +34,25 @@ public T Remove() if (obj) { - obj.OnRemovedFromPool(); + obj.RemoveFromPool(); } return obj; } - public override void Add(PoolableComponent obj) + public void Add(PoolableObject obj) { if (obj != null) { - obj.OnAddedToPool(); + obj.AddToPool(); pooledObjects.Enqueue((T)obj); } } } - private static Dictionary poolLookup = new Dictionary(); + private static Dictionary poolLookup = new Dictionary(); - public static T Instantiate(T original = null) where T : PoolableComponent + public static T Instantiate(T original = null) where T : PoolableObject { T obj = null; @@ -95,7 +78,7 @@ public static T Instantiate(T original = null) where T : PoolableComponent return obj ?? GameObject.Instantiate(original); } - public static T Instantiate(string resourcePath) where T : PoolableComponent + public static T Instantiate(string resourcePath) where T : PoolableObject { T obj = null; @@ -111,7 +94,7 @@ public static T Instantiate(string resourcePath) where T : PoolableComponent return obj ?? Resources.Load(resourcePath); } - public static void Destroy(PoolableComponent obj) + public static void Destroy(PoolableObject obj) { if (obj == null) return; @@ -125,14 +108,14 @@ public static void Destroy(PoolableComponent obj) } } - private static void CreatePool() where T : PoolableComponent + private static void CreatePool() where T : PoolableObject { if (PoolExists(typeof(T))) return; poolLookup.Add(typeof(T), new Pool()); } - private static Pool FindPool() where T : PoolableComponent + private static Pool FindPool() where T : PoolableObject { if (!PoolExists(typeof(T))) throw new KeyNotFoundException(typeof(T).ToString()); @@ -144,14 +127,14 @@ private static bool PoolExists(Type type) return poolLookup.ContainsKey(type); } - private static T GetFromPool() where T : PoolableComponent + private static T GetFromPool() where T : PoolableObject { return PoolExists(typeof(T)) - ? FindPool().Remove() + ? FindPool().Get() : null; } - private static void ReturnToPool(PoolableComponent obj) + private static void ReturnToPool(PoolableObject obj) { if (!PoolExists(obj.GetType())) return; diff --git a/Utils/PoolableObject.cs b/Utils/PoolableObject.cs new file mode 100644 index 0000000..281db91 --- /dev/null +++ b/Utils/PoolableObject.cs @@ -0,0 +1,24 @@ +using DUCK.Utils; +using System; +using UnityEngine; + +namespace DUCK.Pooling +{ + public class PoolableObject : MonoBehaviour + { + public event Action OnAddedToPool; + public event Action OnRemovedFromPool; + + public void AddToPool() + { + OnAddedToPool.SafeInvoke(); + } + + public void RemoveFromPool() + { + OnRemovedFromPool.SafeInvoke(); + } + + public void CopyFrom(PoolableObject instance) { } + } +} diff --git a/Utils/PoolableObject.cs.meta b/Utils/PoolableObject.cs.meta new file mode 100644 index 0000000..1a3d445 --- /dev/null +++ b/Utils/PoolableObject.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: a46d3d1059954344fb851aa38a9e4086 +timeCreated: 1521045479 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From e59ed738f7f54542219339effc59eb78e0dc4c92 Mon Sep 17 00:00:00 2001 From: "DUBITLIMITED\\david.smithson" Date: Wed, 21 Mar 2018 15:31:55 +0000 Subject: [PATCH 3/3] Avoid pulling destroyed objects from the pool, and returning an already-pooled object --- Utils/ObjectPool.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Utils/ObjectPool.cs b/Utils/ObjectPool.cs index a46bd11..a75b184 100644 --- a/Utils/ObjectPool.cs +++ b/Utils/ObjectPool.cs @@ -28,9 +28,11 @@ public Type ObjectType public T Get() { - var obj = (pooledObjects.Count > 0) - ? pooledObjects.Dequeue() - : null; + T obj = null; + while (pooledObjects.Count > 0) + { + obj = pooledObjects.Dequeue(); + } if (obj) { @@ -42,7 +44,7 @@ public T Get() public void Add(PoolableObject obj) { - if (obj != null) + if (obj != null && !pooledObjects.Contains((T)obj)) { obj.AddToPool(); pooledObjects.Enqueue((T)obj);