7.动态加载释放多个资源

7.资源加载基础-动态加载多个资源


7.1 知识点

根据资源名或标签名加载多个对象

Addressables.LoadAssetsAsync 传入一个Key异步加载多个资源

  • 参数一:资源名或标签名
  • 参数二:加载结束后的回调函数 参数就是我们加载出来的资源
  • 参数三:如果为true表示当资源加载失败时,会自动将已加载的资源和依赖都释放掉;如果为false,需要自己手动来管理释放。默认为false
//回调函数处理资源obj 
Addressables.LoadAssetsAsync<Object>("Cube", (obj) =>
{
    //一个obj是一个资源 有多个同名资源会执行多次
    print("回调函数处理资源" + obj.name); //有多少个资源名叫Cube的资源就会执行多少次
});

//asyncOperationHandle完成事件中处理资源handle.Result
//如果要进行资源释放管理 那么我们需要使用这种方式 要方便一些
//因为我们得到了handle返回值对象 就可以释放资源了 但是我们不能对第一种方式的Object对象释放资源
AsyncOperationHandle<IList<Object>> asyncOperationHandle = Addressables.LoadAssetsAsync<Object>("Red", (obj) =>
{
    //print(obj.name);
});
asyncOperationHandle.Completed += (handle) =>
{
    // handle.Result是资源集合 要自己手动遍历
    foreach (var item in handle.Result)
    {
        print("asyncOperationHandle完成时间中处理资源" + item.name);
    }

    //释放资源
    Addressables.Release(handle);
};

根据多种信息加载对象

Addressables.LoadAssetsAsync 传入Key列表异步加载多个资源

  • 参数一:想要加载资源的条件列表(资源名、Lable名)
  • 参数二:每个加载资源结束后会调用的函数,会把加载到的资源传入该函数中
  • 参数三:可寻址的合并模式,用于合并请求结果的选项。
    • 如果键(Cube,Red)映射到结果([1,2,3],[1,3,4]),数字代表不同的资源
    • 比如可以认为123是名为Cube的资源 134是标签为Red的资源
    • None:不发生合并,将使用第一组结果 结果为[1,2,3]
    • UseFirst:应用第一组结果 结果为[1,2,3]
    • Union:合并所有结果 结果为[1,2,3,4]
    • Intersection:使用相交结果 结果为[1,3]
  • 参数四:如果为true表示当资源加载失败时,会自动将已加载的资源和依赖都释放掉。如果为false,需要自己手动来管理释放
List<string> keyList = new List<string>() { "Cube", "Red" }; //如果相交但是又找不到会报错,因为标签可以有多个,所以Key也可以多传几个标签名
Addressables.LoadAssetsAsync<Object>(keyList, (obj) => { print("根据多种信息加载对象" + obj.name); },
    Addressables.MergeMode.Intersection);

加载时的注意事项

  1. 重复加载不会报错,但是没有对应的Key会报错,或者Key相交为空会报错。
  2. 我们还是可以通过泛型类型,来筛选资源类型。比如有三个资源名为Cube的资源 但是有两个是GameObject且模型不一样 一个是贴图 可以传入泛型限制。如果三个都想加载 传入Unity的Object基类

总结

  • 可以根据资源名或标签名+资源类型加载所有满足条件的对象
  • 可以根据资源名+标签名+资源类型+合并模式加载指定的单个或者多个对象

7.2 知识点代码

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

public class Lesson07_资源加载基础_动态加载多个资源 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 根据资源名或标签名加载多个对象

        //Addressables.LoadAssetsAsync 传入一个Key加载多个资源
        //重复加载不会报错 但是没有对应的Key会报错
        
        //参数一:资源名或标签名
        //参数二:加载结束后的回调函数 参数就是我们加载出来的资源
        //参数三:如果为true表示当资源加载失败时,会自动将已加载的资源和依赖都释放掉;如果为false,需要自己手动来管理释放。默认为false

        //注意:我们还是可以通过泛型类型,来筛选资源类型
        //比如有三个资源名为Cube的资源 但是有两个是GameObject且模型不一样 一个是贴图 可以传入泛型限制
        //如果三个都加载 传入Unity的Object基类

        //回调函数处理资源obj 
        Addressables.LoadAssetsAsync<Object>("Cube", (obj) =>
        {
            //一个obj是一个资源 有多个同名资源会执行多次
            print("回调函数处理资源" + obj.name); //有多少个资源名叫Cube的资源就会执行多少次
        });

        //asyncOperationHandle完成事件中处理资源handle.Result
        //如果要进行资源释放管理 那么我们需要使用这种方式 要方便一些
        //因为我们得到了handle返回值对象 就可以释放资源了 但是我们不能对第一种方式的Object对象释放资源
        AsyncOperationHandle<IList<Object>> asyncOperationHandle = Addressables.LoadAssetsAsync<Object>("Red", (obj) =>
        {
            //print(obj.name);
        });
        asyncOperationHandle.Completed += (handle) =>
        {
            // handle.Result是资源集合 要自己手动遍历
            foreach (var item in handle.Result)
            {
                print("asyncOperationHandle完成时间中处理资源" + item.name);
            }

            //释放资源
            Addressables.Release(handle);
        };

        #endregion

        #region 知识点二 根据多种信息加载对象

        //Addressables.LoadAssetsAsync 传入Key列表加载多个资源
        //重复加载不会报错 但是没有对应的Key会报错或者Key相交为空会报错

        //参数一:想要加载资源的条件列表(资源名、Lable名)
        //参数二:每个加载资源结束后会调用的函数,会把加载到的资源传入该函数中
        //参数三:可寻址的合并模式,用于合并请求结果的选项。
            //如果键(Cube,Red)映射到结果([1,2,3],[1,3,4]),数字代表不同的资源
            //比如可以认为123是名为Cube的资源 134是标签为Red的资源
            //None:不发生合并,将使用第一组结果 结果为[1,2,3]
            //UseFirst:应用第一组结果 结果为[1,2,3]
            //Union:合并所有结果 结果为[1,2,3,4]
            //Intersection:使用相交结果 结果为[1,3]
        //参数四:如果为true表示当资源加载失败时,会自动将已加载的资源和依赖都释放掉。如果为false,需要自己手动来管理释放
        
        //注意:我们还是可以通过泛型类型,来筛选资源类型
        
        List<string> keyList = new List<string>() { "Cube", "Red" }; //如果相交但是又找不到会报错,因为标签可以有多个,所以Key也可以多传几个标签名
        Addressables.LoadAssetsAsync<Object>(keyList, (obj) => { print("根据多种信息加载对象" + obj.name); },
            Addressables.MergeMode.Intersection);

        #endregion

        #region 总结

        //1.可以根据 资源名或标签名+资源类型 来加载所有满足条件的对象
        //2.可以根据 资源名+标签名+资源类型+合并模式 来加载指定的单个或者多个对象

        #endregion
    }
}

7.3 练习题

尝试在上一个Addressables资源管理器中,提供一个批量或指定加载释放资源的方法

基础使用回顾

List<string> strs = new List<string>() { "Cube", "HD" };
Addressables.LoadAssetsAsync<Object>(strs, (obj) => {
   print(obj.name);
}, Addressables.MergeMode.Intersection);

实现传入合并模式和变长Key的加载资源函数

//异步加载多个资源 或者 加载指定资源
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 = (AsyncOperationHandle<IList<T>>)resDic[keyName];
        //异步加载是否结束
        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)
{
}

实现释放资源函数,释放字典中存储的handle对象

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 = (AsyncOperationHandle<IList<T>>)resDic[keyName];
        Addressables.Release(handle);
        resDic.Remove(keyName);
    }
}

进行测试

AddressablesManager.Instance.LoadAssetAsync<Object>(Addressables.MergeMode.Union, (obj) =>
{
    print("1" + obj.name);
}, "Cube", "Red");

AddressablesManager.Instance.LoadAssetAsync<Object>(Addressables.MergeMode.Intersection, (obj) =>
{
    print("2" + obj.name);
}, "Cube", "Red");

// AddressablesManager.Instance.Release<Object>("Cube", "Red");

小bug

应该要把Addressables.MergeMode也进行keyName拼接 不然不同模式下得到不同的资源 但是key相同 都进行回调


7.4 练习题代码

AddressablesManager

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, IEnumerator> resDic = new Dictionary<string, IEnumerator>();

        //异步加载资源的方法
        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 = (AsyncOperationHandle<T>)resDic[keyName];

                //判断 这个异步加载是否结束
                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 = (AsyncOperationHandle<T>)resDic[keyName];
                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 = (AsyncOperationHandle<IList<T>>)resDic[keyName];
                //异步加载是否结束
                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 = (AsyncOperationHandle<IList<T>>)resDic[keyName];
                Addressables.Release(handle);
                resDic.Remove(keyName);
            }
        }


        //清空资源
        public void Clear()
        {
            resDic.Clear();
            AssetBundle.UnloadAllAssetBundles(true);
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }
    }
}

Lesson07_练习题

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

public class Lesson07_练习题 : MonoBehaviour
{
    void Start()
    {
        //尝试在上一个Addressables资源管理器中
        //提供一个批量或指定加载释放资源的方法
        
        //基础使用回顾
        //List<string> strs = new List<string>() { "Cube", "HD" };
        //Addressables.LoadAssetsAsync<Object>(strs, (obj) => {
        //    print(obj.name);
        //}, Addressables.MergeMode.Intersection);

        AddressablesManager.Instance.LoadAssetAsync<Object>(Addressables.MergeMode.Union, (obj) =>
        {
            print("1" + obj.name);
        }, "Cube", "Red");

        AddressablesManager.Instance.LoadAssetAsync<Object>(Addressables.MergeMode.Intersection, (obj) =>
        {
            print("2" + obj.name);
        }, "Cube", "Red");
        
        // AddressablesManager.Instance.Release<Object>("Cube", "Red");
        
        //其实是有bug的 应该要把Addressables.MergeMode也进行keyName拼接 不然不同模式下得到不同的资源 但是key相同 都进行回调
    }
}


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

×

喜欢就点赞,疼爱就打赏