df23868b40
fix: update spawn NPC.
209 lines
6.2 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|