diff --git a/README.md b/README.md index 38b1e88..52dbeb4 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,25 @@ Also, you can just put PoolInstaller component on any object on Scene and select ![](https://i.imgur.com/gnyZ0RQ.png) +### How to clear pool and it's instances +```csharp +using ToolBox.Pools; +using UnityEngine; + +public class Spawner : MonoBehaviour +{ + [SerializeField] private GameObject _prefab = null; + + private void Awake() + { + _prefab.Populate(count: 50); + + // If destroy active is true then even active instances will be destroyed + _prefab.Clear(destroyActive: true) + } +} +``` + ### How to get object from pool: ```csharp using ToolBox.Pools; @@ -91,7 +110,7 @@ public class Health : MonoBehaviour, IPoolable private float _health = 0f; - // Awake will be called on first _prefab.Get() + // Awake will be called on first _prefab.Reuse() private void Awake() { OnReuse(); diff --git a/Runtime/Pool.cs b/Runtime/Pool.cs index c20c3b2..058ad90 100644 --- a/Runtime/Pool.cs +++ b/Runtime/Pool.cs @@ -5,53 +5,64 @@ namespace ToolBox.Pools { internal sealed class Pool { - private readonly Poolable _prefab = null; - private readonly Stack _instances = null; - private readonly Quaternion _rotation = default; - private readonly Vector3 _scale = default; + private GameObject _source; + private Poolable _prototype; + private Stack _instances; + private List _allInstances; + private readonly Quaternion _rotation; + private readonly Vector3 _scale; + private readonly bool _prototypeIsNotSource; private static readonly Dictionary _prefabLookup = new Dictionary(64); private static readonly Dictionary _instanceLookup = new Dictionary(512); - private const int INITIAL_SIZE = 128; + private const int InitialSize = 128; public Pool(GameObject prefab) { - _prefab = prefab.GetComponent(); + _source = prefab; + _prototype = prefab.GetComponent(); - if (_prefab == null) + if (_prototype == null) { - _prefab = Object.Instantiate(prefab).AddComponent(); - Object.DontDestroyOnLoad(_prefab); - _prefab.gameObject.SetActive(false); + _prototype = Object.Instantiate(prefab).AddComponent(); + Object.DontDestroyOnLoad(_prototype); + _prototype.gameObject.SetActive(false); + _prototypeIsNotSource = true; } - _instances = new Stack(INITIAL_SIZE); - _prefabLookup.Add(prefab, this); + _instances = new Stack(InitialSize); + _allInstances = new List(InitialSize); + _prefabLookup.Add(_source, this); var transform = prefab.transform; _rotation = transform.rotation; _scale = transform.localScale; } - public static Pool GetPrefabPool(GameObject prefab) + public static Pool GetPoolByPrefab(GameObject prefab, bool create = true) { - bool hasPool = _prefabLookup.TryGetValue(prefab, out var pool); + var hasPool = _prefabLookup.TryGetValue(prefab, out var pool); - if (!hasPool) + if (!hasPool && create) pool = new Pool(prefab); return pool; } - public static bool TryGetInstancePool(GameObject instance, out Pool pool) + public static bool GetPoolByInstance(GameObject instance, out Pool pool) { return _instanceLookup.TryGetValue(instance, out pool); } + public static void Remove(GameObject instance) + { + _instanceLookup.Remove(instance); + } + public void Populate(int count) { - for (int i = 0; i < count; i++) + for (var i = 0; i < count; i++) { var instance = CreateInstance(); instance.gameObject.SetActive(false); @@ -59,6 +70,32 @@ public void Populate(int count) } } + public void Clear(bool destroyActive) + { + _prefabLookup.Remove(_source); + + foreach (var instance in _allInstances) + { + if (instance == null) + continue; + + _instanceLookup.Remove(instance); + + if (!destroyActive && instance.activeInHierarchy) + continue; + + Object.Destroy(instance); + } + + if (_prototypeIsNotSource) + Object.Destroy(_prototype.gameObject); + + _source = null; + _prototype = null; + _instances = null; + _allInstances = null; + } + public GameObject Reuse() { var instance = GetInstance(); @@ -121,7 +158,7 @@ public void Release(GameObject instance) private Poolable GetInstance() { - int count = _instances.Count; + var count = _instances.Count; if (count != 0) { @@ -168,9 +205,11 @@ private Poolable GetInstance() private Poolable CreateInstance() { - var instance = Object.Instantiate(_prefab); + var instance = Object.Instantiate(_prototype); var instanceGameObject = instance.gameObject; + _instanceLookup.Add(instanceGameObject, this); + _allInstances.Add(instanceGameObject); return instance; } diff --git a/Runtime/PoolHelper.cs b/Runtime/PoolHelper.cs index 923d910..789d394 100644 --- a/Runtime/PoolHelper.cs +++ b/Runtime/PoolHelper.cs @@ -6,32 +6,37 @@ public static class PoolHelper { public static void Populate(this GameObject prefab, int count) { - Pool.GetPrefabPool(prefab).Populate(count); + Pool.GetPoolByPrefab(prefab).Populate(count); + } + + public static void Clear(this GameObject prefab, bool destroyActive) + { + Pool.GetPoolByPrefab(prefab, false)?.Clear(destroyActive); } public static GameObject Reuse(this GameObject prefab) { - return Pool.GetPrefabPool(prefab).Reuse(); + return Pool.GetPoolByPrefab(prefab).Reuse(); } public static GameObject Reuse(this GameObject prefab, Transform parent) { - return Pool.GetPrefabPool(prefab).Reuse(parent); + return Pool.GetPoolByPrefab(prefab).Reuse(parent); } public static GameObject Reuse(this GameObject prefab, Transform parent, bool worldPositionStays) { - return Pool.GetPrefabPool(prefab).Reuse(parent, worldPositionStays); + return Pool.GetPoolByPrefab(prefab).Reuse(parent, worldPositionStays); } public static GameObject Reuse(this GameObject prefab, Vector3 position, Quaternion rotation) { - return Pool.GetPrefabPool(prefab).Reuse(position, rotation); + return Pool.GetPoolByPrefab(prefab).Reuse(position, rotation); } public static GameObject Reuse(this GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) { - return Pool.GetPrefabPool(prefab).Reuse(position, rotation, parent); + return Pool.GetPoolByPrefab(prefab).Reuse(position, rotation, parent); } public static T Reuse(this GameObject prefab) where T : Component @@ -61,7 +66,7 @@ public static T Reuse(this GameObject prefab, Vector3 position, Quaternion ro public static void Release(this GameObject instance) { - bool isPooled = Pool.TryGetInstancePool(instance, out var pool); + var isPooled = Pool.GetPoolByInstance(instance, out var pool); if (isPooled) { diff --git a/Runtime/PoolInstaller.cs b/Runtime/PoolInstaller.cs index 2911c23..6c876e9 100644 --- a/Runtime/PoolInstaller.cs +++ b/Runtime/PoolInstaller.cs @@ -5,11 +5,11 @@ namespace ToolBox.Pools [DefaultExecutionOrder(-9999), DisallowMultipleComponent] internal sealed class PoolInstaller : MonoBehaviour { - [SerializeField] private PoolContainer[] _pools = null; + [SerializeField] private PoolContainer[] _pools; private void Awake() { - for (int i = 0; i < _pools.Length; i++) + for (var i = 0; i < _pools.Length; i++) _pools[i].Populate(); } @@ -19,8 +19,10 @@ private struct PoolContainer [SerializeField] private GameObject _prefab; [SerializeField, Min(1)] private int _startCount; - public void Populate() => + public void Populate() + { _prefab.Populate(_startCount); + } } } } diff --git a/Runtime/Poolable.cs b/Runtime/Poolable.cs index a519c62..2b4a22a 100644 --- a/Runtime/Poolable.cs +++ b/Runtime/Poolable.cs @@ -7,7 +7,7 @@ namespace ToolBox.Pools internal sealed class Poolable : MonoBehaviour { private IPoolable[] _poolables = Array.Empty(); - private bool _isInitialized = false; + private bool _isInitialized; private void Awake() { @@ -15,18 +15,23 @@ private void Awake() _isInitialized = true; } + private void OnDestroy() + { + Pool.Remove(gameObject); + } + public void OnReuse() { if (!_isInitialized) return; - for (int i = 0; i < _poolables.Length; i++) + for (var i = 0; i < _poolables.Length; i++) _poolables[i].OnReuse(); } public void OnRelease() { - for (int i = 0; i < _poolables.Length; i++) + for (var i = 0; i < _poolables.Length; i++) _poolables[i].OnRelease(); } }