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()
{
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;
}
}
}