Files
test/Assets/PerfectWorld/Scripts/PoolingManager/Prefabs/PrefabPoolManager.cs
T
Tungdv df23868b40 feat: Add prefabs pooling manager.
fix: update spawn NPC.
2026-05-06 15:33:28 +07:00

209 lines
6.2 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Pool;
/// <summary>
/// Centralized prefab pooling manager for reusable GameObject instances.
/// </summary>
///
namespace BrewMonster.Scripts
{
public class PrefabPoolManager : MonoBehaviour
{
private static PrefabPoolManager instance;
private readonly Dictionary<GameObject, PoolRecord> poolsByPrefab = new();
private readonly Dictionary<GameObject, PoolRecord> allInstances = new();
private readonly Dictionary<GameObject, PoolRecord> activeInstances = new();
public static PrefabPoolManager Instance
{
get
{
if (instance == null)
{
instance = FindAnyObjectByType<PrefabPoolManager>();
if (instance == null)
{
var managerObject = new GameObject(nameof(PrefabPoolManager));
instance = managerObject.AddComponent<PrefabPoolManager>();
}
}
return instance;
}
}
public void InitPool(GameObject prefab, int defaultCapacity = 10, int maxSize = 50)
{
if (prefab == null)
{
Debug.LogError("[PrefabPoolManager] Cannot initialize a pool with a null prefab.");
return;
}
if (poolsByPrefab.ContainsKey(prefab))
return;
defaultCapacity = Mathf.Max(0, defaultCapacity);
maxSize = Mathf.Max(1, maxSize);
if (defaultCapacity > maxSize)
defaultCapacity = maxSize;
var record = new PoolRecord
{
Prefab = prefab,
Container = CreatePoolContainer(prefab)
};
record.Pool = new ObjectPool<GameObject>(
() => CreateInstance(record),
obj => OnGetFromPool(record, obj),
obj => OnReleaseToPool(record, obj),
OnDestroyPooledObject,
collectionCheck: Application.isEditor,
defaultCapacity: defaultCapacity,
maxSize: maxSize);
poolsByPrefab.Add(prefab, record);
Prewarm(record, defaultCapacity);
}
public GameObject Spawn(GameObject prefab, Vector3 position, Quaternion rotation, Transform parent = null)
{
if (prefab == null)
{
Debug.LogError("[PrefabPoolManager] Cannot spawn a null prefab.");
return null;
}
if (!poolsByPrefab.TryGetValue(prefab, out PoolRecord record))
{
InitPool(prefab);
record = poolsByPrefab[prefab];
}
GameObject instanceObject = record.Pool.Get();
instanceObject.transform.SetPositionAndRotation(position, rotation);
if (parent != null)
{
instanceObject.transform.SetParent(parent);
}
return instanceObject;
}
public void Despawn(GameObject obj)
{
if (obj == null)
return;
if (!activeInstances.TryGetValue(obj, out PoolRecord record))
{
if (allInstances.ContainsKey(obj))
{
Debug.LogWarning($"[PrefabPoolManager] Object '{obj.name}' has already been returned to the pool.");
return;
}
Debug.LogWarning($"[PrefabPoolManager] Object '{obj.name}' was not spawned by PrefabPoolManager. Destroying it instead.");
Destroy(obj);
return;
}
record.Pool.Release(obj);
}
private void Awake()
{
if (instance != null && instance != this)
{
Destroy(gameObject);
return;
}
instance = this;
//DontDestroyOnLoad(gameObject);
}
private void OnDestroy()
{
if (instance != this)
return;
foreach (PoolRecord record in poolsByPrefab.Values)
{
record.Pool.Clear();
}
poolsByPrefab.Clear();
allInstances.Clear();
activeInstances.Clear();
instance = null;
}
private Transform CreatePoolContainer(GameObject prefab)
{
var container = new GameObject($"{prefab.name}_Pool").transform;
container.SetParent(transform);
return container;
}
private GameObject CreateInstance(PoolRecord record)
{
GameObject instanceObject = Instantiate(record.Prefab, record.Container);
instanceObject.name = record.Prefab.name;
instanceObject.SetActive(false);
allInstances[instanceObject] = record;
return instanceObject;
}
private void OnGetFromPool(PoolRecord record, GameObject obj)
{
activeInstances[obj] = record;
obj.transform.SetParent(null);
obj.SetActive(true);
}
private void OnReleaseToPool(PoolRecord record, GameObject obj)
{
activeInstances.Remove(obj);
obj.SetActive(false);
obj.transform.SetParent(record.Container);
}
private void OnDestroyPooledObject(GameObject obj)
{
activeInstances.Remove(obj);
allInstances.Remove(obj);
if (obj != null)
Destroy(obj);
}
private static void Prewarm(PoolRecord record, int count)
{
if (count <= 0)
return;
var warmedObjects = new List<GameObject>(count);
for (int i = 0; i < count; i++)
{
warmedObjects.Add(record.Pool.Get());
}
for (int i = 0; i < warmedObjects.Count; i++)
{
record.Pool.Release(warmedObjects[i]);
}
}
internal sealed class PoolRecord
{
public GameObject Prefab;
public Transform Container;
public ObjectPool<GameObject> Pool;
}
}
}