编写管理器,封装资源的加载和卸载等功能,因为管理器唯一,所以采用单例模式开发。
简单写一个饿汉单例(可优化)
public class Singlenton<T> where T : new ()
{
private static T _instance;
public static T Instance
{
get
{
if (_instance == null) { _instance = new T(); };
return _instance;
}
}
protected Singlenton() { Init(); }
protected virtual void Init() { }
}
Init函数提供给继承的管理器去做一些初始化操作,protected 的构造函数防止外部去声明对象,破坏唯一性
【主要内容】
由于Addressables加载的资源返回的是一个泛型句柄,AsyncOperationHandle,故可以声明一个为他的接口IEnumerator类型的字典,存储所有的句柄,采用字典类型,key为资源名和资源类型,即为
//字典容器,存储异步加载返回的句柄
private Dictionary<string, IEnumerator> HandleDic;
protected override void Init()
{
HandleDic = new Dictionary<string, IEnumerator>();
}
但普通类型的句柄,即AsyncOperationHandle提供了 public AsyncOperationHandle Convert();转为泛型类型的句柄,所以可以将IEnumerator改为AsyncOperationHandle,又由于有引用计数的需求,我们需要模拟构造一个类似引用计数的变量,所以声明类AddressableInfo,并提供两个成员变量
public class AddressableInfo {
public AsyncOperationHandle handle;
public uint Count;
public AddressableInfo(AsyncOperationHandle handle)
{
this.handle = handle;
Count += 1;
}
}
同时更改字典类型为AddressableInfo
//字典容器,存储异步加载返回的句柄
private Dictionary<string, AddressableInfo> HandleDic;
protected override void Init()
{
HandleDic = new Dictionary<string, AddressableInfo>();
}
为是否屏蔽引用计数,提供一个引用计数模式的枚举,用于规范卸载指定资源的模式
//指定释放模式,once采用引用计数模式, Complete屏蔽该模式
public enum AddressableReleaseType
{
Once,
Complete
}
【提供API】
从基本需求考虑,需要一个加载指定资源函数,一个释放指定资源函数,一个清空函数,即LoadAssetasync(),Release(),Clear(),
//第二个参数传一个委托函数,可以添加实例化资源等等
public void LoadAssetasync<T>(string name,Action<AsyncOperationHandle<T>> callBack)
{
//资源名拼接资源类型作为唯一Key
string keyName = name + "_" + typeof(T).Name;
if (HandleDic.ContainsKey(keyName))
{
var handle=HandleDic[keyName].handle.Convert<T>();
//引用计数加1
HandleDic[keyName].Count += 1;
//如果资源加载完成,则执行回调
if (handle.IsDone)
{
callBack(handle);
}
else
{
//如果这个时候 还没有异步加载完成 则添加委托 当完成时执行回调
handle.Completed += (obj) => {
if (obj.Status == AsyncOperationStatus.Succeeded)
callBack(obj);
};
}
return;
}
//第一次加载资源时,字典还没有对应句柄
var Handle= Addressables.LoadAssetAsync<T>(name);
Handle.Completed +=(obj)=> {
//资源加载成功时,指定回调
if (obj.Status.Equals(AsyncOperationStatus.Succeeded))
{
callBack(obj);
}
else
{
Debug.LogWarning(keyName + "资源加载失败");
if (HandleDic.ContainsKey(keyName))
HandleDic.Remove(keyName);
}
};
AddressableInfo info = new AddressableInfo(Handle);
//第一次添加对应句柄的键值对
HandleDic.Add(keyName, info);
}
//释放资源的方法, 参数2为是否启用引用计数,默认启用
public void Release<T>(string name,AddressableReleaseType type = AddressableReleaseType.Once)
{
string keyName = name + "_" + typeof(T).Name;
if (HandleDic.ContainsKey(keyName))
{
//如果是使用引用计数,则仅当引用计数为0时,释放资源
if (type.Equals(AddressableReleaseType.Once)) {
HandleDic[keyName].Count -= 1;
if (HandleDic[keyName].Count >0) return;
}
HandleDic[keyName].Count = 0;
AsyncOperationHandle<T> handle = HandleDic[keyName].handle.Convert<T>();
Addressables.Release(handle);
HandleDic.Remove(keyName);
}
}
//清空资源
public void Clear()
{
foreach (var item in HandleDic.Values)
{
Addressables.Release(item.handle);
}
HandleDic.Clear();
AssetBundle.UnloadAllAssetBundles(true);
Resources.UnloadUnusedAssets();
GC.Collect();
}
诸此类推,可以增加多资源加载和卸载api,例如想使用资源名加标签名引用多个资源
基本思路和上述API相同,故不再做多解释,加载模式的局别请自行了解
//指定加载多个资源,指定模式加载
public void LoadAssetasync<T>(Addressables.MergeMode mode, Action<T> callback,params string[] keys)
{
//List
List<string> list = new List<string>(keys);
string keyname = "";
foreach (var item in list)
{
keyname += item + "_";
}
keyname += typeof(T).Name;
AsyncOperationHandle<IList<T>> handle;
if (HandleDic.ContainsKey(keyname)) {
handle = HandleDic[keyname].handle.Convert<IList<T>>();
HandleDic[keyname].Count +=1;
if (handle.IsDone)
{
foreach (var item in handle.Result)
{
callback(item);
}
}
else
{
handle.Completed += (obj) => {
if(obj.Status== AsyncOperationStatus.Succeeded)
{
foreach (var item in handle.Result)
{
callback(item);
}
}
};
}
return;
}
handle = Addressables.LoadAssetsAsync<T>(list, callback, mode);
handle.Completed += (obj) =>
{
if (obj.Status.Equals(AsyncOperationStatus.Failed))
{
Debug.LogWarning(keyname + "资源加载失败");
if (HandleDic.ContainsKey(keyname))
HandleDic.Remove(keyname);
}
};
AddressableInfo info = new AddressableInfo(handle);
HandleDic.Add(keyname, info);
}
//释放多个资源的方法
public void Release<T>(AddressableReleaseType type = AddressableReleaseType.Once,params string[] keys)
{
//List
List<string> list = new List<string>(keys);
string keyname = "";
foreach (var item in list)
{
keyname += item + "_";
}
keyname += typeof(T).Name;
if (HandleDic.ContainsKey(keyname))
{
if (type.Equals(AddressableReleaseType.Once))
{
HandleDic[keyname].Count -= 1;
if (HandleDic[keyname].Count > 0) return;
}
HandleDic[keyname].Count = 0;
AsyncOperationHandle<T> handle = HandleDic[keyname].handle.Convert<T>();
Addressables.Release(handle);
HandleDic.Remove(keyname);
}
}
菜鸡整理的学习资料,如有不足,请大佬多指教
本文介绍了如何使用单例模式创建一个资源管理器,管理Addressables加载的异步操作,包括使用字典存储资源句柄、模拟引用计数的AddressableInfo类,以及提供加载、释放和清除资源的API接口,适用于Unity游戏开发。
2382





