2026-04-22 13:04:31 +08:00
|
|
|
using System;
|
2025-10-11 15:18:09 +08:00
|
|
|
using System.Collections.Generic;
|
2026-03-26 16:14:05 +08:00
|
|
|
using UnityEngine;
|
2025-10-11 15:18:09 +08:00
|
|
|
|
|
|
|
|
namespace AlicizaX.ObjectPool
|
|
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
|
2025-10-11 15:18:09 +08:00
|
|
|
[UnityEngine.Scripting.Preserve]
|
2026-04-20 13:46:44 +08:00
|
|
|
internal sealed partial class ObjectPoolService : ServiceBase, IObjectPoolService, IServiceTickable
|
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;
|
2026-03-26 17:53:07 +08:00
|
|
|
private readonly List<ObjectPoolBase> m_ObjectPoolList;
|
2025-10-11 15:18:09 +08:00
|
|
|
private readonly List<ObjectPoolBase> m_CachedAllObjectPools;
|
|
|
|
|
private readonly Comparison<ObjectPoolBase> m_ObjectPoolComparer;
|
|
|
|
|
|
2026-03-26 16:14:05 +08:00
|
|
|
public ObjectPoolService()
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
|
|
|
|
m_ObjectPools = new Dictionary<TypeNamePair, ObjectPoolBase>();
|
2026-03-26 17:53:07 +08:00
|
|
|
m_ObjectPoolList = new List<ObjectPoolBase>();
|
2025-10-11 15:18:09 +08:00
|
|
|
m_CachedAllObjectPools = new List<ObjectPoolBase>();
|
|
|
|
|
m_ObjectPoolComparer = ObjectPoolComparer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Priority => 1;
|
2026-04-22 13:04:31 +08:00
|
|
|
public int Count => m_ObjectPools.Count;
|
2025-10-11 15:18:09 +08:00
|
|
|
|
2026-03-26 16:14:05 +08:00
|
|
|
void IServiceTickable.Tick(float deltaTime)
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-03-26 17:53:07 +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
|
|
|
}
|
|
|
|
|
|
2026-03-26 16:14:05 +08:00
|
|
|
protected override void OnInitialize() { }
|
|
|
|
|
|
|
|
|
|
protected override void OnDestroyService()
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
for (int i = m_ObjectPoolList.Count - 1; i >= 0; i--)
|
2026-03-26 17:53:07 +08:00
|
|
|
m_ObjectPoolList[i].Shutdown();
|
2025-10-11 15:18:09 +08:00
|
|
|
m_ObjectPools.Clear();
|
2026-03-26 17:53:07 +08:00
|
|
|
m_ObjectPoolList.Clear();
|
2025-10-11 15:18:09 +08:00
|
|
|
m_CachedAllObjectPools.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
// ========== Has ==========
|
2025-10-11 15:18:09 +08:00
|
|
|
|
2026-04-22 13:04:31 +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
|
2026-04-22 13:04:31 +08:00
|
|
|
=> m_ObjectPools.ContainsKey(new TypeNamePair(typeof(T), name));
|
|
|
|
|
|
|
|
|
|
public bool HasObjectPool(Type objectType)
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +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)
|
|
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
ValidateObjectType(objectType);
|
|
|
|
|
return m_ObjectPools.ContainsKey(new TypeNamePair(objectType, name));
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
// ========== Get ==========
|
2025-10-11 15:18:09 +08:00
|
|
|
|
|
|
|
|
public IObjectPool<T> GetObjectPool<T>() where T : ObjectBase
|
2026-04-22 13:04:31 +08:00
|
|
|
=> (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
|
2026-04-22 13:04:31 +08:00
|
|
|
=> (IObjectPool<T>)InternalGet(new TypeNamePair(typeof(T), name));
|
|
|
|
|
|
|
|
|
|
public ObjectPoolBase GetObjectPool(Type objectType)
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
ValidateObjectType(objectType);
|
|
|
|
|
return InternalGet(new TypeNamePair(objectType));
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ObjectPoolBase GetObjectPool(Type objectType, string name)
|
|
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
ValidateObjectType(objectType);
|
|
|
|
|
return InternalGet(new TypeNamePair(objectType, name));
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
// ========== GetAll ==========
|
2025-10-11 15:18:09 +08:00
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
public ObjectPoolBase[] GetAllObjectPools() => m_ObjectPoolList.ToArray();
|
2025-10-11 15:18:09 +08:00
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
public ObjectPoolBase[] GetAllObjectPools(bool sort)
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
if (!sort) return m_ObjectPoolList.ToArray();
|
|
|
|
|
var results = new List<ObjectPoolBase>(m_ObjectPoolList);
|
|
|
|
|
results.Sort(m_ObjectPoolComparer);
|
2025-10-11 15:18:09 +08:00
|
|
|
return results.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
public void GetAllObjectPools(List<ObjectPoolBase> results) => GetAllObjectPools(false, results);
|
2025-10-11 15:18:09 +08:00
|
|
|
|
|
|
|
|
public void GetAllObjectPools(bool sort, List<ObjectPoolBase> results)
|
|
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
if (results == null) throw new GameFrameworkException("Results is invalid.");
|
2025-10-11 15:18:09 +08:00
|
|
|
results.Clear();
|
2026-03-26 17:53:07 +08:00
|
|
|
results.AddRange(m_ObjectPoolList);
|
2026-04-22 13:04:31 +08:00
|
|
|
if (sort) results.Sort(m_ObjectPoolComparer);
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
// ========== Create (single entry point) ==========
|
|
|
|
|
|
2026-03-31 17:25:20 +08:00
|
|
|
public IObjectPool<T> CreatePool<T>(ObjectPoolCreateOptions options = default) where T : ObjectBase
|
|
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
var key = new TypeNamePair(typeof(T), options.Name);
|
|
|
|
|
if (m_ObjectPools.ContainsKey(key))
|
|
|
|
|
throw new GameFrameworkException($"Already exist object pool '{key}'.");
|
2026-03-31 17:25:20 +08:00
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
var pool = new ObjectPool<T>(
|
|
|
|
|
options.Name ?? string.Empty,
|
2026-03-31 17:25:20 +08:00
|
|
|
options.AllowMultiSpawn,
|
2026-04-22 13:04:31 +08:00
|
|
|
options.AutoReleaseInterval ?? DefaultExpireTime,
|
|
|
|
|
options.Capacity ?? DefaultCapacity,
|
|
|
|
|
options.ExpireTime ?? DefaultExpireTime,
|
2026-03-31 17:25:20 +08:00
|
|
|
options.Priority);
|
2025-10-11 15:18:09 +08:00
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
m_ObjectPools.Add(key, pool);
|
|
|
|
|
m_ObjectPoolList.Add(pool);
|
|
|
|
|
return pool;
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
public ObjectPoolBase CreatePool(Type objectType, ObjectPoolCreateOptions options = default)
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +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
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
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);
|
2025-10-11 15:18:09 +08:00
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
m_ObjectPools.Add(key, pool);
|
|
|
|
|
m_ObjectPoolList.Add(pool);
|
|
|
|
|
return pool;
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
// ========== Destroy ==========
|
2025-10-11 15:18:09 +08:00
|
|
|
|
|
|
|
|
public bool DestroyObjectPool<T>() where T : ObjectBase
|
2026-04-22 13:04:31 +08:00
|
|
|
=> InternalDestroy(new TypeNamePair(typeof(T)));
|
2025-10-11 15:18:09 +08:00
|
|
|
|
|
|
|
|
public bool DestroyObjectPool<T>(string name) where T : ObjectBase
|
2026-04-22 13:04:31 +08:00
|
|
|
=> InternalDestroy(new TypeNamePair(typeof(T), name));
|
|
|
|
|
|
|
|
|
|
public bool DestroyObjectPool(Type objectType)
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
ValidateObjectType(objectType);
|
|
|
|
|
return InternalDestroy(new TypeNamePair(objectType));
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool DestroyObjectPool(Type objectType, string name)
|
|
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
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
|
|
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
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)
|
|
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
// ========== Release ==========
|
|
|
|
|
|
2025-10-11 15:18:09 +08:00
|
|
|
public void Release()
|
|
|
|
|
{
|
|
|
|
|
GetAllObjectPools(true, m_CachedAllObjectPools);
|
2026-04-22 13:04:31 +08:00
|
|
|
for (int i = 0; i < m_CachedAllObjectPools.Count; i++)
|
|
|
|
|
m_CachedAllObjectPools[i].Release();
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ReleaseAllUnused()
|
|
|
|
|
{
|
|
|
|
|
GetAllObjectPools(true, m_CachedAllObjectPools);
|
2026-04-22 13:04:31 +08:00
|
|
|
for (int i = 0; i < m_CachedAllObjectPools.Count; i++)
|
|
|
|
|
m_CachedAllObjectPools[i].ReleaseAllUnused();
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
// ========== Low memory ==========
|
2025-10-11 15:18:09 +08:00
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
public void OnLowMemory()
|
2026-03-31 17:25:20 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
for (int i = 0; i < m_ObjectPoolList.Count; i++)
|
|
|
|
|
m_ObjectPoolList[i].OnLowMemory();
|
2026-03-31 17:25:20 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
// ========== Internal ==========
|
2026-03-31 17:25:20 +08:00
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
private ObjectPoolBase InternalGet(TypeNamePair key)
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
m_ObjectPools.TryGetValue(key, out var pool);
|
|
|
|
|
return pool;
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
private bool InternalDestroy(TypeNamePair key)
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
if (m_ObjectPools.TryGetValue(key, out var pool))
|
2025-10-11 15:18:09 +08:00
|
|
|
{
|
2026-04-22 13:04:31 +08:00
|
|
|
pool.Shutdown();
|
|
|
|
|
m_ObjectPoolList.Remove(pool);
|
|
|
|
|
return m_ObjectPools.Remove(key);
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
2026-04-22 13:04:31 +08:00
|
|
|
return false;
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 13:04:31 +08:00
|
|
|
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))
|
2026-04-22 13:04:31 +08:00
|
|
|
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)
|
2026-04-22 13:04:31 +08:00
|
|
|
=> a.Priority.CompareTo(b.Priority);
|
2025-10-11 15:18:09 +08:00
|
|
|
}
|
|
|
|
|
}
|