6.动态加载释放单个资源

6.资源加载基础-动态加载单个资源


6.1 知识点

通过资源名或标签名动态加载单个资源

  • 命名空间:UnityEngine.AddressableAssets 和 UnityEngine.ResourceManagement.AsyncOperations

Addressables.LoadAssetAsync 动态加载单个资源 传入的参数可以是资源名或标签名

// Addressables.LoadAssetAsync 动态加载单个资源 传入的参数可以是资源名或标签名
asyncOperationHandle = Addressables.LoadAssetAsync<GameObject>("Cube");
asyncOperationHandle.Completed += (handle) =>
{
    //判断加载成功
    if (handle.Status == AsyncOperationStatus.Succeeded)
        Instantiate(handle.Result);
};

一步到位的Addressables.LoadAssetAsync

Addressables.LoadAssetAsync<GameObject>("Red").Completed += (asyncOperationHandle) =>
{
   //判断加载成功
   if (asyncOperationHandle.Status == AsyncOperationStatus.Succeeded)
       Instantiate(asyncOperationHandle.Result);
};

注意:

  1. 重复加载相同资源不会报错,但是没有对应的Key会报错
  2. 如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
  3. 如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个

释放资源

Addressables.Release 要释放哪一个返回值

//直接写在这不合理 要加载完了再释放
//Addressables.Release(asyncOperationHandle);
asyncOperationHandle = Addressables.LoadAssetAsync<GameObject>("Cube");
asyncOperationHandle.Completed += (handle) =>
{
    //判断加载成功
    if (handle.Status == AsyncOperationStatus.Succeeded)
        Instantiate(handle.Result);

    //一定要是 加载完成后 使用完毕后 再去释放
    //不管任何资源 只要释放后 都会影响之前在使用该资源的对象 要在AB包模式下才能看出
    //所以要保证不再使用该资源后才释放
    //和通过标识加载不同 标识加载GameObjetct实例化后释放不受影响
    Addressables.Release(handle);
};
//在对象被删除后释放是较为合理的
private void OnDestroy()
{
    Addressables.Release(asyncOperationHandle);
}

动态加载场景

Addressables.LoadSceneAsync 动态加载场景

SceneInstance.ActivateAsync 手动激活异步加载的场景

//参数一:场景名 场景名是场景资源在Group下的名字
//参数二:加载模式 (叠加还是单独,叠加就是两个场景一起显示,单独就是只保留新加载的场景,正常情况为单独)
//参数三:场景加载是否激活,如果为false,加载完成后不会直接切换,需要自己使用返回值中的ActivateAsync方法
//参数四:场景加载的异步操作优先级 一般不填
Addressables.LoadSceneAsync("SampleScene", UnityEngine.SceneManagement.LoadSceneMode.Single, false)
    //给场景加载完成后添加回调
    .Completed += (handle) =>
    {
        Debug.Log("异步场景加载完成");

        //比如说 手动激活场景 异步激活后也可以添加完成回调 
        //加载场景的话handle.Result是SceneInstance
        handle.Result.ActivateAsync().completed += (a) =>
        {
            Debug.Log("异步激活场景完成");
            
            //然后再去创建场景上的对象

            //然后再去隐藏 加载界面

            //注意:场景资源也是可以释放的,并不会影响当前已经加载出来的场景,因为场景的本质只是配置文件
            Addressables.Release(handle);
        };
    };

总结

  1. 根据名字或标签加载单个资源相对之前的指定加载资源更加灵活
    • 主要通过Addressables类中的静态方法传入资源名或标签名进行加载
    • 注意:
      1. 如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
      2. 如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个
  2. 释放资源时需要传入之前记录的AsyncOperationHandle对象
    • 注意:一定要保证资源使用完毕过后再释放资源
  3. 场景异步加载可以自己手动激活加载完成的场景

6.2 知识点代码

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

public class Lesson06_资源加载基础_动态加载单个资源 : MonoBehaviour
{
    AsyncOperationHandle<GameObject> asyncOperationHandle;

    void Start()
    {
        #region 知识点一 通过资源名或标签名动态加载单个资源

        //命名空间:
        //UnityEngine.AddressableAssets 和 UnityEngine.ResourceManagement.AsyncOperations

        // Addressables.LoadAssetAsync 动态加载单个资源 传入的参数可以是资源名或标签名
        asyncOperationHandle = Addressables.LoadAssetAsync<GameObject>("Cube");
        asyncOperationHandle.Completed += (handle) =>
        {
            //判断加载成功
            if (handle.Status == AsyncOperationStatus.Succeeded)
                Instantiate(handle.Result);

            //一定要是 加载完成后 使用完毕后 再去释放
            //不管任何资源 只要释放后 都会影响之前在使用该资源的对象 要在AB包模式下才能看出
            //所以要保证不再使用该资源后才释放
            //和通过标识加载不同 标识加载GameObjetct实例化后释放不受影响
            Addressables.Release(handle);
        };

        //一步到位
        //Addressables.LoadAssetAsync<GameObject>("Red").Completed += (asyncOperationHandle) =>
        //{
        //    //判断加载成功
        //    if (asyncOperationHandle.Status == AsyncOperationStatus.Succeeded)
        //        Instantiate(asyncOperationHandle.Result);
        //};

        //注意:
        //1.如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
        //2.如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个

        #endregion

        #region 知识点二 释放资源

        //Addressables.Release 要释放哪一个返回值
        //写在这不合理 要加载完了在释放
        //Addressables.Release(asyncOperationHandle);

        #endregion

        #region 知识点三 动态加载场景

        // Addressables.LoadSceneAsync 动态加载场景 
        //参数一:场景名 场景名是场景资源在Group下的名字
        //参数二:加载模式 (叠加还是单独,叠加就是两个场景一起显示,单独就是只保留新加载的场景,正常情况为单独)
        //参数三:场景加载是否激活,如果为false,加载完成后不会直接切换,需要自己使用返回值中的ActivateAsync方法
        //参数四:场景加载的异步操作优先级
        Addressables.LoadSceneAsync("SampleScene", UnityEngine.SceneManagement.LoadSceneMode.Single, false)
            //给场景加载完成后添加回调
            .Completed += (handle) =>
        {
            Debug.Log("异步场景加载完成");
            //比如说 手动激活场景 异步激活后也可以添加完成回调
            handle.Result.ActivateAsync().completed += (a) =>
            {
                Debug.Log("异步激活场景完成");

                //然后再去创建场景上的对象

                //然后再去隐藏 加载界面

                //注意:场景资源也是可以释放的,并不会影响当前已经加载出来的场景,因为场景的本质只是配置文件
                Addressables.Release(handle);
            };
        };

        #endregion

        #region 知识点四 总结

        //1.根据名字或标签加载单个资源相对之前的指定加载资源更加灵活
        //  主要通过Addressables类中的静态方法传入资源名或标签名进行加载
        //  注意:
        //  1-1.如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
        //  1-2.如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个

        //2.释放资源时需要传入之前记录的AsyncOperationHandle对象
        //  注意:一定要保证资源使用完毕过后再释放资源

        //3.场景异步加载可以自己手动激活加载完成的场景

        #endregion
    }

    //在对象被删除后释放是较为合理的
    private void OnDestroy()
    {
        //Addressables.Release(asyncOperationHandle);
    }
}

6.3 练习题

尝试自己写一个Addressables资源管理器,帮助我们通过名字加载单个资源或场景,并管理资源相关内容

分析

  1. 资源动态加载可以通过返回的对象AsyncOperationHandle<>(异步操作句柄) 添加完成监听来处理加载后的资源
  2. 释放资源时也是通过释放返回的对象AsyncOperationHandle<>(异步操作句柄) 来进行释放
  3. 如果分散在各脚本中自己管理资源难免显得太过凌乱,所以我们可以通过一个资源管理器来管理所有的异步加载返回对象AsyncOperationHandle<>(异步操作句柄)
//正常单资源加载的写法
AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Cube");
handle.Completed += (obj) => {
   //加载成功后的逻辑处理

   Addressables.Release(obj);
};

创建AddressablesManager,实现单例

public class AddressablesManager : BaseSingletonInMonoBehaviour<AddressablesManager>
{

}

定义存储AsyncOperationHandle的容器

//有一个容器 帮助我们存储各个异步加载的返回值AsyncOperationHandle AsyncOperationHandle继承IEnumerator 所以把IEnumerator作为值方便里氏替换原则
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 Clear()
{
    resDic.Clear();
    AssetBundle.UnloadAllAssetBundles(true);
    Resources.UnloadUnusedAssets();
    GC.Collect();
}

进行测试

AddressablesManager.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
{
    Instantiate(obj.Result);
});

AddressablesManager.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
{
    Instantiate(obj.Result, Vector3.right * 5, Quaternion.identity);
    //使用完资源后 移除资源 注意非AB包模式下Cube还会存在
    AddressablesManager.Instance.Release<GameObject>("Cube");
});
        
// AddressablesManager.Instance.Clear();

6.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>
    {
        //有一个容器 帮助我们存储各个异步加载的返回值AsyncOperationHandle AsyncOperationHandle继承IEnumerator 所以把IEnumerator作为值方便里氏替换原则
        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 Clear()
        {
            resDic.Clear();
            AssetBundle.UnloadAllAssetBundles(true);
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }
    }
}

Lesson06_练习题

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

public class Lesson06_练习题 : MonoBehaviour
{

    void Start()
    {
        //尝试自己写一个Addressables资源管理器,帮助我们通过名字加载单个资源或场景,并管理资源相关内容

        //AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Cube");
        //handle.Completed += (obj) => {
        //    //加载成功后的逻辑处理

        //    Addressables.Release(obj);
        //};
        
        //分析:
        //1.资源动态加载可以通过返回的对象AsyncOperationHandle<>(异步操作句柄) 添加完成监听来处理加载后的资源
        //2.释放资源时也是通过释放返回的对象AsyncOperationHandle<>(异步操作句柄) 来进行释放
        //3.如果分散在各脚本中自己管理资源难免显得太过凌乱,所以我们可以通过一个资源管理器来管理所有的异步加载返回对象AsyncOperationHandle<>(异步操作句柄)

        //所以如果我们要自己写一个Addressables资源管理器,主要就是用来管理AsyncOperationHandle<>对象的

        AddressablesManager.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
        {
            Instantiate(obj.Result);
        });

        AddressablesManager.Instance.LoadAssetAsync<GameObject>("Cube", (obj) =>
        {
            Instantiate(obj.Result, Vector3.right * 5, Quaternion.identity);
            //使用完资源后 移除资源 注意非AB包模式下Cube还会存在
            AddressablesManager.Instance.Release<GameObject>("Cube");
        });
        
        // AddressablesManager.Instance.Clear();
    }
    
}


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

×

喜欢就点赞,疼爱就打赏