using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; using UnityEngine.AddressableAssets; namespace BaseGames.Core.Pool { /// /// 全局对象池(Persistent 场景)。 /// 先调用 预热后,再通过 取出对象。 /// [DefaultExecutionOrder(-800)] public class GlobalObjectPool : MonoBehaviour, IObjectPoolService { [System.Serializable] public struct PoolConfig { public string AddressKey; public int InitialCount; /// 0 = 无上限;> 0 强制限制池中 + 活跃对象总数。 public int MaxCount; } [SerializeField] private PoolConfig[] _warmupConfigs; private readonly Dictionary> _pools = new(); private readonly Dictionary> _alive = new(); private readonly Dictionary _prefabCache = new(); private readonly Dictionary _maxCounts = new(); private void Awake() { if (ServiceLocator.GetOrDefault() != null) { Destroy(gameObject); return; } ServiceLocator.Register(this); } private void OnDestroy() { // 释放所有通过 Addressables.LoadAssetAsync 加载的预制件引用 foreach (var pfx in _prefabCache.Values) Addressables.Release(pfx); _prefabCache.Clear(); ServiceLocator.Unregister(this); } // ── 预热 ────────────────────────────────────────────────────────── /// /// 异步预热所有配置中的对象。 /// MonoBehaviour 中可用 StartCoroutine(pool.WarmupAsync().AsIEnumerator()) 桥接, /// 或直接 await(UniTask / Awaitable)。 /// public async Task WarmupAsync() { foreach (var cfg in _warmupConfigs) { _maxCounts[cfg.AddressKey] = cfg.MaxCount; await WarmupSingleAsync(cfg.AddressKey, cfg.InitialCount); } } private async Task WarmupSingleAsync(string key, int count) { var prefab = await Addressables.LoadAssetAsync(key).Task; _prefabCache[key] = prefab; EnsureCollections(key, count); for (int i = 0; i < count; i++) EnqueueNew(key, prefab); } private void EnsureCollections(string key, int capacity) { if (!_pools.ContainsKey(key)) _pools[key] = new Queue(capacity); // MaxCount==0 表示无上限,无需追踪活跃对象,跳过 _alive 分配 if (_maxCounts.GetValueOrDefault(key, 0) > 0 && !_alive.ContainsKey(key)) _alive[key] = new LinkedList(); } private void EnqueueNew(string key, GameObject prefab) { var go = Instantiate(prefab); var po = go.GetComponent(); if (po == null) po = go.AddComponent(); po.Setup(key, this); go.SetActive(false); _pools[key].Enqueue(po); } // ── Spawn ───────────────────────────────────────────────────────── public T Spawn(string key, Vector3 position, Quaternion rotation) where T : Component => SpawnInternal(key, position, rotation)?.GetComponentCached(); public GameObject Spawn(string key, Vector3 position, Quaternion rotation) => SpawnInternal(key, position, rotation)?.gameObject; private PooledObject SpawnInternal(string key, Vector3 pos, Quaternion rot) { if (!_pools.TryGetValue(key, out var queue)) { Debug.LogError($"[GlobalObjectPool] '{key}' 未预热,请先调用 WarmupAsync。"); return null; } PooledObject po; int maxCount = _maxCounts.GetValueOrDefault(key, 0); // maxCount==0 时不追踪活跃对象,aliveList 保持 null LinkedList aliveList = maxCount > 0 ? GetAliveList(key) : null; if (queue.Count > 0) { po = queue.Dequeue(); } else if (aliveList != null && aliveList.Count >= maxCount) { // 已达上限:LRU 回收最早 Spawn 的活跃对象(LinkedList 头节点即最老) po = aliveList.First.Value; aliveList.RemoveFirst(); // O(1) po.AliveNode = null; po.ForceReturnToPool(); Debug.LogWarning($"[GlobalObjectPool] '{key}' 已达 MaxCount={maxCount},LRU 回收中。"); } else { // 池空且未达上限:同步实例化,并在后台协程补池 if (!_prefabCache.TryGetValue(key, out var pfx)) { Debug.LogError($"[GlobalObjectPool] '{key}' Prefab 未缓存。"); return null; } var go = Instantiate(pfx); po = go.GetComponent(); if (po == null) po = go.AddComponent(); po.Setup(key, this); StartCoroutine(BackgroundRefillCoroutine(key, 1)); } po.transform.SetPositionAndRotation(pos, rot); po.gameObject.SetActive(true); po.OnSpawn(); // 存储节点引用,供 Despawn 时 O(1) 移除 if (aliveList != null) po.AliveNode = aliveList.AddLast(po); // 尾部 = 最新,头部 = 最老(LRU) return po; } // ── Despawn ─────────────────────────────────────────────────────── public void Despawn(string key, PooledObject po) { // 通过存储的节点 O(1) 移除,避免 LinkedList.Remove(value) 的 O(n) 遍历 if (po.AliveNode != null) { if (_alive.TryGetValue(key, out var aliveRef)) aliveRef.Remove(po.AliveNode); po.AliveNode = null; } po.gameObject.SetActive(false); po.OnDespawn(); int maxCount = _maxCounts.GetValueOrDefault(key, 0); int queueSize = _pools.TryGetValue(key, out var queue) ? queue.Count : 0; int aliveCount = (maxCount > 0 && _alive.TryGetValue(key, out var al)) ? al.Count : 0; if (maxCount > 0 && queueSize + aliveCount >= maxCount) { Destroy(po.gameObject); return; } if (queue == null) { _pools[key] = queue = new Queue(); } queue.Enqueue(po); } // ── 后台补池 ────────────────────────────────────────────────────── private IEnumerator BackgroundRefillCoroutine(string key, int count) { if (!_prefabCache.TryGetValue(key, out var pfx)) yield break; for (int i = 0; i < count; i++) { yield return null; // 下一帧 var go = Instantiate(pfx); var po = go.GetComponent(); if (po == null) po = go.AddComponent(); po.Setup(key, this); go.SetActive(false); if (_pools.TryGetValue(key, out var q)) q.Enqueue(po); } } // ── 清空 ────────────────────────────────────────────────────────── public void ClearPool(string key) { if (_pools.TryGetValue(key, out var queue)) { while (queue.Count > 0) { var po = queue.Dequeue(); if (po != null) Destroy(po.gameObject); } _pools.Remove(key); } _alive.Remove(key); if (_prefabCache.TryGetValue(key, out var pfx)) { Addressables.Release(pfx); _prefabCache.Remove(key); } } private LinkedList GetAliveList(string key) { if (!_alive.TryGetValue(key, out var list)) _alive[key] = list = new LinkedList(); return list; } } }