2024-12-06 Unity Addressables3——资源加载

  • Unity 版本:6000.0.26f1c1
  • Addressables 版本:2.3.1

1 引用加载

1.1 Addressables 的资源引用类

  • AssetReference:通用资源引用类,可加载任意类型资源。
  • AssetReferenceAtlasedSprite:图集资源引用类。
  • AssetReferenceGameObject:游戏对象资源引用类。
  • AssetReferenceSprite:精灵图片资源引用类。
  • AssetReferenceTexture:贴图资源引用类。
  • AssetReferenceT<>:指定类型引用类。

​ 通过申明不同类型引用类对象,可以在 Inspector 窗口中筛选关联的 Addressables 对象。

public class Lesson3 : MonoBehaviour
{
    public AssetReference              AssetReference;
    public AssetReferenceAtlasedSprite AssetReferenceAtlasedSprite;
    public AssetReferenceGameObject    AssetReferenceGameObject;
    public AssetReferenceSprite        AssetReferenceSprite;
    public AssetReferenceTexture       AssetReferenceTexture;

    public AssetReferenceT<AudioClip>                 AssetReferenceTAudioClip;
    public AssetReferenceT<RuntimeAnimatorController> AssetReferenceTRuntimeAnimatorController;
    public AssetReferenceT<TextAsset>                 AssetReferenceTTextAsset;
    public AssetReferenceT<Material>                  AssetReferenceTMaterialRed;
    public AssetReference                             AssetReferenceTScene;
}
image-20241206164715350

1.2 加载资源

注意:所有 Addressables 加载相关都使用异步加载。

需要引用命名空间:using UnityEngine.ResourceManagement.AsyncOperations;

public class Lesson3 : MonoBehaviour
{
    public AssetReference AssetReference;
    ...

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        // 1. 使用异步操作句柄
        AsyncOperationHandle<GameObject> handle = assetReference.LoadAssetAsync<GameObject>();
        handle.Completed += TestFun;
        
        // 2. 简化写法
        AssetReference.LoadAssetAsync<GameObject>().Completed += handle =>
        {
            // 判断是否加载成功
            // 建议使用传入参数
            if (handle.Status == AsyncOperationStatus.Succeeded)
            {
                var obj = Instantiate(handle.Result);
                obj.name = "Loaded GameObject";
            }
            
            // 不建议使用标识类创建
            // if(assetReference.IsDone)
            // {
            //    Instantiate(assetReference.Asset);
            // }
        };
    }

    private void TestFun(AsyncOperationHandle<GameObject> handle)
    {
        // 加载成功后,使用加载的资源嘛
        // 判断是否加载成功
        if(handle.Status == AsyncOperationStatus.Succeeded)
        {
            Instantiate(handle.Result);
        }
    }
}

1.3 加载场景

public class Lesson3 : MonoBehaviour
{
    public AssetReference AssetReferenceTScene; // 需要引用 Scene 资源
    ...

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        AssetReferenceTScene.LoadSceneAsync().Completed += (handle) =>
        {
           // 初始化场景的一些信息
           print("场景加载结束");
        };
    }
}

1.4 释放资源

        AssetReference.ReleaseAsset();

  1. 释放资源方法后,资源引用类中的资源会置空,但是 AsyncOperationHandle 类中的对象不为空。
  2. 释放资源不会影响场景中被实例化出来的对象,但是会影响使用的资源。
public class Lesson3 : MonoBehaviour
{
    public AssetReference AssetReference;
    ...

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        AssetReference.LoadAssetAsync<GameObject>().Completed += handle =>
        {
            if (handle.Status == AsyncOperationStatus.Succeeded)
            {
                var obj = Instantiate(handle.Result);
                obj.name = "Loaded GameObject";
                
                // 加载完成后,释放资源
                AssetReference.ReleaseAsset();
                    
                Debug.Log(handle.Result == null);        // false
                Debug.Log(AssetReference.Asset == null); // true
            }
        };
    }
}

2 Label 介绍

​ 引用加载必须在脚本中声明各种引用类来指定加载的资源,不够灵活,只适合做一些小项目。

​ 实际商业项目开发中,很多时候根据配置文件决定加载的资源,即动态加载。

​ 通过给定名称和标签,来加载指定资源。

image-20241206170645705

Label 作用举例:

  1. 游戏装备中有一顶帽子:Hat,拥有不同的品质,比如:红、绿、白、蓝。

    通过标签 Label 来区分,Label 分别是:Red、Green、White、Blue。

  2. 游戏中根据设备好坏选择不同质量的图片或者模型,比如:高清、标清、超清。

    但在不同标准下,这些模型的命名应该是相同的,Label 分别是:HD、SD、FHD。

  3. 游戏中逢年过节时更换模型和 UI 显示,比如:中秋节、春节、圣诞节。

    不同节日时角色或者 UI 等资源看起来是不同的,但资源的命名应该都遵循同样的规范。

    比如登录面板,在中秋节、春节、圣诞节时它的资源名都是登录面板,Label 分别可以是:MidAutumn、Spring、Christmas。

3 动态加载

  • 命名空间:

    UnityEngine.AddressableAssets

    UnityEngine.ResourceManagement.AsyncOperations

​ 以下是打包示例:

image-20241206172212626

3.1 加载单个资源

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

​        public static AsyncOperationHandle<TObject> LoadAssetAsync<TObject>(object key);

注意:

  1. 如果存在同名或同标签的同类型资源,会自动加载第一个满足条件的对象。
  2. 如果存在同名或同标签的不同类型资源,可以根据泛型类型来决定加载哪一个。

​ 以加载标签为 “Red” 的 GameObject 资源为例:

public class Lesson5 : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        Addressables.LoadAssetAsync<GameObject>("Red").Completed += handle =>
        {
            if (handle.Status == AsyncOperationStatus.Succeeded)
            {
                Instantiate(handle.Result);
            }
        };
    }
}

​ 以加载名称为 “Red” 的 GameObject 资源为例:

public class Lesson5 : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        Addressables.LoadAssetAsync<GameObject>("Cube").Completed += handle =>
        {
            if (handle.Status == AsyncOperationStatus.Succeeded)
            {
                Instantiate(handle.Result);
            }
        };
    }
}

(2)动态加载场景

public static AsyncOperationHandle<SceneInstance> LoadSceneAsync(

​        object key,

        LoadSceneMode loadMode = LoadSceneMode.Single,

        bool activateOnLoad = true,

​        int priority = 100,

​        SceneReleaseMode releaseMode = SceneReleaseMode.ReleaseSceneWhenSceneUnloaded

);

  • key:场景名。
  • loadMode:加载模式。
    • LoadSceneMode.Single:单独加载,只保留新加载的场景;
    • LoadSceneMode.Additive:叠加加载,两个场景一起显示(不常用)。
  • activateOnLoad:场景加载是否激活。如果为 false,加载完成后不会直接切换,需要自己使用返回值中的 ActivateAsync 方法。
  • priority:场景加载的异步操作优先级。
  • releaseMode:释放模式。
    • SceneReleaseMode.ReleaseSceneWhenSceneUnloaded:卸载场景时释放;
    • SceneReleaseMode.OnlyReleaseSceneOnHandleRelease:手动释放。

​ 例如,加载 SampleScene 场景并手动激活:

public class Lesson5 : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        Addressables.LoadSceneAsync("SampleScene", LoadSceneMode.Single, false, 100).Completed += (obj) =>
        {
            // 手动激活场景
            obj.Result.ActivateAsync();
        };
    }
}

(3)释放资源

        public static void Release<TObject>(AsyncOperationHandle<TObject> handle)

​ 释放资源时,需要记录加载后的 Handle。

public class Lesson5 : MonoBehaviour
{
    public AsyncOperationHandle<GameObject> Handle;

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        Handle = Addressables.LoadAssetAsync<GameObject>("Cube");

        Handle.Completed += operationHandle =>
        {
            if (operationHandle.Status == AsyncOperationStatus.Succeeded)
            {
                Instantiate(operationHandle.Result);

                // 加载完成后,释放资源
                Addressables.Release(Handle);
            }
        };
    }
}

注意:

​ 一定要保证资源使用完毕过后再释放资源。

​ 建议将 Play Mode Script 改为 “Use Existing Build” 测试,因为 “Use Asset Database” 模式下,不会真正地从 AB 包释放资源。

image-20241206173338294

3.2 加载多个资源

(1)重载一

public static AsyncOperationHandle<IList<TObject>> LoadAssetsAsync<TObject>(

        object key,

        Action<TObject> callback,

        bool releaseDependenciesOnFailure

);

  • key:资源名或标签名。
  • callback:每个加载资源结束后会调用的函数,会把加载到的资源传入该函数中。
  • releaseDependenciesOnFailure:为 true,当资源加载失败时自动将已加载的资源和依赖释放;为 false,需要自己手动来管理释放。
public class Lesson6 : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        var handle = Addressables.LoadAssetsAsync<GameObject>("Red", obj =>
        {
            // (1):应用于每个 GameObject,与 (2) 等价
            Debug.Log(obj.name);
        }, true);

        // (2):遍历 GameObject 列表,与 (1) 等价
        handle.Completed += (obj) =>
        {
            foreach (var o in obj.Result)
            {
                Debug.Log(o.name);
            }
        };
    }
}

(2)重载二

public static AsyncOperationHandle<IList<TObject>> LoadAssetsAsync<TObject>(

        IEnumerable keys,

        Action<TObject> callback,

        MergeMode mode,

        bool releaseDependenciesOnFailure

);

  • keys:想要加载资源的条件列表(资源名、Lable 名)。

  • callback:每个加载资源结束后会调用的函数,会把加载到的资源传入该函数中。

  • mode:可寻址的合并模式,用于合并请求结果的选项。

    如果键 “Cube”、“Red” 分别对应结果 [1, 2, 3]、[1, 3, 4](数字代表不同的资源):

    • MergeMode.None:不发生合并,将使用第一组结果 结果为 [1, 2, 3];
    • MergeMode.UseFirst:应用第一组结果,结果为 [1, 2, 3];
    • MergeMode.Union:合并所有结果,结果为 [1, 2, 3, 4];
    • MergeMode.Intersection:使用相交结果,结果为 [1, 3]。
  • releaseDependenciesOnFailure:为 true,当资源加载失败时自动将已加载的资源和依赖释放;为 false,需要自己手动来管理释放。

public class Lesson6 : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        Addressables.LoadAssetsAsync<Object>(
            new[] { "Cube", "Red" },
            obj => { Debug.Log(obj.name); }, // 打印结果:"Cube"
            Addressables.MergeMode.Intersection,
            true
        );
    }
}
### Unity 分包技术与流式加载实现方法及最佳实践 #### 资源分包策略 为了减少初始安装包大小以及提高资源管理灵活性,在Unity项目中采用合理的分包方案至关重要。通常的做法是按照功能模块或者场景来划分AssetBundle,这样可以确保只有当特定部分被请求时才下载对应的资产文件。 对于静态内容较多的应用程序来说,可以通过构建工具链自动生成依赖关系图谱,并据此规划哪些资源应该被打包在一起[^1]。此外,针对不同平台特性定制化打包也是提升用户体验的有效手段之一;例如移动设备上可能更倾向于细粒度的小型AB包以便按需增量更新。 #### 流式加载机制概述 面对大型复杂的游戏世界或应用环境,传统的整体预载入方式不仅消耗大量时间和存储空间,而且可能导致不必要的数据冗余。因此引入基于位置感知的渐进式加载成为解决之道——即仅读取玩家视野范围内可见的对象及其关联素材。 具体而言,可通过计算摄像机视锥体内的实体列表,动态调整优先级队列来进行异步拉取操作。与此同时配合LOD(Level of Detail)层次细节简化远处物体表现形式从而降低渲染压力[^2]。 #### 技术实施要点 - **使用Addressable Assets System (AAS)** AAS 是官方推荐的新一代资产管理框架,它允许开发者定义灵活多样的地址规则用于定位远程服务器上的二进制片段。借助其内置的任务调度器能够轻松完成跨域获取工作流程。 ```csharp using UnityEngine; using UnityEngine.AddressableAssets; public class AssetLoader : MonoBehaviour { async void Start() { var assetRef = Addressables.LoadAssetAsync<Sprite>("sprite_key"); await assetRef.Task; GetComponent<SpriteRenderer>().sprite = assetRef.Result; // Unload when no longer needed to free up memory. Addressables.Release(assetRef); } } ``` - **避免滥用 Resources 文件夹** 尽管 `Resources` API 提供了一种便捷途径访问嵌入式资料库中的项,但由于缺乏缓存控制且强制全量编译至最终产物内,故而应谨慎选用此路径作为主要发行渠道之外的选择[^3]。 - **定期检测并修正内存泄漏** 即便经过精心设计仍难以完全杜绝意外情况的发生,所以务必建立完善的监控体系捕捉异常波动现象。一旦发现疑似泄露点位则立即着手排查直至彻底消除隐患[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔗理苦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值