using System.Collections.Generic; using UnityEngine; namespace WK { public class UnityObjectPool : System.IDisposable where T : UnityEngine.Object { public interface IPooledInstanceInitializer { void InitPooledInstance(T instance); void DestroyPooledInstance(T instance); } private T _sourceObject; private Transform _poolParent; private int _initialSize; private IPooledInstanceInitializer _initalizer; private List _usedInstances; private Stack _freeInstances; public UnityObjectPool(T sourceObject, Transform poolParent, IPooledInstanceInitializer initializer, int initialSize = 0) { Debug.Assert(sourceObject != null); Debug.Assert(poolParent != null); Debug.Assert(initializer != null); Debug.Assert(initialSize >= 0); _sourceObject = sourceObject; _poolParent = poolParent; _initalizer = initializer; _initialSize = initialSize; _usedInstances = new List(); _freeInstances = new Stack(initialSize); for (int i = 0; i < _initialSize; ++i) { T instance = CreateInstance(); _freeInstances.Push(instance); } } public T Acquire() { T instance; if (_freeInstances.Count > 0) { instance = _freeInstances.Pop(); } else { instance = CreateInstance(); } _usedInstances.Add(instance); return instance; } public void Release(T instance) { Debug.Assert(instance != null); Debug.Assert(!_freeInstances.Contains(instance)); bool res = _usedInstances.Remove(instance); Debug.Assert(res); _freeInstances.Push(instance); } private T CreateInstance() { T instance = Object.Instantiate(_sourceObject, _poolParent); // Let the client code initialize the object however it want to _initalizer.InitPooledInstance(instance); return instance; } #region IDisposable Support private bool _disposed = false; protected virtual void Dispose(bool disposing) { if (Application.isPlaying) { Debug.Assert(!_disposed, "Trying to dispose an pool twice!"); Debug.Assert(_usedInstances.Count == 0, "Disposing pool before releasing all objects!"); } foreach (T instance in _freeInstances) { // We can't destroy a UnityEngine.Object without knowing if it's a Gameobject or Component. Calling Destroy(instance) directly here will give not destroy the Gameobject if it's a component _initalizer.DestroyPooledInstance(instance); } _freeInstances.Clear(); _sourceObject = null; _poolParent = null; _initalizer = null; _disposed = true; } public void Dispose() { Dispose(true); System.GC.SuppressFinalize(this); } #if ENABLE_ASSERTS ~UnityObjectPool() { MainThreadDispatcher.BeginInvoke(() => // Can't call Application.isPlaying in Unity's finalizer thread { if (Application.isPlaying) { DebugTools.Assert(false, string.Format("Failed to dispose a pool of type {0}!", typeof(T))); } }); } #endif #endregion } }