Unity资源热更-Addressables总结(二)

OK,这里我将总结Addressables的一些代码上的用法,众所周知,Addressables的用途就在于资源的热更、加载和卸载,等于是把我们之前工程对AssetBundle资源包的管理类AssetManager该干的事给干了

1.首先是资源的热更,这个绝大多数的解决方案都是在登录界面检查资源更新,然后下载更新到本地,那么在Addressables中如何实现这种热更呢?Unity资源热更-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,然后后续自己再手动卸载释放掉 

Unity资源热更-Addressables总结(二)

比如我这里是在下载成功后进行的释放:

Unity资源热更-Addressables总结(二)

 然后就是我们大家还会遇到一个问题,我在写这些代码的时候应该是还没解决,那就是断点续传,我在使用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

上一篇:Unity商业游戏底层资源加载框架(Unity2018.1)


下一篇:Unity开发笔记-Odin标签实现原理探究