using System; using UnityEngine; namespace AlicizaX.ObjectPool { [UnityEngine.Scripting.Preserve] internal sealed partial class ObjectPoolService : ServiceBase, IObjectPoolService, IServiceTickable { private const float DefaultAutoReleaseInterval = float.MaxValue; private const int DefaultCapacity = int.MaxValue; private const float DefaultExpireTime = float.MaxValue; private const int InitPoolArrayCapacity = 8; private TypeNamePairOpenHashMap m_PoolMap; private ReferenceOpenHashMap m_PoolRefMap; private ObjectPoolBase[] m_Pools; private int m_PoolCount; private ObjectPoolBase[] m_CachedSortedPools; private int m_CachedSortedCount; public ObjectPoolService() { m_PoolMap = new TypeNamePairOpenHashMap(InitPoolArrayCapacity); m_PoolRefMap = new ReferenceOpenHashMap(InitPoolArrayCapacity); m_Pools = new ObjectPoolBase[InitPoolArrayCapacity]; m_PoolCount = 0; m_CachedSortedPools = Array.Empty(); m_CachedSortedCount = 0; } public int Priority => 1; public int Count => m_PoolMap.Count; void IServiceTickable.Tick(float deltaTime) { float unscaled = Time.unscaledDeltaTime; for (int i = 0; i < m_PoolCount; i++) { var pool = m_Pools[i]; if (pool.IsActive) pool.Update(deltaTime, unscaled); } } protected override void OnInitialize() { } protected override void OnDestroyService() { for (int i = m_PoolCount - 1; i >= 0; i--) m_Pools[i].Shutdown(); m_PoolMap.Clear(); m_PoolRefMap.Clear(); Array.Clear(m_Pools, 0, m_PoolCount); m_PoolCount = 0; m_CachedSortedCount = 0; } // ========== Has ========== public bool HasObjectPool() where T : ObjectBase => m_PoolMap.ContainsKey(new TypeNamePair(typeof(T))); public bool HasObjectPool(string name) where T : ObjectBase => m_PoolMap.ContainsKey(new TypeNamePair(typeof(T), name)); // ========== Get ========== public IObjectPool GetObjectPool() where T : ObjectBase => (IObjectPool)InternalGet(new TypeNamePair(typeof(T))); public IObjectPool GetObjectPool(string name) where T : ObjectBase => (IObjectPool)InternalGet(new TypeNamePair(typeof(T), name)); // ========== GetAll ========== internal int GetAllObjectPools(bool sort, ObjectPoolBase[] results) { if (results == null) { #if UNITY_EDITOR UnityEngine.Debug.LogError("Results is invalid."); #endif return 0; } if (sort) { CacheSortedObjectPools(); int copyCount = results.Length < m_CachedSortedCount ? results.Length : m_CachedSortedCount; Array.Copy(m_CachedSortedPools, 0, results, 0, copyCount); return m_CachedSortedCount; } int count = m_PoolCount; int copy = results.Length < count ? results.Length : count; Array.Copy(m_Pools, 0, results, 0, copy); return count; } // ========== Create (single entry point) ========== public IObjectPool CreatePool(ObjectPoolCreateOptions options = default) where T : ObjectBase { var key = new TypeNamePair(typeof(T), options.Name); if (m_PoolMap.ContainsKey(key)) { #if UNITY_EDITOR UnityEngine.Debug.LogError($"Already exist object pool '{key}'."); #endif return null; } var pool = new ObjectPool( options.Name ?? string.Empty, options.AllowMultiSpawn, options.AutoReleaseInterval ?? DefaultAutoReleaseInterval, options.Capacity ?? DefaultCapacity, options.ExpireTime ?? DefaultExpireTime, options.Priority); int idx = m_PoolCount; if (idx >= m_Pools.Length) { var newArr = new ObjectPoolBase[m_Pools.Length * 2]; Array.Copy(m_Pools, 0, newArr, 0, m_PoolCount); m_Pools = newArr; } m_Pools[idx] = pool; m_PoolCount++; m_PoolMap.AddOrUpdate(key, idx); m_PoolRefMap.AddOrUpdate(pool, idx); return pool; } // ========== Destroy ========== public bool DestroyObjectPool() where T : ObjectBase => InternalDestroy(new TypeNamePair(typeof(T))); public bool DestroyObjectPool(string name) where T : ObjectBase => InternalDestroy(new TypeNamePair(typeof(T), name)); public bool DestroyObjectPool(IObjectPool objectPool) where T : ObjectBase { if (objectPool == null) { #if UNITY_EDITOR UnityEngine.Debug.LogError("Object pool is invalid."); #endif return false; } return InternalDestroy(new TypeNamePair(typeof(T), objectPool.Name)); } // ========== Release ========== public void Release() { CacheSortedObjectPools(); for (int i = 0; i < m_CachedSortedCount; i++) m_CachedSortedPools[i].Release(); } public void ReleaseAllUnused() { CacheSortedObjectPools(); for (int i = 0; i < m_CachedSortedCount; i++) m_CachedSortedPools[i].ReleaseAllUnused(); } // ========== Low memory ========== public void OnLowMemory() { for (int i = 0; i < m_PoolCount; i++) m_Pools[i].OnLowMemory(); } // ========== Internal ========== private ObjectPoolBase InternalGet(TypeNamePair key) { if (m_PoolMap.TryGetValue(key, out int idx)) return m_Pools[idx]; return null; } private bool InternalDestroy(TypeNamePair key) { if (!m_PoolMap.TryGetValue(key, out int idx)) return false; var pool = m_Pools[idx]; pool.Shutdown(); int lastIndex = m_PoolCount - 1; if (idx < lastIndex) { var lastPool = m_Pools[lastIndex]; m_Pools[idx] = lastPool; m_PoolRefMap.AddOrUpdate(lastPool, idx); var lastKey = new TypeNamePair(lastPool.ObjectType, lastPool.Name); m_PoolMap.AddOrUpdate(lastKey, idx); } m_Pools[lastIndex] = null; m_PoolCount--; m_PoolMap.Remove(key); m_PoolRefMap.Remove(pool); return true; } private void CacheSortedObjectPools() { int count = m_PoolCount; if (m_CachedSortedPools.Length < count) m_CachedSortedPools = new ObjectPoolBase[Math.Max(count, 8)]; Array.Copy(m_Pools, 0, m_CachedSortedPools, 0, count); m_CachedSortedCount = count; for (int i = 1; i < count; i++) { var key = m_CachedSortedPools[i]; int keyPriority = key.Priority; int j = i - 1; while (j >= 0 && m_CachedSortedPools[j].Priority > keyPriority) { m_CachedSortedPools[j + 1] = m_CachedSortedPools[j]; j--; } m_CachedSortedPools[j + 1] = key; } } } }