using System; using System.Collections.Generic; using UnityEngine; namespace AlicizaX.ObjectPool { [UnityEngine.Scripting.Preserve] internal sealed partial class ObjectPoolService : ServiceBase, IObjectPoolService, IServiceTickable { private const int DefaultCapacity = int.MaxValue; private const float DefaultExpireTime = float.MaxValue; private readonly Dictionary m_ObjectPools; private readonly List m_ObjectPoolList; private readonly List m_CachedAllObjectPools; private readonly Comparison m_ObjectPoolComparer; public ObjectPoolService() { m_ObjectPools = new Dictionary(); m_ObjectPoolList = new List(); m_CachedAllObjectPools = new List(); m_ObjectPoolComparer = ObjectPoolComparer; } public int Priority => 1; public int Count => m_ObjectPools.Count; void IServiceTickable.Tick(float deltaTime) { for (int i = 0; i < m_ObjectPoolList.Count; i++) m_ObjectPoolList[i].Update(deltaTime, Time.unscaledDeltaTime); } protected override void OnInitialize() { } protected override void OnDestroyService() { for (int i = m_ObjectPoolList.Count - 1; i >= 0; i--) m_ObjectPoolList[i].Shutdown(); m_ObjectPools.Clear(); m_ObjectPoolList.Clear(); m_CachedAllObjectPools.Clear(); } // ========== Has ========== public bool HasObjectPool() where T : ObjectBase => m_ObjectPools.ContainsKey(new TypeNamePair(typeof(T))); public bool HasObjectPool(string name) where T : ObjectBase => m_ObjectPools.ContainsKey(new TypeNamePair(typeof(T), name)); public bool HasObjectPool(Type objectType) { ValidateObjectType(objectType); return m_ObjectPools.ContainsKey(new TypeNamePair(objectType)); } public bool HasObjectPool(Type objectType, string name) { ValidateObjectType(objectType); return m_ObjectPools.ContainsKey(new TypeNamePair(objectType, 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)); public ObjectPoolBase GetObjectPool(Type objectType) { ValidateObjectType(objectType); return InternalGet(new TypeNamePair(objectType)); } public ObjectPoolBase GetObjectPool(Type objectType, string name) { ValidateObjectType(objectType); return InternalGet(new TypeNamePair(objectType, name)); } // ========== GetAll ========== public ObjectPoolBase[] GetAllObjectPools() => m_ObjectPoolList.ToArray(); public ObjectPoolBase[] GetAllObjectPools(bool sort) { if (!sort) return m_ObjectPoolList.ToArray(); var results = new List(m_ObjectPoolList); results.Sort(m_ObjectPoolComparer); return results.ToArray(); } public void GetAllObjectPools(List results) => GetAllObjectPools(false, results); public void GetAllObjectPools(bool sort, List results) { if (results == null) throw new GameFrameworkException("Results is invalid."); results.Clear(); results.AddRange(m_ObjectPoolList); if (sort) results.Sort(m_ObjectPoolComparer); } // ========== Create (single entry point) ========== public IObjectPool CreatePool(ObjectPoolCreateOptions options = default) where T : ObjectBase { var key = new TypeNamePair(typeof(T), options.Name); if (m_ObjectPools.ContainsKey(key)) throw new GameFrameworkException($"Already exist object pool '{key}'."); var pool = new ObjectPool( options.Name ?? string.Empty, options.AllowMultiSpawn, options.AutoReleaseInterval ?? DefaultExpireTime, options.Capacity ?? DefaultCapacity, options.ExpireTime ?? DefaultExpireTime, options.Priority); m_ObjectPools.Add(key, pool); m_ObjectPoolList.Add(pool); return pool; } public ObjectPoolBase CreatePool(Type objectType, ObjectPoolCreateOptions options = default) { ValidateObjectType(objectType); var key = new TypeNamePair(objectType, options.Name); if (m_ObjectPools.ContainsKey(key)) throw new GameFrameworkException($"Already exist object pool '{key}'."); var poolType = typeof(ObjectPool<>).MakeGenericType(objectType); var pool = (ObjectPoolBase)Activator.CreateInstance(poolType, options.Name ?? string.Empty, options.AllowMultiSpawn, options.AutoReleaseInterval ?? DefaultExpireTime, options.Capacity ?? DefaultCapacity, options.ExpireTime ?? DefaultExpireTime, options.Priority); m_ObjectPools.Add(key, pool); m_ObjectPoolList.Add(pool); 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(Type objectType) { ValidateObjectType(objectType); return InternalDestroy(new TypeNamePair(objectType)); } public bool DestroyObjectPool(Type objectType, string name) { ValidateObjectType(objectType); return InternalDestroy(new TypeNamePair(objectType, name)); } public bool DestroyObjectPool(IObjectPool objectPool) where T : ObjectBase { if (objectPool == null) throw new GameFrameworkException("Object pool is invalid."); return InternalDestroy(new TypeNamePair(typeof(T), objectPool.Name)); } public bool DestroyObjectPool(ObjectPoolBase objectPool) { if (objectPool == null) throw new GameFrameworkException("Object pool is invalid."); return InternalDestroy(new TypeNamePair(objectPool.ObjectType, objectPool.Name)); } // ========== Release ========== public void Release() { GetAllObjectPools(true, m_CachedAllObjectPools); for (int i = 0; i < m_CachedAllObjectPools.Count; i++) m_CachedAllObjectPools[i].Release(); } public void ReleaseAllUnused() { GetAllObjectPools(true, m_CachedAllObjectPools); for (int i = 0; i < m_CachedAllObjectPools.Count; i++) m_CachedAllObjectPools[i].ReleaseAllUnused(); } // ========== Low memory ========== public void OnLowMemory() { for (int i = 0; i < m_ObjectPoolList.Count; i++) m_ObjectPoolList[i].OnLowMemory(); } // ========== Internal ========== private ObjectPoolBase InternalGet(TypeNamePair key) { m_ObjectPools.TryGetValue(key, out var pool); return pool; } private bool InternalDestroy(TypeNamePair key) { if (m_ObjectPools.TryGetValue(key, out var pool)) { pool.Shutdown(); m_ObjectPoolList.Remove(pool); return m_ObjectPools.Remove(key); } return false; } private static void ValidateObjectType(Type objectType) { if (objectType == null) throw new GameFrameworkException("Object type is invalid."); if (!typeof(ObjectBase).IsAssignableFrom(objectType)) throw new GameFrameworkException($"Object type '{objectType.FullName}' is invalid."); } private static int ObjectPoolComparer(ObjectPoolBase a, ObjectPoolBase b) => a.Priority.CompareTo(b.Priority); } }