using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using Object = UnityEngine.Object; namespace AibisDream.Framework { public class SceneResourceLoader : MonoBehaviour { [SerializeField] private bool isPersistent; private readonly Dictionary _cache = new(); private readonly List _uncachedHandles = new(); private void Awake() { if (isPersistent) ResourceSystem.RegisterPersistentLoader(this); else ResourceSystem.RegisterSceneLoader(this); } private void OnDestroy() { ReleaseAll(); if (isPersistent) ResourceSystem.UnregisterPersistentLoader(this); else ResourceSystem.UnregisterSceneLoader(this); } /// /// 异步加载资源(带缓存,同 Key 不重复加载)。 /// 返回的 Handle 可在协程中 yield return。 /// public AsyncOperationHandle LoadAsync(string key) where T : Object { if (_cache.TryGetValue(key, out var cached) && cached.IsValid()) return cached.Convert(); var handle = Addressables.LoadAssetAsync(key); _cache[key] = handle; handle.Completed += op => { if (op.Status != AsyncOperationStatus.Succeeded) Debug.LogError($"[SceneResourceLoader] Failed to load '{key}': {op.OperationException}"); }; return handle; } /// /// 异步加载资源(回调风格,带缓存)。 /// public void LoadAsync(string key, Action onComplete, Action onError = null) where T : Object { var handle = LoadAsync(key); handle.Completed += op => { if (op.Status == AsyncOperationStatus.Succeeded) onComplete?.Invoke(op.Result); else onError?.Invoke($"Failed to load '{key}': {op.OperationException}"); }; } /// /// 异步加载资源(不走缓存,每次产生独立 Handle)。 /// 适用于 Timeline 等需要独立生命周期管理的资源。 /// public AsyncOperationHandle LoadUncachedAsync(string key) where T : Object { var handle = Addressables.LoadAssetAsync(key); _uncachedHandles.Add(handle); handle.Completed += op => { if (op.Status != AsyncOperationStatus.Succeeded) Debug.LogError($"[SceneResourceLoader] Failed to load '{key}': {op.OperationException}"); }; return handle; } /// /// 按 Label 批量预加载资源。返回的 Handle 可在协程中 yield return。 /// public AsyncOperationHandle> PreloadByLabel(string label) where T : Object { var handle = Addressables.LoadAssetsAsync(label, null); _uncachedHandles.Add(handle); handle.Completed += op => { if (op.Status != AsyncOperationStatus.Succeeded) Debug.LogError($"[SceneResourceLoader] Failed to preload label '{label}': {op.OperationException}"); }; return handle; } /// /// 提前释放指定 Key 的缓存资源。 /// public void EarlyRelease(string key) { if (_cache.TryGetValue(key, out var handle)) { if (handle.IsValid()) Addressables.Release(handle); _cache.Remove(key); } } /// /// 提前释放指定的非缓存 Handle。 /// public void EarlyRelease(AsyncOperationHandle handle) { if (handle.IsValid()) { Addressables.Release(handle); _uncachedHandles.Remove(handle); } } /// /// 释放所有持有的资源 Handle。由 SceneLoader 在卸载场景前调用,或 OnDestroy 时自动调用。 /// public void ReleaseAll() { foreach (var kvp in _cache) { if (kvp.Value.IsValid()) Addressables.Release(kvp.Value); } _cache.Clear(); foreach (var handle in _uncachedHandles) { if (handle.IsValid()) Addressables.Release(handle); } _uncachedHandles.Clear(); } public int CachedCount => _cache.Count; public int UncachedCount => _uncachedHandles.Count; public int TotalHandleCount => _cache.Count + _uncachedHandles.Count; } }