17.AsyncOperationHandle拓展

17.AsyncOperationHandle


17.1 知识点

获取加载进度

AsyncOperationHandle.GetDownloadStatus 得到handle下载状态

DownloadStatus.Percent 下载进度

DownloadStatus.DownloadedBytes 当前下载了多少字节

DownloadStatus.TotalBytes 总共要下载多少字节

IEnumerator LoadAssetCoroutine()
{
    AsyncOperationHandle<GameObject> asyncOperationHandle = Addressables.LoadAssetAsync<GameObject>("Cube");

    while (!asyncOperationHandle.IsDone)
    {
        DownloadStatus downloadStatus = asyncOperationHandle.GetDownloadStatus();
        //进度
        print(downloadStatus.Percent);
        //字节加载进度 代表 AB包 加载了多少
        //当前下载了多少内容 /  总体有多少内容 单位是字节数
        print(downloadStatus.DownloadedBytes + "/" + downloadStatus.TotalBytes);
        yield return 0;
    }

    if (asyncOperationHandle.Status == AsyncOperationStatus.Succeeded)
    {
        Instantiate(asyncOperationHandle.Result);
    }
    else
        Addressables.Release(asyncOperationHandle);
}
StartCoroutine(LoadAssetCoroutine());

无类型句柄转换

有类型AsyncOperationHandle隐式转换成无类型AsyncOperationHandle

AsyncOperationHandle.Convert<T>() 无类型转有类型

AsyncOperationHandle<Texture2D> asyncOperationHandleTexture2D = Addressables.LoadAssetAsync<Texture2D>("Cube");

// 把有类型的泛型对象 转换为 无类型句柄
AsyncOperationHandle tempAsyncOperationHandle = asyncOperationHandleTexture2D;

// 把无类型句柄 转换为 有类型的泛型对象
asyncOperationHandleTexture2D = tempAsyncOperationHandle.Convert<Texture2D>();

//这样转换可以让我们的Addressables管理器字典中的值使用AsyncOperationHandle类型

强制同步加载资源

AsyncOperationHandle.WaitForCompletion 等待异步加载完成继续执行

//如果执行了WaitForCompletion 那么会卡主主线程 一定要当资源加载结束后
//才会继续往下执行
print("1");
asyncOperationHandleTexture2D.WaitForCompletion();//卡在这等待异步加载结束
print("2");
print(asyncOperationHandleTexture2D.Result.name);
//注意:
//Unity2020.1版本或者之前,执行该句代码不仅会等待该资源
//他会等待所有没有加载完成的异步加载加载完后才会继续往下执行
//Unity2020.2版本或以上版本,在加载已经下载的资源时性能影响会好一些
//所以,总体来说不建议大家使用这种方式加载资源

17.2 知识点代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class Lesson17_其他_资源加载相关_关于AsyncOperationHandle : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 获取加载进度

        // StartCoroutine(LoadAssetCoroutine());

        #endregion

        #region 知识点二 无类型句柄转换

        AsyncOperationHandle<Texture2D> asyncOperationHandleTexture2D = Addressables.LoadAssetAsync<Texture2D>("Cube");
        // 把有类型的泛型对象 转换为 无类型句柄
        AsyncOperationHandle tempAsyncOperationHandle = asyncOperationHandleTexture2D;
        // 把无类型句柄 转换为 有类型的泛型对象
        asyncOperationHandleTexture2D = tempAsyncOperationHandle.Convert<Texture2D>();
        
        //这样转换可以让我们的Addressables管理器字典中的值使用AsyncOperationHandle类型

        #endregion

        #region 知识点三 强制同步加载资源

        //如果执行了WaitForCompletion 那么会卡主主线程 一定要当资源加载结束后
        //才会继续往下执行
        print("1");
        asyncOperationHandleTexture2D.WaitForCompletion();//卡在这等待异步加载结束
        print("2");
        print(asyncOperationHandleTexture2D.Result.name);
        //注意:
        //Unity2020.1版本或者之前,执行该句代码不仅会等待该资源
        //他会等待所有没有加载完成的异步加载加载完后才会继续往下执行
        //Unity2020.2版本或以上版本,在加载已经下载的资源时性能影响会好一些
        //所以,总体来说不建议大家使用这种方式加载资源

        #endregion
    }

    IEnumerator LoadAssetCoroutine()
    {
        AsyncOperationHandle<GameObject> asyncOperationHandle = Addressables.LoadAssetAsync<GameObject>("Cube");

        //if (!handle.IsDone)
        //    yield return handle;

        //注意:如果该资源相关的AB包 已经加载过了 那么可能只进去一帧 只会打印0
        while (!asyncOperationHandle.IsDone)
        {
            DownloadStatus downloadStatus = asyncOperationHandle.GetDownloadStatus();
            //进度
            print(downloadStatus.Percent);
            //字节加载进度 代表 AB包 加载了多少
            //当前下载了多少内容 /  总体有多少内容 单位是字节数
            print(downloadStatus.DownloadedBytes + "/" + downloadStatus.TotalBytes);
            yield return 0;
        }

        if (asyncOperationHandle.Status == AsyncOperationStatus.Succeeded)
        {
            Instantiate(asyncOperationHandle.Result);
        }
        else
            Addressables.Release(asyncOperationHandle);
    }
}

17.3 练习题

修改之前练习题写的可寻址管理器,让Clear中可以清除所有字典中的对象

把字典类型改成Dictionary<string, AsyncOperationHandle>

修改强转代码

(AsyncOperationHandle)resDic[keyName] 改成 resDic[keyName].Convert()
(AsyncOperationHandle<IList>)resDic[keyName] 改成 resDic[keyName].Convert<IList>()

清空函数添加遍历清空逻辑

//清空资源
public void Clear()
{
    foreach (var item in resDic.Values)
    {
        Addressables.Release(item);
    }

    resDic.Clear();
    AssetBundle.UnloadAllAssetBundles(true);
    Resources.UnloadUnusedAssets();
    GC.Collect();
}

17.4 练习题代码

using System;
using System.Collections;
using System.Collections.Generic;
using BaseFramework;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

namespace BaseFramework
{
    public class AddressablesManager : BaseSingletonInMonoBehaviour<AddressablesManager>
    {
        //有一个容器 帮助我们存储 异步加载的返回值
        public Dictionary<string, AsyncOperationHandle> resDic = new Dictionary<string, AsyncOperationHandle>();


        //异步加载资源的方法
        public void LoadAssetAsync<T>(string name, Action<AsyncOperationHandle<T>> callBack)
        {
            //由于存在同名 不同类型资源的区分加载
            //所以我们通过名字和类型拼接作为 key
            string keyName = name + "_" + typeof(T).Name;
            AsyncOperationHandle<T> handle;
            //如果已经加载过该资源
            if (resDic.ContainsKey(keyName))
            {
                //获取异步加载返回的操作内容
                handle = resDic[keyName].Convert<T>();

                //判断 这个异步加载是否结束
                if (handle.IsDone)
                {
                    //如果成功 就不需要异步了 直接相当于同步调用了 这个委托函数 传入对应的返回值
                    callBack(handle);
                }
                //还没有加载完成
                else
                {
                    //如果这个时候 还没有异步加载完成 那么我们只需要 告诉它 完成时做什么就行了
                    handle.Completed += (obj) =>
                    {
                        if (obj.Status == AsyncOperationStatus.Succeeded)
                            callBack(obj);
                    };
                }

                return;
            }

            //如果没有加载过该资源
            //直接进行异步加载 并且记录
            handle = Addressables.LoadAssetAsync<T>(name);
            handle.Completed += (obj) =>
            {
                if (obj.Status == AsyncOperationStatus.Succeeded)
                    callBack(obj);
                else
                {
                    Debug.LogWarning(keyName + "资源加载失败");
                    if (resDic.ContainsKey(keyName))
                        resDic.Remove(keyName);
                }
            };
            resDic.Add(keyName, handle);
        }

        //释放资源的方法 
        public void Release<T>(string name)
        {
            //由于存在同名 不同类型资源的区分加载
            //所以我们通过名字和类型拼接作为 key
            string keyName = name + "_" + typeof(T).Name;
            if (resDic.ContainsKey(keyName))
            {
                //取出对象 移除资源 并且从字典里面移除
                AsyncOperationHandle<T> handle = resDic[keyName].Convert<T>();
                Addressables.Release(handle);
                resDic.Remove(keyName);
            }
        }

        //异步加载多个资源 或者 加载指定资源
        public void LoadAssetAsync<T>(Addressables.MergeMode mode, Action<T> callBack, params string[] keys)
        {
            //1.构建一个keyName  之后用于存入到字典中
            List<string> list = new List<string>(keys);
            string keyName = "";
            foreach (string key in list)
                keyName += key + "_";
            keyName += typeof(T).Name;
            //2.判断是否存在已经加载过的内容 
            //存在做什么
            AsyncOperationHandle<IList<T>> handle;
            if (resDic.ContainsKey(keyName))
            {
                handle = resDic[keyName].Convert<IList<T>>();
                //异步加载是否结束
                if (handle.IsDone)
                {
                    foreach (T item in handle.Result)
                        callBack(item);
                }
                else
                {
                    handle.Completed += (obj) =>
                    {
                        //加载成功才调用外部传入的委托函数
                        if (obj.Status == AsyncOperationStatus.Succeeded)
                        {
                            foreach (T item in handle.Result)
                                callBack(item);
                        }
                    };
                }

                return;
            }

            //不存在做什么
            handle = Addressables.LoadAssetsAsync<T>(list, callBack, mode);
            handle.Completed += (obj) =>
            {
                if (obj.Status == AsyncOperationStatus.Failed)
                {
                    Debug.LogError("资源加载失败" + keyName);
                    if (resDic.ContainsKey(keyName))
                        resDic.Remove(keyName);
                }
            };
            resDic.Add(keyName, handle);
        }

        public void LoadAssetAsync<T>(Addressables.MergeMode mode, Action<AsyncOperationHandle<IList<T>>> callBack,
            params string[] keys)
        {
        }

        public void Release<T>(params string[] keys)
        {
            //1.构建一个keyName  之后用于存入到字典中
            List<string> list = new List<string>(keys);
            string keyName = "";
            foreach (string key in list)
                keyName += key + "_";
            keyName += typeof(T).Name;

            if (resDic.ContainsKey(keyName))
            {
                //取出字典里面的对象
                AsyncOperationHandle<IList<T>> handle = resDic[keyName].Convert<IList<T>>();
                Addressables.Release(handle);
                resDic.Remove(keyName);
            }
        }


        //清空资源
        public void Clear()
        {
            foreach (var item in resDic.Values)
            {
                Addressables.Release(item);
            }

            resDic.Clear();
            AssetBundle.UnloadAllAssetBundles(true);
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏