com.alicizax.unity.framework/Runtime/ABase/GameObjectPool/GameObjectPool.cs

1363 lines
42 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Threading;
using AlicizaX;
using AlicizaX.Resource.Runtime;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace AlicizaX
{
/// <summary>
/// 对象池配置项。
/// </summary>
[Serializable]
public class PoolConfig
{
public string asset;
public float time;
public int poolCount;
}
/// <summary>
/// 预制体引用计数信息。
/// </summary>
public class PrefabRefInfo
{
public GameObject Prefab;
public int RefCount;
public float LastAccessTime;
public string AssetPath;
public PrefabRefInfo(GameObject prefab, string assetPath)
{
this.Prefab = prefab;
this.AssetPath = assetPath;
this.RefCount = 0;
this.LastAccessTime = Time.time;
}
public void AddRef()
{
RefCount++;
LastAccessTime = Time.time;
}
public void RemoveRef()
{
if (RefCount > 0) // 防止引用计数变成负数
{
RefCount--;
if (RefCount > 0)
{
LastAccessTime = Time.time;
}
Log.Info($"RemoveRef: {AssetPath}, refCount: {RefCount}");
}
else
{
Log.Warning($"尝试减少已经为0的引用计数: {AssetPath}");
}
}
public bool CanUnload(float expireTime)
{
return RefCount <= 0 && expireTime > 0 && (Time.time - LastAccessTime) > expireTime;
}
}
[Serializable]
public class PooledObject : IMemory
{
public GameObject gameObject;
public string assetPath;
public float lastUsedTime;
public bool isActive;
public string instanceName;
public bool isRefCountReduced;
[NonSerialized] public PoolObjectMonitor monitor;
public PooledObject(GameObject go, string path)
{
gameObject = go;
assetPath = path;
lastUsedTime = Time.time;
isActive = false;
instanceName = go.name;
isRefCountReduced = false;
}
public void Clear()
{
gameObject = null;
assetPath = null;
lastUsedTime = 0f;
isActive = false;
instanceName = null;
isRefCountReduced = false;
monitor = null;
}
/// <summary>
/// 获取过期进度 (0-1)1表示即将过期。
/// </summary>
public float GetExpireProgress(float expireTime)
{
if (expireTime <= 0 || isActive) return 0f;
float timeElapsed = Time.time - lastUsedTime;
return Mathf.Clamp01(timeElapsed / expireTime);
}
/// <summary>
/// 获取剩余时间。
/// </summary>
public float GetRemainingTime(float expireTime)
{
if (expireTime <= 0 || isActive) return -1f;
float timeElapsed = Time.time - lastUsedTime;
return Mathf.Max(0f, expireTime - timeElapsed);
}
}
/// <summary>
/// Inspector显示用的对象信息。
/// </summary>
[Serializable]
public class PoolObjectInfo : IMemory
{
[SerializeField] public string objectName;
[SerializeField] public string assetPath;
[SerializeField] public bool isActive;
[SerializeField] public float lastUsedTime;
[SerializeField] public float remainingTime;
[SerializeField] public float expireProgress;
[SerializeField] public GameObject gameObject;
public void UpdateFromPooledObject(PooledObject pooledObj, float expireTime)
{
objectName = pooledObj.instanceName;
assetPath = pooledObj.assetPath;
isActive = pooledObj.isActive;
lastUsedTime = pooledObj.lastUsedTime;
remainingTime = pooledObj.GetRemainingTime(expireTime);
expireProgress = pooledObj.GetExpireProgress(expireTime);
gameObject = pooledObj.gameObject;
}
public void Clear()
{
objectName = null;
assetPath = null;
isActive = false;
lastUsedTime = 0f;
remainingTime = 0f;
expireProgress = 0f;
gameObject = null;
}
}
/// <summary>
/// Inspector显示用的预制体信息.
/// </summary>
[Serializable]
public class PrefabRefInfoDisplay : IMemory
{
[SerializeField] public string assetPath;
[SerializeField] public int refCount;
[SerializeField] public float lastAccessTime;
[SerializeField] public GameObject prefab;
public void UpdateFromPrefabRefInfo(PrefabRefInfo info)
{
assetPath = info.AssetPath;
refCount = info.RefCount;
lastAccessTime = info.LastAccessTime;
prefab = info.Prefab;
}
public void Clear()
{
assetPath = null;
refCount = 0;
lastAccessTime = 0f;
prefab = null;
}
}
/// <summary>
/// Inspector显示用的池信息。
/// </summary>
[Serializable]
public class ConfigPoolInfo : IMemory
{
[SerializeField] public string configAsset;
[SerializeField] public int maxCount;
[SerializeField] public float expireTime;
[SerializeField] public int totalObjects;
[SerializeField] public int activeObjects;
[SerializeField] public int availableObjects;
[SerializeField] public int loadedPrefabs;
[SerializeField] public List<string> assetPaths = new List<string>();
[SerializeField] public List<PoolObjectInfo> objects = new List<PoolObjectInfo>();
[SerializeField] public List<PrefabRefInfoDisplay> prefabRefs = new List<PrefabRefInfoDisplay>();
public void UpdateFromPool(ConfigPool pool)
{
configAsset = pool.Config.asset;
maxCount = pool.Config.poolCount;
expireTime = pool.Config.time;
totalObjects = pool.AllObjects.Count;
activeObjects = 0;
foreach (var obj in pool.AllObjects)
{
if (obj.isActive) activeObjects++;
}
// 计算所有队列中的可用对象总数
availableObjects = 0;
foreach (var queue in pool.AvailableObjectsByPath.Values)
{
availableObjects += queue.Count;
}
loadedPrefabs = pool.LoadedPrefabs.Count;
assetPaths.Clear();
assetPaths.AddRange(pool.LoadedPrefabs.Keys);
int objectIndex = 0;
foreach (var pooledObj in pool.AllObjects)
{
if (pooledObj.gameObject != null)
{
PoolObjectInfo info;
if (objectIndex < objects.Count)
{
info = objects[objectIndex];
}
else
{
info = MemoryPool.Acquire<PoolObjectInfo>();
objects.Add(info);
}
info.UpdateFromPooledObject(pooledObj, pool.Config.time);
objectIndex++;
}
}
ReleasePoolObjectInfos(objectIndex);
int prefabIndex = 0;
foreach (var kvp in pool.LoadedPrefabs)
{
PrefabRefInfoDisplay info;
if (prefabIndex < prefabRefs.Count)
{
info = prefabRefs[prefabIndex];
}
else
{
info = MemoryPool.Acquire<PrefabRefInfoDisplay>();
prefabRefs.Add(info);
}
info.UpdateFromPrefabRefInfo(kvp.Value);
prefabIndex++;
}
ReleasePrefabRefInfos(prefabIndex);
}
public void Clear()
{
configAsset = null;
maxCount = 0;
expireTime = 0f;
totalObjects = 0;
activeObjects = 0;
availableObjects = 0;
loadedPrefabs = 0;
assetPaths.Clear();
ReleasePoolObjectInfos(0);
ReleasePrefabRefInfos(0);
}
private void ReleasePoolObjectInfos(int keepCount)
{
while (objects.Count > keepCount)
{
int lastIndex = objects.Count - 1;
MemoryPool.Release(objects[lastIndex]);
objects.RemoveAt(lastIndex);
}
}
private void ReleasePrefabRefInfos(int keepCount)
{
while (prefabRefs.Count > keepCount)
{
int lastIndex = prefabRefs.Count - 1;
MemoryPool.Release(prefabRefs[lastIndex]);
prefabRefs.RemoveAt(lastIndex);
}
}
}
/// <summary>
/// 配置组对象池 - 管理一个PoolConfig下的所有资源。
/// </summary>
public class ConfigPool
{
public readonly PoolConfig Config;
// 按资源路径分组的可用对象队列
public readonly Dictionary<string, Queue<PooledObject>> AvailableObjectsByPath;
public readonly HashSet<PooledObject> AllObjects;
public readonly Dictionary<string, PrefabRefInfo> LoadedPrefabs;
public readonly Dictionary<string, List<UniTaskCompletionSource<GameObject>>> PendingRequests;
public readonly HashSet<string> LoadingAssets;
public readonly Transform PoolRoot;
private readonly IResourceLoader _resourceLoader;
// GameObject到PooledObject的快速查找字典
private readonly Dictionary<GameObject, PooledObject> _gameObjectToPooledObject;
private readonly List<string> _availableObjectPaths;
// 重用临时队列,避免重复创建。
private static Queue<PooledObject> _tempQueue = new Queue<PooledObject>();
// 重用过期对象列表,避免重复创建。
private static readonly List<PooledObject> _expiredObjects = new List<PooledObject>();
private static readonly List<string> _expiredPrefabs = new List<string>();
public ConfigPool(PoolConfig config, IResourceLoader resourceLoader)
{
_resourceLoader = resourceLoader;
Config = config;
AvailableObjectsByPath = new Dictionary<string, Queue<PooledObject>>();
AllObjects = new HashSet<PooledObject>();
LoadedPrefabs = new Dictionary<string, PrefabRefInfo>();
PendingRequests = new Dictionary<string, List<UniTaskCompletionSource<GameObject>>>();
LoadingAssets = new HashSet<string>();
_gameObjectToPooledObject = new Dictionary<GameObject, PooledObject>();
_availableObjectPaths = new List<string>();
// 创建池根节点。
GameObject poolRootGo = new GameObject($"ConfigPool_{config.asset.Replace('/', '_')}");
PoolRoot = poolRootGo.transform;
PoolRoot.SetParent(GameObjectPool.Instance.poolContainer);
poolRootGo.SetActive(false);
}
public bool MatchesAsset(string assetPath)
{
return assetPath.StartsWith(Config.asset);
}
/// <summary>
/// 同步获取对象,如果资源未加载则同步加载。
/// </summary>
public GameObject Get(string assetPath)
{
if (!LoadedPrefabs.ContainsKey(assetPath))
{
if (LoadingAssets.Contains(assetPath))
{
Log.Warning($"资源 {assetPath} 正在异步加载中,同步获取可能导致重复加载,建议使用异步方法");
}
try
{
GameObject prefab = _resourceLoader.LoadPrefab(assetPath);
if (prefab != null)
{
LoadedPrefabs[assetPath] = new PrefabRefInfo(prefab, assetPath);
Log.Info($"同步加载资源成功: {assetPath}");
}
else
{
Log.Error($"同步加载资源失败: {assetPath}");
return null;
}
}
catch (Exception e)
{
Log.Error($"同步加载资源异常: {assetPath}, 错误: {e.Message}");
return null;
}
}
return GetInternal(assetPath);
}
/// <summary>
/// 异步获取对象。
/// </summary>
public async UniTask<GameObject> GetAsync(string assetPath, CancellationToken cancellationToken = default)
{
if (LoadedPrefabs.ContainsKey(assetPath))
{
return GetInternal(assetPath);
}
if (LoadingAssets.Contains(assetPath))
{
var completionSource = new UniTaskCompletionSource<GameObject>();
if (!PendingRequests.ContainsKey(assetPath))
{
PendingRequests[assetPath] = new List<UniTaskCompletionSource<GameObject>>();
}
PendingRequests[assetPath].Add(completionSource);
try
{
return await completionSource.Task.AttachExternalCancellation(cancellationToken);
}
catch (OperationCanceledException)
{
PendingRequests[assetPath].Remove(completionSource);
throw;
}
}
LoadingAssets.Add(assetPath);
try
{
GameObject prefab = await _resourceLoader.LoadPrefabAsync(assetPath, cancellationToken);
if (prefab != null)
{
LoadedPrefabs[assetPath] = new PrefabRefInfo(prefab, assetPath);
Log.Info($"异步加载资源成功: {assetPath}");
if (PendingRequests.ContainsKey(assetPath))
{
var requests = PendingRequests[assetPath];
PendingRequests.Remove(assetPath);
foreach (var request in requests)
{
try
{
var go = GetInternal(assetPath);
request.TrySetResult(go);
}
catch (Exception e)
{
request.TrySetException(e);
}
}
}
return GetInternal(assetPath);
}
else
{
throw new Exception($"无法异步加载资源: {assetPath}");
}
}
catch (Exception e)
{
Log.Error($"异步加载资源失败: {assetPath}, 错误: {e.Message}");
if (PendingRequests.ContainsKey(assetPath))
{
var requests = PendingRequests[assetPath];
PendingRequests.Remove(assetPath);
foreach (var request in requests)
{
request.TrySetException(e);
}
}
throw;
}
finally
{
LoadingAssets.Remove(assetPath);
}
}
/// <summary>
/// 创建新的池对象。
/// </summary>
private PooledObject CreatePooledObject(string assetPath)
{
var prefabRefInfo = LoadedPrefabs[assetPath];
GameObject instantiate = GameObject.Instantiate(prefabRefInfo.Prefab);
var pooledObj = MemoryPool.Acquire<PooledObject>();
pooledObj.gameObject = instantiate;
pooledObj.assetPath = assetPath;
pooledObj.lastUsedTime = Time.time;
pooledObj.isActive = false;
pooledObj.instanceName = instantiate.name;
pooledObj.isRefCountReduced = false;
AllObjects.Add(pooledObj);
_gameObjectToPooledObject[instantiate] = pooledObj;
prefabRefInfo.AddRef();
var monitor = instantiate.GetComponent<PoolObjectMonitor>();
if (monitor == null)
{
monitor = instantiate.AddComponent<PoolObjectMonitor>();
}
monitor.Initialize(this, pooledObj);
pooledObj.monitor = monitor;
return pooledObj;
}
private GameObject GetInternal(string assetPath)
{
PooledObject pooledObj = null;
// 从按路径分组的队列中获取
if (AvailableObjectsByPath.TryGetValue(assetPath, out var queue) && queue.Count > 0)
{
// 清理已销毁的对象
while (queue.Count > 0)
{
var obj = queue.Dequeue();
if (obj.gameObject == null)
{
// 只有在引用计数未减少时才处理
if (!obj.isRefCountReduced)
{
OnObjectReallyDestroyed(obj);
}
else
{
// 只需要从集合中移除,不需要减少引用计数
AllObjects.Remove(obj);
}
continue;
}
pooledObj = obj;
break;
}
}
if (pooledObj == null)
{
if (AllObjects.Count < Config.poolCount)
{
pooledObj = CreatePooledObject(assetPath);
}
else
{
// 使用LINQ找到最旧的未使用对象更高效
PooledObject oldestObj = null;
foreach (var obj in AllObjects)
{
if (obj.isActive)
{
continue;
}
if (oldestObj == null || obj.lastUsedTime < oldestObj.lastUsedTime)
{
oldestObj = obj;
}
}
if (oldestObj != null)
{
DestroyPooledObject(oldestObj);
pooledObj = CreatePooledObject(assetPath);
}
else
{
Log.Warning($"对象池已满且所有对象都在使用中: {Config.asset},无法创建新对象 {assetPath}");
return null;
}
}
}
pooledObj.isActive = true;
pooledObj.lastUsedTime = Time.time;
pooledObj.gameObject.SetActive(true);
return pooledObj.gameObject;
}
public void Return(GameObject go)
{
if (!_gameObjectToPooledObject.TryGetValue(go, out var pooledObj))
{
return;
}
if (pooledObj != null && pooledObj.isActive)
{
pooledObj.isActive = false;
pooledObj.lastUsedTime = Time.time;
go.SetActive(false);
go.transform.SetParent(PoolRoot);
go.transform.localPosition = Vector3.zero;
go.transform.localRotation = Quaternion.identity;
go.transform.localScale = Vector3.one;
// 放入按路径分组的队列
if (!AvailableObjectsByPath.TryGetValue(pooledObj.assetPath, out var queue))
{
queue = new Queue<PooledObject>();
AvailableObjectsByPath[pooledObj.assetPath] = queue;
}
queue.Enqueue(pooledObj);
}
}
public void OnObjectDestroyed(PooledObject pooledObj)
{
// 从GameObjectPool的字典中移除防止内存泄漏
if (pooledObj.gameObject != null)
{
GameObjectPool.Instance.RemoveGameObjectReference(pooledObj.gameObject);
}
// 防止重复减少引用计数
if (!pooledObj.isRefCountReduced)
{
OnObjectReallyDestroyed(pooledObj);
MemoryPool.Release(pooledObj);
}
else
{
// DestroyPooledObject 已调用 Detach此分支理论上不可达
AllObjects.Remove(pooledObj);
CleanAvailableQueue(pooledObj);
}
}
private void OnObjectReallyDestroyed(PooledObject pooledObj)
{
// 标记引用计数已减少,防止重复处理
if (pooledObj.isRefCountReduced)
{
return;
}
pooledObj.isRefCountReduced = true;
AllObjects.Remove(pooledObj);
// 从快速查找字典中移除
if (pooledObj.gameObject != null)
{
_gameObjectToPooledObject.Remove(pooledObj.gameObject);
}
// 减少预制体引用计数
if (LoadedPrefabs.TryGetValue(pooledObj.assetPath, out PrefabRefInfo refInfo))
{
refInfo.RemoveRef();
}
CleanAvailableQueue(pooledObj);
}
// 清理可用队列
private void CleanAvailableQueue(PooledObject pooledObj)
{
// 从对应路径的队列中移除
if (AvailableObjectsByPath.TryGetValue(pooledObj.assetPath, out var queue))
{
_tempQueue.Clear();
while (queue.Count > 0)
{
var obj = queue.Dequeue();
if (obj != pooledObj)
{
_tempQueue.Enqueue(obj);
}
}
// 交换队列
(queue, _tempQueue) = (_tempQueue, queue);
// 如果队列为空,从字典中移除
if (queue.Count == 0)
{
AvailableObjectsByPath.Remove(pooledObj.assetPath);
}
}
}
private void DestroyPooledObject(PooledObject pooledObj)
{
// 先标记引用计数已减少
if (pooledObj.isRefCountReduced)
{
return;
}
// 先处理引用计数
OnObjectReallyDestroyed(pooledObj);
if (pooledObj.gameObject != null)
{
pooledObj.monitor?.Detach();
GameObject.Destroy(pooledObj.gameObject);
}
MemoryPool.Release(pooledObj);
}
public void CheckExpiredObjects()
{
if (Config.time <= 0) return;
float currentTime = Time.time;
// 重用过期对象列表
_expiredObjects.Clear();
foreach (var obj in AllObjects)
{
if (!obj.isActive && !obj.isRefCountReduced && (currentTime - obj.lastUsedTime) > Config.time)
{
_expiredObjects.Add(obj);
}
}
foreach (var expiredObj in _expiredObjects)
{
DestroyPooledObject(expiredObj);
}
// 重建所有路径的可用队列
_availableObjectPaths.Clear();
foreach (var assetPath in AvailableObjectsByPath.Keys)
{
_availableObjectPaths.Add(assetPath);
}
foreach (var assetPath in _availableObjectPaths)
{
if (!AvailableObjectsByPath.TryGetValue(assetPath, out var queue))
{
continue;
}
_tempQueue.Clear();
while (queue.Count > 0)
{
var obj = queue.Dequeue();
if (AllObjects.Contains(obj) && !obj.isRefCountReduced)
{
_tempQueue.Enqueue(obj);
}
}
if (_tempQueue.Count > 0)
{
// 交换队列
(queue, _tempQueue) = (_tempQueue, queue);
}
else
{
// 队列为空,从字典中移除
AvailableObjectsByPath.Remove(assetPath);
}
}
CheckExpiredPrefabs();
}
private void CheckExpiredPrefabs()
{
if (Config.time <= 0) return;
// 重用过期预制体列表
_expiredPrefabs.Clear();
foreach (var kvp in LoadedPrefabs)
{
var refInfo = kvp.Value;
if (refInfo.CanUnload(Config.time))
{
_expiredPrefabs.Add(kvp.Key);
}
}
foreach (var assetPath in _expiredPrefabs)
{
var refInfo = LoadedPrefabs[assetPath];
Log.Info($"卸载过期预制体: {assetPath}, 引用计数: {refInfo.RefCount}");
_resourceLoader.UnloadAsset(refInfo.Prefab);
LoadedPrefabs.Remove(assetPath);
}
}
public void Clear()
{
foreach (var obj in AllObjects)
{
if (obj.gameObject != null)
{
obj.monitor?.Detach();
GameObject.Destroy(obj.gameObject);
}
MemoryPool.Release(obj);
}
AllObjects.Clear();
AvailableObjectsByPath.Clear();
_gameObjectToPooledObject.Clear();
foreach (var kvp in LoadedPrefabs)
{
var refInfo = kvp.Value;
if (refInfo.Prefab != null)
{
Log.Info($"清理时卸载预制体: {kvp.Key}, 引用计数: {refInfo.RefCount}");
_resourceLoader.UnloadAsset(refInfo.Prefab);
}
}
LoadedPrefabs.Clear();
LoadingAssets.Clear();
foreach (var requests in PendingRequests.Values)
{
foreach (var request in requests)
{
request.TrySetCanceled();
}
}
PendingRequests.Clear();
if (PoolRoot != null)
{
GameObject.Destroy(PoolRoot.gameObject);
}
}
}
/// <summary>
/// 对象销毁监听器。
/// </summary>
public class PoolObjectMonitor : MonoBehaviour
{
private ConfigPool _pool;
private PooledObject _pooledObject;
public void Initialize(ConfigPool pool, PooledObject pooledObject)
{
_pool = pool;
_pooledObject = pooledObject;
}
public void Detach()
{
_pool = null;
_pooledObject = null;
}
private void OnDestroy()
{
if (_pool != null && _pooledObject != null)
{
_pool.OnObjectDestroyed(_pooledObject);
}
}
}
/// <summary>
/// 游戏对象池管理器。
/// </summary>
public class GameObjectPool : MonoBehaviour
{
private static GameObjectPool _instance;
public static GameObjectPool Instance
{
get
{
if (_instance == null)
{
GameObject go = new GameObject("[GameObjectPool]");
_instance = go.AddComponent<GameObjectPool>();
DontDestroyOnLoad(go);
}
return _instance;
}
}
[Header("检查间隔")] public float checkInterval = 10f;
[Header("资源加载器")] public bool useEngineResourceLoader = true;
[Header("配置路径")] public string poolConfigPath = "Assets/Bundles/Configs/ScriptableObject/PoolConfig";
[Header("Inspector显示设置")] public bool showDetailedInfo = true;
[Header("池状态信息")] [SerializeField] private List<ConfigPoolInfo> poolInfos = new List<ConfigPoolInfo>();
public Transform poolContainer;
internal IResourceLoader _resourceLoader;
private List<PoolConfig> _poolConfigs;
private List<ConfigPool> _configPools;
private Dictionary<GameObject, ConfigPool> _gameObjectToPool;
private Dictionary<string, ConfigPool> _configPoolCache;
// 重用预加载对象列表
private static readonly List<GameObject> _preloadedObjects = new List<GameObject>();
private float _lastCleanupTime;
private void Awake()
{
if (_instance == null)
{
_instance = this;
DontDestroyOnLoad(gameObject);
Initialize();
}
else if (_instance != this)
{
Destroy(gameObject);
}
}
private void Initialize()
{
_resourceLoader = useEngineResourceLoader ? new AlicizaResourceLoader() as IResourceLoader : new DefaultResourceLoader() as IResourceLoader;
GameObject containerGo = new GameObject("PoolContainer");
poolContainer = containerGo.transform;
poolContainer.SetParent(transform);
_configPools = new List<ConfigPool>();
_gameObjectToPool = new Dictionary<GameObject, ConfigPool>();
_configPoolCache = new Dictionary<string, ConfigPool>();
try
{
_poolConfigs = ModuleSystem.GetModule<IResourceModule>().LoadAsset<PoolConfigScriptableObject>(poolConfigPath).configs;
_poolConfigs.Sort((a, b) => b.asset.Length.CompareTo(a.asset.Length));
foreach (var config in _poolConfigs)
{
var configPool = new ConfigPool(config, _resourceLoader);
_configPools.Add(configPool);
}
}
catch (Exception e)
{
Log.Error($"加载对象池配置失败: {e.Message}");
_poolConfigs = new List<PoolConfig>();
}
// 初始化清理时间
_lastCleanupTime = Time.time;
}
private void Update()
{
if (Time.time - _lastCleanupTime >= checkInterval)
{
PerformCleanup();
_lastCleanupTime = Time.time;
}
}
/// <summary>
/// 执行对象池清理。
/// </summary>
private void PerformCleanup()
{
if (_configPools == null || _configPools.Count == 0)
{
return;
}
foreach (var pool in _configPools)
{
pool.CheckExpiredObjects();
}
}
/// <summary>
/// 手动触发一次清理。
/// </summary>
public void ForceCleanup()
{
PerformCleanup();
_lastCleanupTime = Time.time;
}
/// <summary>
/// 内部方法从字典中移除GameObject引用防止内存泄漏。
/// </summary>
internal void RemoveGameObjectReference(GameObject go)
{
if (go != null)
{
_gameObjectToPool.Remove(go);
}
}
// Editor专用的刷新。
private void UpdateInspectorInfo()
{
if (_configPools == null)
{
ReleaseInspectorInfos(0);
return;
}
int poolIndex = 0;
foreach (var pool in _configPools)
{
ConfigPoolInfo info;
if (poolIndex < poolInfos.Count)
{
info = poolInfos[poolIndex];
}
else
{
info = MemoryPool.Acquire<ConfigPoolInfo>();
poolInfos.Add(info);
}
info.UpdateFromPool(pool);
poolIndex++;
}
ReleaseInspectorInfos(poolIndex);
}
public void SetResourceLoader(IResourceLoader resourceLoader)
{
_resourceLoader = resourceLoader;
}
public GameObject GetGameObject(string assetPath, Transform parent = null)
{
ConfigPool pool = FindConfigPool(assetPath);
GameObject go = null;
if (pool != null)
{
go = pool.Get(assetPath);
}
else
{
go = _resourceLoader.LoadGameObject(assetPath, parent);
}
if (go != null && pool != null)
{
_gameObjectToPool[go] = pool;
go.transform.SetParent(parent);
}
return go;
}
public async UniTask<GameObject> GetGameObjectAsync(string assetPath, Transform parent = null, CancellationToken cancellationToken = default)
{
ConfigPool pool = FindConfigPool(assetPath);
GameObject go = null;
if (pool != null)
{
go = await pool.GetAsync(assetPath, cancellationToken);
}
else
{
go = await _resourceLoader.LoadGameObjectAsync(assetPath, parent, cancellationToken);
}
if (go != null && pool != null)
{
_gameObjectToPool[go] = pool;
go.transform.SetParent(parent); // 设置父节点
}
return go;
}
public void Release(GameObject go)
{
if (go == null) return;
if (_gameObjectToPool.TryGetValue(go, out ConfigPool pool))
{
pool.Return(go);
_gameObjectToPool.Remove(go);
}
else
{
Destroy(go);
}
}
public async UniTask PreloadAsync(string assetPath, int count = 1, CancellationToken cancellationToken = default)
{
ConfigPool pool = FindConfigPool(assetPath);
if (pool == null)
{
Log.Warning($"资源 {assetPath} 没有对应的池配置,无法预加载");
return;
}
// 优化:重用预加载对象列表
_preloadedObjects.Clear();
for (int i = 0; i < count; i++)
{
GameObject go = await pool.GetAsync(assetPath, cancellationToken);
if (go != null)
{
_preloadedObjects.Add(go);
}
}
foreach (var go in _preloadedObjects)
{
pool.Return(go);
_gameObjectToPool.Remove(go);
}
}
public void Preload(string assetPath, int count = 1)
{
ConfigPool pool = FindConfigPool(assetPath);
if (pool == null)
{
Log.Warning($"资源 {assetPath} 没有对应的池配置,无法预加载");
return;
}
// 优化:重用预加载对象列表
_preloadedObjects.Clear();
for (int i = 0; i < count; i++)
{
GameObject go = pool.Get(assetPath);
if (go != null)
{
_preloadedObjects.Add(go);
}
}
foreach (var go in _preloadedObjects)
{
pool.Return(go);
_gameObjectToPool.Remove(go);
}
}
private ConfigPool FindConfigPool(string assetPath)
{
if (string.IsNullOrEmpty(assetPath))
{
return null;
}
if (_configPools == null)
{
return null;
}
if (_configPoolCache == null)
{
_configPoolCache = new Dictionary<string, ConfigPool>();
}
if (_configPoolCache != null && _configPoolCache.TryGetValue(assetPath, out var cachedPool))
{
return cachedPool;
}
foreach (var pool in _configPools)
{
if (pool.MatchesAsset(assetPath))
{
_configPoolCache[assetPath] = pool;
return pool;
}
}
_configPoolCache[assetPath] = null;
return null;
}
/// <summary>
/// 手动刷新Inspector信息
/// </summary>
public void RefreshInspectorInfo()
{
UpdateInspectorInfo();
}
public void ClearAllPools()
{
if (_configPools != null)
{
foreach (var pool in _configPools)
{
pool.Clear();
}
}
if (_gameObjectToPool != null)
{
_gameObjectToPool.Clear();
}
if (_configPoolCache != null)
{
_configPoolCache.Clear();
}
ReleaseInspectorInfos(0);
}
private void ReleaseInspectorInfos(int keepCount)
{
while (poolInfos.Count > keepCount)
{
int lastIndex = poolInfos.Count - 1;
MemoryPool.Release(poolInfos[lastIndex]);
poolInfos.RemoveAt(lastIndex);
}
}
private void OnDestroy()
{
ClearAllPools();
}
}
public interface IResourceLoader
{
GameObject LoadPrefab(string location);
UniTask<GameObject> LoadPrefabAsync(string location, CancellationToken cancellationToken = default);
GameObject LoadGameObject(string location, Transform parent = null);
UniTask<GameObject> LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default);
void UnloadAsset(GameObject gameObject);
}
public class DefaultResourceLoader : IResourceLoader
{
public GameObject LoadPrefab(string location)
{
return Resources.Load<GameObject>(location);
}
public async UniTask<GameObject> LoadPrefabAsync(string location, CancellationToken cancellationToken = default)
{
return await Resources.LoadAsync<GameObject>(location).ToUniTask(cancellationToken: cancellationToken) as GameObject;
}
public GameObject LoadGameObject(string location, Transform parent = null)
{
var prefab = Resources.Load<GameObject>(location);
if (prefab == null) return null;
var instance = GameObject.Instantiate(prefab);
if (instance != null && parent != null)
{
instance.transform.SetParent(parent);
}
return instance;
}
public async UniTask<GameObject> LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default)
{
var prefab = await Resources.LoadAsync<GameObject>(location).ToUniTask(cancellationToken: cancellationToken) as GameObject;
if (prefab == null) return null;
var instance = GameObject.Instantiate(prefab);
if (instance != null && parent != null)
{
instance.transform.SetParent(parent);
}
return instance;
}
public void UnloadAsset(GameObject gameObject)
{
Resources.UnloadAsset(gameObject);
}
}
public class AlicizaResourceLoader : IResourceLoader
{
private IResourceModule _resourceModule;
private void CheckInit()
{
if (_resourceModule == null)
{
_resourceModule = ModuleSystem.GetModule<IResourceModule>();
}
}
public GameObject LoadPrefab(string location)
{
CheckInit();
return _resourceModule.LoadAsset<GameObject>(location);
}
public async UniTask<GameObject> LoadPrefabAsync(string location, CancellationToken cancellationToken = default)
{
CheckInit();
return await _resourceModule.LoadAssetAsync<GameObject>(location, cancellationToken);
}
public GameObject LoadGameObject(string location, Transform parent = null)
{
CheckInit();
return _resourceModule.LoadGameObject(location, parent);
}
public async UniTask<GameObject> LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default)
{
CheckInit();
return await _resourceModule.LoadGameObjectAsync(location, parent, cancellationToken);
}
public void UnloadAsset(GameObject gameObject)
{
CheckInit();
_resourceModule.UnloadAsset(gameObject);
}
}
public static class GameObjectPoolUtil
{
public static GameObject LoadGameObject(string assetPath, Transform parent = null)
{
return GameObjectPool.Instance.GetGameObject(assetPath, parent);
}
public static async UniTask<GameObject> LoadGameObjectAsync(string assetPath, Transform parent = null, CancellationToken cancellationToken = default)
{
return await GameObjectPool.Instance.GetGameObjectAsync(assetPath, parent, cancellationToken);
}
public static void Release(GameObject go)
{
GameObjectPool.Instance.Release(go);
}
}
}