Files
5.24BuYu/Assets/Framework/Pool/EffectPool.cs
T
2026-07-13 04:48:18 +08:00

172 lines
4.8 KiB
C#

using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 特效专用对象池 — 按 prefab.GetInstanceID() 分桶
/// 粒子播放完自动回池(通过 EffectAutoReturn 组件检测)
/// 用法:
/// var go = EffectPool.Instance.Get(prefab, pos, rot, parent);
/// // 无需手动回收,EffectAutoReturn 会在粒子播完后自动回池
/// EffectPool.Instance.ClearAll(); // 切场景时彻底清理
/// </summary>
public class EffectPool : Singleton<EffectPool>
{
// prefab.GetInstanceID() → 该 prefab 的池
private Dictionary<int, BulletPoolData> dic = new Dictionary<int, BulletPoolData>();
// 活跃特效映射:特效GameObject → prefab.GetInstanceID()
private Dictionary<GameObject, int> activeMap = new Dictionary<GameObject, int>();
private const int MaxCachePerPrefab = 100;
private GameObject poolRoot;
/// <summary>从池中取一个特效(池空则 Instantiate),取出后自动播放并在播完时回池</summary>
public GameObject Get(GameObject prefab, Vector3 position, Quaternion rotation, Transform parent)
{
if (prefab == null) return null;
int prefabID = prefab.GetInstanceID();
GameObject obj = null;
if (dic.TryGetValue(prefabID, out var data))
{
// 循环跳过已销毁的坏对象
while (data.poolList.Count > 0)
{
obj = data.GetObj();
if (obj != null) break;
obj = null;
}
}
if (obj != null)
{
obj.transform.SetParent(parent, false);
obj.transform.position = position;
obj.transform.rotation = rotation;
obj.SetActive(true);
}
else
{
obj = GameObject.Instantiate(prefab, position, rotation, parent);
obj.SetActive(true);
}
activeMap[obj] = prefabID;
// 挂上自动回收组件(已挂则复用)
var autoReturn = obj.GetComponent<EffectAutoReturn>();
if (autoReturn == null)
autoReturn = obj.AddComponent<EffectAutoReturn>();
autoReturn.prefabID = prefabID;
autoReturn.Play();
return obj;
}
/// <summary>回收特效到池(通常由 EffectAutoReturn 自动调用)</summary>
public void Return(GameObject obj)
{
if (obj == null) return;
if (!activeMap.TryGetValue(obj, out int prefabID) || prefabID == 0)
{
GameObject.Destroy(obj);
return;
}
activeMap.Remove(obj);
if (!dic.TryGetValue(prefabID, out var data))
dic[prefabID] = data = new BulletPoolData(EnsurePoolRoot().transform, prefabID);
if (data.poolList.Count >= MaxCachePerPrefab)
{
GameObject.Destroy(obj);
return;
}
data.PushObj(obj);
}
/// <summary>销毁所有活跃特效 + 清空所有池(切场景调用)</summary>
public void ClearAll()
{
foreach (var kv in activeMap.Keys)
{
if (kv != null) GameObject.Destroy(kv);
}
activeMap.Clear();
foreach (var kv in dic.Values)
kv.Clear();
dic.Clear();
if (poolRoot != null)
{
GameObject.Destroy(poolRoot);
poolRoot = null;
}
}
private GameObject EnsurePoolRoot()
{
if (poolRoot == null)
{
poolRoot = new GameObject("[EffectPool]");
poolRoot.SetActive(false);
}
return poolRoot;
}
}
/// <summary>挂在池内特效上,粒子播放完自动回池</summary>
public class EffectAutoReturn : MonoBehaviour
{
[HideInInspector] public int prefabID;
private ParticleSystem[] particles;
private float checkTimer;
private const float MinPlayTime = 0.3f; // 至少播 0.3s 再检测,避免第一帧 IsAlive=false 误判
/// <summary>重新播放所有粒子</summary>
public void Play()
{
if (particles == null || particles.Length == 0)
particles = GetComponentsInChildren<ParticleSystem>(true);
checkTimer = 0f;
foreach (var p in particles)
{
if (p != null)
{
p.Clear(true);
p.Play(true);
}
}
}
void Update()
{
checkTimer += Time.deltaTime;
if (checkTimer < MinPlayTime) return;
// 所有粒子都播完 → 回池
if (particles == null || particles.Length == 0)
{
EffectPool.Instance.Return(gameObject);
return;
}
bool anyAlive = false;
foreach (var p in particles)
{
if (p != null && p.IsAlive(true)) { anyAlive = true; break; }
}
if (!anyAlive)
{
EffectPool.Instance.Return(gameObject);
}
}
}