diff --git a/Runtime/ABase/GameObjectPool/GameObjectPool.cs b/Runtime/ABase/GameObjectPool/GameObjectPool.cs index 984b580..92c7cfa 100644 --- a/Runtime/ABase/GameObjectPool/GameObjectPool.cs +++ b/Runtime/ABase/GameObjectPool/GameObjectPool.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using AlicizaX; using AlicizaX.Resource.Runtime; @@ -71,7 +70,7 @@ namespace AlicizaX } [Serializable] - public class PooledObject + public class PooledObject : IMemory { public GameObject gameObject; public string assetPath; @@ -79,6 +78,7 @@ namespace AlicizaX public bool isActive; public string instanceName; public bool isRefCountReduced; + [NonSerialized] public PoolObjectMonitor monitor; public PooledObject(GameObject go, string path) { @@ -90,6 +90,17 @@ namespace AlicizaX isRefCountReduced = false; } + public void Clear() + { + gameObject = null; + assetPath = null; + lastUsedTime = 0f; + isActive = false; + instanceName = null; + isRefCountReduced = false; + monitor = null; + } + /// /// 获取过期进度 (0-1),1表示即将过期。 /// @@ -115,7 +126,7 @@ namespace AlicizaX /// Inspector显示用的对象信息。 /// [Serializable] - public class PoolObjectInfo + public class PoolObjectInfo : IMemory { [SerializeField] public string objectName; [SerializeField] public string assetPath; @@ -135,13 +146,24 @@ namespace AlicizaX expireProgress = pooledObj.GetExpireProgress(expireTime); gameObject = pooledObj.gameObject; } + + public void Clear() + { + objectName = null; + assetPath = null; + isActive = false; + lastUsedTime = 0f; + remainingTime = 0f; + expireProgress = 0f; + gameObject = null; + } } /// /// Inspector显示用的预制体信息. /// [Serializable] - public class PrefabRefInfoDisplay + public class PrefabRefInfoDisplay : IMemory { [SerializeField] public string assetPath; [SerializeField] public int refCount; @@ -155,13 +177,21 @@ namespace AlicizaX lastAccessTime = info.LastAccessTime; prefab = info.Prefab; } + + public void Clear() + { + assetPath = null; + refCount = 0; + lastAccessTime = 0f; + prefab = null; + } } /// /// Inspector显示用的池信息。 /// [Serializable] - public class ConfigPoolInfo + public class ConfigPoolInfo : IMemory { [SerializeField] public string configAsset; [SerializeField] public int maxCount; @@ -198,7 +228,6 @@ namespace AlicizaX assetPaths.Clear(); assetPaths.AddRange(pool.LoadedPrefabs.Keys); - objects.Clear(); int objectIndex = 0; foreach (var pooledObj in pool.AllObjects) { @@ -211,7 +240,7 @@ namespace AlicizaX } else { - info = new PoolObjectInfo(); + info = MemoryPool.Acquire(); objects.Add(info); } @@ -219,8 +248,8 @@ namespace AlicizaX objectIndex++; } } + ReleasePoolObjectInfos(objectIndex); - prefabRefs.Clear(); int prefabIndex = 0; foreach (var kvp in pool.LoadedPrefabs) { @@ -231,13 +260,48 @@ namespace AlicizaX } else { - info = new PrefabRefInfoDisplay(); + info = MemoryPool.Acquire(); 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); + } } } @@ -259,6 +323,7 @@ namespace AlicizaX // GameObject到PooledObject的快速查找字典 private readonly Dictionary _gameObjectToPooledObject; + private readonly List _availableObjectPaths; // 重用临时队列,避免重复创建。 private static Queue _tempQueue = new Queue(); @@ -277,6 +342,7 @@ namespace AlicizaX PendingRequests = new Dictionary>>(); LoadingAssets = new HashSet(); _gameObjectToPooledObject = new Dictionary(); + _availableObjectPaths = new List(); // 创建池根节点。 GameObject poolRootGo = new GameObject($"ConfigPool_{config.asset.Replace('/', '_')}"); @@ -422,7 +488,15 @@ namespace AlicizaX { var prefabRefInfo = LoadedPrefabs[assetPath]; GameObject instantiate = GameObject.Instantiate(prefabRefInfo.Prefab); - var pooledObj = new PooledObject(instantiate, assetPath); + + var pooledObj = MemoryPool.Acquire(); + 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; @@ -435,6 +509,7 @@ namespace AlicizaX } monitor.Initialize(this, pooledObj); + pooledObj.monitor = monitor; return pooledObj; } @@ -478,10 +553,19 @@ namespace AlicizaX else { // 使用LINQ找到最旧的未使用对象(更高效) - var oldestObj = AllObjects - .Where(obj => !obj.isActive) - .OrderBy(obj => obj.lastUsedTime) - .FirstOrDefault(); + PooledObject oldestObj = null; + foreach (var obj in AllObjects) + { + if (obj.isActive) + { + continue; + } + + if (oldestObj == null || obj.lastUsedTime < oldestObj.lastUsedTime) + { + oldestObj = obj; + } + } if (oldestObj != null) { @@ -543,10 +627,11 @@ namespace AlicizaX if (!pooledObj.isRefCountReduced) { OnObjectReallyDestroyed(pooledObj); + MemoryPool.Release(pooledObj); } else { - // 只需要从集合中移除 + // DestroyPooledObject 已调用 Detach,此分支理论上不可达 AllObjects.Remove(pooledObj); CleanAvailableQueue(pooledObj); } @@ -618,8 +703,11 @@ namespace AlicizaX if (pooledObj.gameObject != null) { + pooledObj.monitor?.Detach(); GameObject.Destroy(pooledObj.gameObject); } + + MemoryPool.Release(pooledObj); } public void CheckExpiredObjects() @@ -645,10 +733,18 @@ namespace AlicizaX } // 重建所有路径的可用队列 - foreach (var kvp in AvailableObjectsByPath.ToList()) + _availableObjectPaths.Clear(); + foreach (var assetPath in AvailableObjectsByPath.Keys) { - var assetPath = kvp.Key; - var queue = kvp.Value; + _availableObjectPaths.Add(assetPath); + } + + foreach (var assetPath in _availableObjectPaths) + { + if (!AvailableObjectsByPath.TryGetValue(assetPath, out var queue)) + { + continue; + } _tempQueue.Clear(); while (queue.Count > 0) @@ -707,8 +803,11 @@ namespace AlicizaX { if (obj.gameObject != null) { + obj.monitor?.Detach(); GameObject.Destroy(obj.gameObject); } + + MemoryPool.Release(obj); } AllObjects.Clear(); @@ -759,6 +858,12 @@ namespace AlicizaX _pooledObject = pooledObject; } + public void Detach() + { + _pool = null; + _pooledObject = null; + } + private void OnDestroy() { if (_pool != null && _pooledObject != null) @@ -806,6 +911,7 @@ namespace AlicizaX private List _poolConfigs; private List _configPools; private Dictionary _gameObjectToPool; + private Dictionary _configPoolCache; // 重用预加载对象列表 private static readonly List _preloadedObjects = new List(); @@ -836,6 +942,7 @@ namespace AlicizaX _configPools = new List(); _gameObjectToPool = new Dictionary(); + _configPoolCache = new Dictionary(); try { @@ -906,13 +1013,31 @@ namespace AlicizaX // Editor专用的刷新。 private void UpdateInspectorInfo() { - poolInfos.Clear(); + if (_configPools == null) + { + ReleaseInspectorInfos(0); + return; + } + + int poolIndex = 0; foreach (var pool in _configPools) { - var info = new ConfigPoolInfo(); + ConfigPoolInfo info; + if (poolIndex < poolInfos.Count) + { + info = poolInfos[poolIndex]; + } + else + { + info = MemoryPool.Acquire(); + poolInfos.Add(info); + } + info.UpdateFromPool(pool); - poolInfos.Add(info); + poolIndex++; } + + ReleaseInspectorInfos(poolIndex); } public void SetResourceLoader(IResourceLoader resourceLoader) @@ -1037,14 +1162,36 @@ namespace AlicizaX private ConfigPool FindConfigPool(string assetPath) { + if (string.IsNullOrEmpty(assetPath)) + { + return null; + } + + if (_configPools == null) + { + return null; + } + + if (_configPoolCache == null) + { + _configPoolCache = new Dictionary(); + } + + 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; } @@ -1058,13 +1205,35 @@ namespace AlicizaX public void ClearAllPools() { - foreach (var pool in _configPools) + if (_configPools != null) { - pool.Clear(); + foreach (var pool in _configPools) + { + pool.Clear(); + } } - _gameObjectToPool.Clear(); - poolInfos.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() @@ -1175,7 +1344,7 @@ namespace AlicizaX { public static GameObject LoadGameObject(string assetPath, Transform parent = null) { - return GameObjectPool.Instance.GetGameObject(assetPath); + return GameObjectPool.Instance.GetGameObject(assetPath, parent); } public static async UniTask LoadGameObjectAsync(string assetPath, Transform parent = null, CancellationToken cancellationToken = default)