using System.Collections.Generic; using UnityEngine; /// /// 特效专用对象池 — 按 prefab.GetInstanceID() 分桶 /// 粒子播放完自动回池(通过 EffectAutoReturn 组件检测) /// 用法: /// var go = EffectPool.Instance.Get(prefab, pos, rot, parent); /// // 无需手动回收,EffectAutoReturn 会在粒子播完后自动回池 /// EffectPool.Instance.ClearAll(); // 切场景时彻底清理 /// public class EffectPool : Singleton { // prefab.GetInstanceID() → 该 prefab 的池 private Dictionary dic = new Dictionary(); // 活跃特效映射:特效GameObject → prefab.GetInstanceID() private Dictionary activeMap = new Dictionary(); private const int MaxCachePerPrefab = 100; private GameObject poolRoot; /// 从池中取一个特效(池空则 Instantiate),取出后自动播放并在播完时回池 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(); if (autoReturn == null) autoReturn = obj.AddComponent(); autoReturn.prefabID = prefabID; autoReturn.Play(); return obj; } /// 回收特效到池(通常由 EffectAutoReturn 自动调用) 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); } /// 销毁所有活跃特效 + 清空所有池(切场景调用) 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; } } /// 挂在池内特效上,粒子播放完自动回池 public class EffectAutoReturn : MonoBehaviour { [HideInInspector] public int prefabID; private ParticleSystem[] particles; private float checkTimer; private const float MinPlayTime = 0.3f; // 至少播 0.3s 再检测,避免第一帧 IsAlive=false 误判 private const float MaxLifetime = 6f; // ⭐ 兜底:超过 6 秒强制回池,防止 looping 残留导致特效永生 /// 重新播放所有粒子 public void Play() { if (particles == null || particles.Length == 0) particles = GetComponentsInChildren(true); checkTimer = 0f; foreach (var p in particles) { if (p != null) { // ⭐ 强制关闭 looping:有些预制体子级 ParticleSystem 设了 looping=1, // 会导致 IsAlive(true) 永远返回 true,特效一直累加直至内存爆满死机 var main = p.main; main.loop = false; p.Clear(true); p.Play(true); } } } void Update() { checkTimer += Time.deltaTime; if (checkTimer < MinPlayTime) return; // ⭐ 6 秒兜底:无论是否还存活,超过 6 秒强制回池 if (checkTimer >= MaxLifetime) { EffectPool.Instance.Return(gameObject); 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); } } }