OK,这里我将总结Addressables的一些代码上的用法,众所周知,Addressables的用途就在于资源的热更、加载和卸载,等于是把我们之前工程对AssetBundle资源包的管理类AssetManager该干的事给干了
1.首先是资源的热更,这个绝大多数的解决方案都是在登录界面检查资源更新,然后下载更新到本地,那么在Addressables中如何实现这种热更呢?
先把这个Disable Catalog Update on Startup的选项给勾选,表示在应用启动的时候不自动更新我们的Catalog,而是在登录界面时候再更新这个Catalog来获取我们需要下载的资源,先看下我的代码吧:
public async Task CheckCatalogAsync(Action checkDoneCallback = null)
{
if(_isChecking)
return;
Debug.Log("check catalog begin");
_isChecking = true;
if(!_catalogHandle.IsValid())
_catalogHandle = Addressables.CheckForCatalogUpdates(false);
_catalogs = await _catalogHandle.Task;
Debug.Log("catalogs:"+_catalogs.Count);
if(_catalogs != null && _catalogs.Count > 0)
{
Debug.Log("Updatecatalogs begin");
List<object> keys = new List<object>();
var assets = await Addressables.UpdateCatalogs(_catalogs).Task;
for(int i =0;i<assets.Count;i++)
{
keys.AddRange(assets[i].Keys);
}
Debug.Log("check download begin :" + keys.Count);
await CheckDownloadAsync(keys,checkDoneCallback);
}
else
{
Addressables.Release(_catalogHandle);
await CheckDoneToGameAsync(checkDoneCallback);
}
}
private async Task CheckDownloadAsync(List<object> keys,Action checkDoneCallback = null)
{
downloadSize = await Addressables.GetDownloadSizeAsync(keys).Task;
Debug.Log("downloadsize:"+downloadSize);
if(downloadSize > 0)
{
string title = DataManager.GetText("Login_001");
string content = DataManager.GetText("Login_002", (Mathf.Ceil((float)downloadSize / (1024 * 1024))).ToString());
await UIManager.Instance.CreateDialogAsync(title,content,
confirmCallback: async ()=>{
await UIManager.Instance.InstantiateDownloadProgressAsync();
await DownloadAsync(keys,checkDoneCallback);
},
cancelCallback: ()=>{
_isChecking = false;
});
}
else
{
await CheckDoneToGameAsync(checkDoneCallback);
}
}
private async Task DownloadAsync(List<object> keys,Action checkDoneCallback = null)
{
Debug.Log("dowload begin");
_downloadHandle = Addressables.DownloadDependenciesAsync(keys,Addressables.MergeMode.Union);
while(!_downloadHandle.IsDone)
{
UIManager.Instance.ShowDownloadProgress(_downloadHandle.GetDownloadStatus().Percent);
await Task.Delay(TimeSpan.FromSeconds(Time.deltaTime));
}
Debug.Log("download finish");
if(_downloadHandle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log("download success!");
Addressables.Release(_catalogHandle);
Addressables.Release(_downloadHandle);
await CheckDoneToGameAsync(checkDoneCallback);
}
else
{
Debug.LogError("download failed! error:"+_downloadHandle.OperationException.ToString());
await UIManager.Instance.CreateDialogAsync("Login_001","Login_003",
confirmCallback:async ()=>{
await DownloadAsync(keys,checkDoneCallback);
});
}
}
如代码所示,我做了三件事:检查catalog的更新->检查需要下载的资源大小并弹窗显示->确定下载之后开始下载,那么这里面首先需要注意的是:
if(!_catalogHandle.IsValid())
_catalogHandle = Addressables.CheckForCatalogUpdates(false);
这里我做了个缓存,是因为有些需求是点击下载弹窗之外的区域会隐藏或销毁这个弹窗,再点击登录界面又会弹出下载弹窗,有了这个缓存就少了检查catalog这一步,还有CheckForCatalogUpdate这个方法里务必要填入false这个参数,因为不管是yield return还是await,如果不填入false这个参数check完都会被自动释放,导致你后续拿不到相关的数据,这个大家可以通过vs看到代码提示里,有这个autoReleaseHandle参数的都最好心里掂量一下,这个你后续是否还有操作某些数据的必要,是的话务必填入false,然后后续自己再手动卸载释放掉
比如我这里是在下载成功后进行的释放:
然后就是我们大家还会遇到一个问题,我在写这些代码的时候应该是还没解决,那就是断点续传,我在使用Addressables的时候发现一旦更新下载好了catalog,即使我第一次下载了一部分资源然后杀掉程序再打开,会发现Addressables会后台下载,但是它不会走我上面的代码流程进行弹窗继续下载之类的流程,所以我还加了以下这段代码来解决这个问题:
public async Task InitAsync()
{
_catalogPath = Application.persistentDataPath +"/com.unity.addressables";
if(Directory.Exists(_catalogPath))
{
try
{
Directory.Delete(_catalogPath,true);
Debug.Log("delete catalog cache done!");
}
catch(Exception e)
{
Debug.LogError(e.ToString());
}
}
await Addressables.InitializeAsync().Task;
_isInitDone = true;
Debug.Log("AssetManager init done");
}
没错,我们只需要在Addressables初始化之前删掉catalog所在的文件夹,那么它就可以进行断点续传了,这也是为什么第一章节,我们要设置Cache Initialization Settings为我们的persistentDataPath,如果现在Addressables已经有更优雅的解决方案了烦请评论区告知我一声哈,先谢过了~
2.资源的加载与卸载
资源的加载用的最多的就两个方法:LoadAssetAsync以及InstantiateAsync,有时候会用上LoadAssetsAsync,这个方法可以加入一个回调函数,非常好用,举个例子:
private async Task LoadTextsAsync(List<string> paths)
{
var handle = Addressables.LoadAssetsAsync<TextAsset>(paths,ParseText,Addressables.MergeMode.Union,true);
await handle.Task;
Addressables.Release(handle);
}
简单来说,就是它会每加载完paths里面的一个path资源,就会执行一次ParseText,相信聪明的你一定马上就明白它适用于哪些场景了吧
资源的卸载:Unity官方给出的就两个,ReleaseInstance和Release,第一个比较常用于InstantiateAsync出来的GameObject,Release常用于卸载Load出来的资源,不过我试过用Release卸载InstantiateAsync的句柄handle也是OK的,有时候有一些依赖的资源被加载进来无法通过Release来从内存中卸载掉,这个时候调用Resources.UnloadUnusedAssets可能会解决你的烦恼
还有一些高级的用法:比如group里面的Labels,它可以让你对资源进行分类,非常好用,打个比方:你可能会根据手机的性能给它安排不同的图片分辨率,那么你可以通过Labels来分成低清、普清、高清这三类来加载
最后附上Addressables的官方文档:Addressables | Addressables | 1.19.18