com.alicizax.unity.framework/Runtime/ObjectPool/ObjectPoolService.cs

265 lines
9.6 KiB
C#
Raw Normal View History

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