Files

143 lines
4.9 KiB
C#

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 : IDisposable
{
private readonly bool _isPersistent;
private readonly Dictionary<string, AsyncOperationHandle> _cache = new();
private readonly List<AsyncOperationHandle> _uncachedHandles = new();
public SceneResourceLoader(bool isPersistent = false)
{
_isPersistent = isPersistent;
}
public void Dispose()
{
ReleaseAll();
}
public bool IsPersistent => _isPersistent;
/// <summary>
/// 异步加载资源(带缓存,同 Key 不重复加载)。
/// 返回的 Handle 可在协程中 yield return。
/// </summary>
public AsyncOperationHandle<T> LoadAsync<T>(string key) where T : Object
{
if (_cache.TryGetValue(key, out var cached) && cached.IsValid())
return cached.Convert<T>();
var handle = Addressables.LoadAssetAsync<T>(key);
_cache[key] = handle;
handle.Completed += op =>
{
if (op.Status != AsyncOperationStatus.Succeeded)
Debug.LogError($"[SceneResourceLoader] Failed to load '{key}': {op.OperationException}");
};
return handle;
}
/// <summary>
/// 异步加载资源(回调风格,带缓存)。
/// </summary>
public void LoadAsync<T>(string key, Action<T> onComplete, Action<string> onError = null) where T : Object
{
var handle = LoadAsync<T>(key);
handle.Completed += op =>
{
if (op.Status == AsyncOperationStatus.Succeeded)
onComplete?.Invoke(op.Result);
else
onError?.Invoke($"Failed to load '{key}': {op.OperationException}");
};
}
/// <summary>
/// 异步加载资源(不走缓存,每次产生独立 Handle)。
/// 适用于 Timeline 等需要独立生命周期管理的资源。
/// </summary>
public AsyncOperationHandle<T> LoadUncachedAsync<T>(string key) where T : Object
{
var handle = Addressables.LoadAssetAsync<T>(key);
_uncachedHandles.Add(handle);
handle.Completed += op =>
{
if (op.Status != AsyncOperationStatus.Succeeded)
Debug.LogError($"[SceneResourceLoader] Failed to load '{key}': {op.OperationException}");
};
return handle;
}
/// <summary>
/// 按 Label 批量预加载资源。返回的 Handle 可在协程中 yield return。
/// </summary>
public AsyncOperationHandle<IList<T>> PreloadByLabel<T>(string label) where T : Object
{
var handle = Addressables.LoadAssetsAsync<T>(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;
}
/// <summary>
/// 提前释放指定 Key 的缓存资源。
/// </summary>
public void EarlyRelease(string key)
{
if (_cache.TryGetValue(key, out var handle))
{
if (handle.IsValid())
Addressables.Release(handle);
_cache.Remove(key);
}
}
/// <summary>
/// 提前释放指定的非缓存 Handle。
/// </summary>
public void EarlyRelease<T>(AsyncOperationHandle<T> handle)
{
if (handle.IsValid())
{
Addressables.Release(handle);
_uncachedHandles.Remove(handle);
}
}
/// <summary>
/// 释放所有持有的资源 Handle。由 SceneLoader 在卸载场景前调用,或 OnDestroy 时自动调用。
/// </summary>
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;
}
}