This commit is contained in:
陈思海 2026-03-26 13:30:57 +08:00
parent 9756674342
commit 722dc7c251
3 changed files with 62 additions and 87 deletions

View File

@ -9,6 +9,14 @@ namespace AlicizaX
{ {
public sealed class GameObjectPool : MonoServiceBehaviour<GameObjectPool> public sealed class GameObjectPool : MonoServiceBehaviour<GameObjectPool>
{ {
private static readonly Comparison<GameObjectPoolSnapshot> SnapshotComparer = (left, right) =>
{
if (left == null && right == null) return 0;
if (left == null) return 1;
if (right == null) return -1;
int groupCompare = string.Compare(left.group, right.group, StringComparison.Ordinal);
return groupCompare != 0 ? groupCompare : string.Compare(left.assetPath, right.assetPath, StringComparison.Ordinal);
};
[Header("检查间隔")] [Header("检查间隔")]
public float checkInterval = 10f; public float checkInterval = 10f;
@ -25,6 +33,7 @@ namespace AlicizaX
private readonly Dictionary<string, RuntimePrefabPool> _poolsByKey = new Dictionary<string, RuntimePrefabPool>(StringComparer.Ordinal); private readonly Dictionary<string, RuntimePrefabPool> _poolsByKey = new Dictionary<string, RuntimePrefabPool>(StringComparer.Ordinal);
private readonly Dictionary<string, PoolConfig> _resolvedConfigCache = new Dictionary<string, PoolConfig>(StringComparer.Ordinal); private readonly Dictionary<string, PoolConfig> _resolvedConfigCache = new Dictionary<string, PoolConfig>(StringComparer.Ordinal);
private readonly Dictionary<string, PoolResourceLoaderType> _groupLoaderCache = new Dictionary<string, PoolResourceLoaderType>(StringComparer.Ordinal);
private readonly Dictionary<GameObject, RuntimePrefabPool> _ownersByObject = new Dictionary<GameObject, RuntimePrefabPool>(); private readonly Dictionary<GameObject, RuntimePrefabPool> _ownersByObject = new Dictionary<GameObject, RuntimePrefabPool>();
private readonly Dictionary<PoolResourceLoaderType, IResourceLoader> _resourceLoaders = new Dictionary<PoolResourceLoaderType, IResourceLoader>(); private readonly Dictionary<PoolResourceLoaderType, IResourceLoader> _resourceLoaders = new Dictionary<PoolResourceLoaderType, IResourceLoader>();
private readonly List<PoolConfig> _configs = new List<PoolConfig>(); private readonly List<PoolConfig> _configs = new List<PoolConfig>();
@ -36,6 +45,8 @@ namespace AlicizaX
private Exception _initializationException; private Exception _initializationException;
private float _lastCleanupTime; private float _lastCleanupTime;
private bool _isShuttingDown;
public bool IsReady => _initializationCompleted && _initializationException == null; public bool IsReady => _initializationCompleted && _initializationException == null;
protected override void OnServiceInitialize() protected override void OnServiceInitialize()
@ -202,15 +213,18 @@ namespace AlicizaX
public void ClearAllPools() public void ClearAllPools()
{ {
_isShuttingDown = true;
foreach (RuntimePrefabPool pool in _poolsByKey.Values) foreach (RuntimePrefabPool pool in _poolsByKey.Values)
{ {
pool.Shutdown(); pool.Shutdown();
MemoryPool.Release(pool); MemoryPool.Release(pool);
} }
_isShuttingDown = false;
_poolsByKey.Clear(); _poolsByKey.Clear();
_ownersByObject.Clear(); _ownersByObject.Clear();
_resolvedConfigCache.Clear(); _resolvedConfigCache.Clear();
_groupLoaderCache.Clear();
ReleaseDebugSnapshots(); ReleaseDebugSnapshots();
} }
@ -223,31 +237,7 @@ namespace AlicizaX
_debugSnapshots.Add(pool.CreateSnapshot()); _debugSnapshots.Add(pool.CreateSnapshot());
} }
_debugSnapshots.Sort((left, right) => _debugSnapshots.Sort(SnapshotComparer);
{
if (left == null && right == null)
{
return 0;
}
if (left == null)
{
return 1;
}
if (right == null)
{
return -1;
}
int groupCompare = string.Compare(left.group, right.group, StringComparison.Ordinal);
if (groupCompare != 0)
{
return groupCompare;
}
return string.Compare(left.assetPath, right.assetPath, StringComparison.Ordinal);
});
return _debugSnapshots; return _debugSnapshots;
} }
@ -264,7 +254,7 @@ namespace AlicizaX
internal void UnregisterOwnedObject(GameObject gameObject) internal void UnregisterOwnedObject(GameObject gameObject)
{ {
if (gameObject == null) if (gameObject == null || _isShuttingDown)
{ {
return; return;
} }
@ -375,7 +365,7 @@ namespace AlicizaX
{ {
if (!_resourceLoaders.ContainsKey(PoolResourceLoaderType.AssetBundle)) if (!_resourceLoaders.ContainsKey(PoolResourceLoaderType.AssetBundle))
{ {
_resourceLoaders[PoolResourceLoaderType.AssetBundle] = new AlicizaResourceLoader(); _resourceLoaders[PoolResourceLoaderType.AssetBundle] = new AssetBundleResourceLoader();
} }
if (!_resourceLoaders.ContainsKey(PoolResourceLoaderType.Resources)) if (!_resourceLoaders.ContainsKey(PoolResourceLoaderType.Resources))
@ -400,9 +390,11 @@ namespace AlicizaX
{ {
_configs.Clear(); _configs.Clear();
_resolvedConfigCache.Clear(); _resolvedConfigCache.Clear();
_groupLoaderCache.Clear();
IResourceModule resourceModule = ModuleSystem.GetModule<IResourceModule>();
PoolConfigScriptableObject configAsset = PoolConfigScriptableObject configAsset =
ModuleSystem.GetModule<IResourceModule>().LoadAsset<PoolConfigScriptableObject>(poolConfigPath); resourceModule.LoadAsset<PoolConfigScriptableObject>(poolConfigPath);
if (configAsset == null || configAsset.configs == null) if (configAsset == null || configAsset.configs == null)
{ {
@ -429,6 +421,17 @@ namespace AlicizaX
_configs.Sort(PoolConfig.CompareByPriority); _configs.Sort(PoolConfig.CompareByPriority);
LogConfigWarnings(); LogConfigWarnings();
for (int i = 0; i < _configs.Count; i++)
{
PoolConfig config = _configs[i];
if (!_groupLoaderCache.ContainsKey(config.group))
{
_groupLoaderCache[config.group] = config.resourceLoaderType;
}
}
resourceModule.UnloadAsset(configAsset);
} }
private async UniTask PrewarmConfiguredPoolsAsync(CancellationToken cancellationToken) private async UniTask PrewarmConfiguredPoolsAsync(CancellationToken cancellationToken)
@ -460,7 +463,8 @@ namespace AlicizaX
return null; return null;
} }
string cacheKey = $"{group ?? string.Empty}|{assetPath}"; bool hasGroup = !string.IsNullOrEmpty(group);
string cacheKey = hasGroup ? string.Concat(group, "|", assetPath) : assetPath;
if (_resolvedConfigCache.TryGetValue(cacheKey, out PoolConfig cachedConfig)) if (_resolvedConfigCache.TryGetValue(cacheKey, out PoolConfig cachedConfig))
{ {
return cachedConfig; return cachedConfig;
@ -516,16 +520,10 @@ namespace AlicizaX
private IResourceLoader GetDirectLoadResourceLoader(string group) private IResourceLoader GetDirectLoadResourceLoader(string group)
{ {
if (!string.IsNullOrWhiteSpace(group)) if (!string.IsNullOrWhiteSpace(group) &&
_groupLoaderCache.TryGetValue(group, out PoolResourceLoaderType loaderType))
{ {
for (int i = 0; i < _configs.Count; i++) return GetResourceLoader(loaderType);
{
PoolConfig config = _configs[i];
if (string.Equals(config.group, group, StringComparison.Ordinal))
{
return GetResourceLoader(config.resourceLoaderType);
}
}
} }
return GetResourceLoader(DefaultDirectLoadResourceLoaderType); return GetResourceLoader(DefaultDirectLoadResourceLoaderType);
@ -567,28 +565,14 @@ namespace AlicizaX
private void LogConfigWarnings() private void LogConfigWarnings()
{ {
var seen = new HashSet<(string group, string assetPath)>();
for (int i = 0; i < _configs.Count; i++) for (int i = 0; i < _configs.Count; i++)
{ {
PoolConfig left = _configs[i]; PoolConfig config = _configs[i];
for (int j = i + 1; j < _configs.Count; j++) var key = (config.group, config.assetPath);
if (!seen.Add(key))
{ {
PoolConfig right = _configs[j]; Log.Warning($"Duplicate pool config detected: '{config.group}:{config.assetPath}'.");
if (!string.Equals(left.group, right.group, StringComparison.Ordinal))
{
continue;
}
if (left.matchMode != right.matchMode)
{
continue;
}
if (!string.Equals(left.assetPath, right.assetPath, StringComparison.Ordinal))
{
continue;
}
Log.Warning($"Duplicate pool config detected: '{left.group}:{left.assetPath}'.");
} }
} }
} }

View File

@ -56,11 +56,12 @@ namespace AlicizaX
public void UnloadAsset(GameObject gameObject) public void UnloadAsset(GameObject gameObject)
{ {
Resources.UnloadAsset(gameObject); // Resources.UnloadAsset cannot unload GameObjects.
// The prefab reference is nulled by the caller; Unity handles cleanup via UnloadUnusedAssets.
} }
} }
public class AlicizaResourceLoader : IResourceLoader public class AssetBundleResourceLoader : IResourceLoader
{ {
private IResourceModule _resourceModule; private IResourceModule _resourceModule;

View File

@ -117,6 +117,14 @@ namespace AlicizaX
internal sealed class RuntimePrefabPool : IMemory internal sealed class RuntimePrefabPool : IMemory
{ {
private static readonly Comparison<GameObjectPoolInstanceSnapshot> InstanceSnapshotComparer = (left, right) =>
{
if (left == null && right == null) return 0;
if (left == null) return 1;
if (right == null) return -1;
if (left.isActive != right.isActive) return left.isActive ? -1 : 1;
return string.Compare(left.instanceName, right.instanceName, StringComparison.Ordinal);
};
private PoolConfig _config; private PoolConfig _config;
private string _assetPath; private string _assetPath;
private IResourceLoader _loader; private IResourceLoader _loader;
@ -124,6 +132,7 @@ namespace AlicizaX
private CancellationToken _shutdownToken; private CancellationToken _shutdownToken;
private Dictionary<GameObject, RuntimePooledInstance> _instancesByObject; private Dictionary<GameObject, RuntimePooledInstance> _instancesByObject;
private LinkedList<RuntimePooledInstance> _inactiveInstances; private LinkedList<RuntimePooledInstance> _inactiveInstances;
private List<RuntimePooledInstance> _shutdownBuffer;
private Transform _root; private Transform _root;
private GameObject _prefab; private GameObject _prefab;
@ -135,6 +144,7 @@ namespace AlicizaX
{ {
_instancesByObject = new Dictionary<GameObject, RuntimePooledInstance>(); _instancesByObject = new Dictionary<GameObject, RuntimePooledInstance>();
_inactiveInstances = new LinkedList<RuntimePooledInstance>(); _inactiveInstances = new LinkedList<RuntimePooledInstance>();
_shutdownBuffer = new List<RuntimePooledInstance>();
} }
public void Initialize( public void Initialize(
@ -221,12 +231,13 @@ namespace AlicizaX
instance.LastReleaseTime = Time.time; instance.LastReleaseTime = Time.time;
_activeCount = Mathf.Max(0, _activeCount - 1); _activeCount = Mathf.Max(0, _activeCount - 1);
gameObject.SetActive(false);
Transform transform = gameObject.transform; Transform transform = gameObject.transform;
transform.SetParent(_root, false); transform.SetParent(_root, false);
transform.localPosition = Vector3.zero; transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity; transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one; transform.localScale = Vector3.one;
gameObject.SetActive(false);
instance.InactiveNode = _inactiveInstances.AddLast(instance); instance.InactiveNode = _inactiveInstances.AddLast(instance);
TouchPrefab(); TouchPrefab();
@ -296,41 +307,19 @@ namespace AlicizaX
snapshot.instances.Add(instanceSnapshot); snapshot.instances.Add(instanceSnapshot);
} }
snapshot.instances.Sort((left, right) => snapshot.instances.Sort(InstanceSnapshotComparer);
{
if (left == null && right == null)
{
return 0;
}
if (left == null)
{
return 1;
}
if (right == null)
{
return -1;
}
if (left.isActive != right.isActive)
{
return left.isActive ? -1 : 1;
}
return string.Compare(left.instanceName, right.instanceName, StringComparison.Ordinal);
});
return snapshot; return snapshot;
} }
public void Shutdown() public void Shutdown()
{ {
var instances = new List<RuntimePooledInstance>(_instancesByObject.Values); _shutdownBuffer.AddRange(_instancesByObject.Values);
foreach (RuntimePooledInstance instance in instances) foreach (RuntimePooledInstance instance in _shutdownBuffer)
{ {
DestroyInstance(instance); DestroyInstance(instance);
} }
_shutdownBuffer.Clear();
_inactiveInstances.Clear(); _inactiveInstances.Clear();
_instancesByObject.Clear(); _instancesByObject.Clear();
@ -455,7 +444,7 @@ namespace AlicizaX
GameObject gameObject = instance.GameObject; GameObject gameObject = instance.GameObject;
if (parent != null) if (parent != null)
{ {
gameObject.transform.SetParent(parent); gameObject.transform.SetParent(parent, false);
} }
gameObject.SetActive(true); gameObject.SetActive(true);
@ -566,6 +555,7 @@ namespace AlicizaX
_shutdownToken = default; _shutdownToken = default;
_instancesByObject.Clear(); _instancesByObject.Clear();
_inactiveInstances.Clear(); _inactiveInstances.Clear();
_shutdownBuffer.Clear();
_root = null; _root = null;
_prefab = null; _prefab = null;
_prefabLoadSource = null; _prefabLoadSource = null;