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

1217 lines
38 KiB
C#
Raw Normal View History

2026-03-10 13:19:58 +08:00
#region Class Documentation
/************************************************************************************************************
Class Name: GameObjectPool.cs
Type: Pool, GameObject, GameObjectPool
Example:
// 异步加载游戏物体。
var gameObject = await GameObjectPool.Instance.GetGameObjectAsync(path, token);
// 同步加载游戏物体。
var gameObject = GameObjectPool.Instance.GetGameObject(path);
Example1:
// 异步加载游戏物体。
var gameObject = await GameObjectPoolHelper.LoadGameObjectAsync(path, token);
// 同步加载游戏物体。
var gameObject = GameObjectPoolHelper.LoadGameObject(path);
************************************************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
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
{
public GameObject gameObject;
public string assetPath;
public float lastUsedTime;
public bool isActive;
public string instanceName;
public bool isRefCountReduced;
public PooledObject(GameObject go, string path)
{
gameObject = go;
assetPath = path;
lastUsedTime = Time.time;
isActive = false;
instanceName = go.name;
isRefCountReduced = false;
}
/// <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
{
[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;
}
}
/// <summary>
/// Inspector显示用的预制体信息.
/// </summary>
[Serializable]
public class PrefabRefInfoDisplay
{
[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;
}
}
/// <summary>
/// Inspector显示用的池信息。
/// </summary>
[Serializable]
public class ConfigPoolInfo
{
[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);
objects.Clear();
int objectIndex = 0;
foreach (var pooledObj in pool.AllObjects)
{
if (pooledObj.gameObject != null)
{
PoolObjectInfo info;
if (objectIndex < objects.Count)
{
info = objects[objectIndex];
}
else
{
info = new PoolObjectInfo();
objects.Add(info);
}
info.UpdateFromPooledObject(pooledObj, pool.Config.time);
objectIndex++;
}
}
prefabRefs.Clear();
int prefabIndex = 0;
foreach (var kvp in pool.LoadedPrefabs)
{
PrefabRefInfoDisplay info;
if (prefabIndex < prefabRefs.Count)
{
info = prefabRefs[prefabIndex];
}
else
{
info = new PrefabRefInfoDisplay();
prefabRefs.Add(info);
}
info.UpdateFromPrefabRefInfo(kvp.Value);
prefabIndex++;
}
}
}
/// <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 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>();
// 创建池根节点。
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 = new PooledObject(instantiate, assetPath);
AllObjects.Add(pooledObj);
_gameObjectToPooledObject[instantiate] = pooledObj;
prefabRefInfo.AddRef();
var monitor = instantiate.GetComponent<PoolObjectMonitor>();
if (monitor == null)
{
monitor = instantiate.AddComponent<PoolObjectMonitor>();
}
monitor.Initialize(this, pooledObj);
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找到最旧的未使用对象更高效
var oldestObj = AllObjects
.Where(obj => !obj.isActive)
.OrderBy(obj => obj.lastUsedTime)
.FirstOrDefault();
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);
}
else
{
// 只需要从集合中移除
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)
{
GameObject.Destroy(pooledObj.gameObject);
}
}
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);
}
// 重建所有路径的可用队列
foreach (var kvp in AvailableObjectsByPath.ToList())
{
var assetPath = kvp.Key;
var queue = kvp.Value;
_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)
{
GameObject.Destroy(obj.gameObject);
}
}
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;
}
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 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>();
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()
{
poolInfos.Clear();
foreach (var pool in _configPools)
{
var info = new ConfigPoolInfo();
info.UpdateFromPool(pool);
poolInfos.Add(info);
}
}
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)
{
foreach (var pool in _configPools)
{
if (pool.MatchesAsset(assetPath))
{
return pool;
}
}
return null;
}
/// <summary>
/// 手动刷新Inspector信息
/// </summary>
public void RefreshInspectorInfo()
{
UpdateInspectorInfo();
}
public void ClearAllPools()
{
foreach (var pool in _configPools)
{
pool.Clear();
}
_gameObjectToPool.Clear();
poolInfos.Clear();
}
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 ObjectPoolUtil
{
public static GameObject LoadGameObject(string assetPath, Transform parent = null)
{
return GameObjectPool.Instance.GetGameObject(assetPath);
}
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);
}
}
}