02-02-原理篇-Unity Addressable Assets原理深度解析

Unity Addressable Assets 原理深度解析

篇章 :02-原理篇 · 基础 阅读时间 :约 40 分钟 前置知识:了解 Unity 基本资源加载方式


一、引言

Addressable Assets System(简称 Addressables)是 Unity 官方提供的资源管理系统,旨在解决 AssetBundle 使用复杂、依赖管理繁琐等问题。理解 Addressables 的工作原理,对于掌握 YooAsset 等上层资源管理方案具有重要意义。

Addressables 是 Unity 在 2018.1 版本中引入的一个实验性包,经过多个版本的迭代,已经成为 Unity 推荐的资源管理方案。它基于 AssetBundle 构建,但提供了更高级的 API 和更自动化的依赖管理。

Addressables 的核心设计理念是"可寻址"(Addressable),即为每个资源分配一个唯一的字符串地址,开发者可以通过这个地址加载资源,而不需要关心资源的具体位置和依赖关系。这种设计大大简化了资源管理的复杂度。

Addressables 的设计哲学与 YooAsset 有诸多相似之处,两者都采用了"可寻址"的设计理念,但实现方式有所不同。YooAsset 在 Addressables 的基础上进行了改进,提供了更灵活的分包策略和更强大的热更新能力。


二、Addressables 架构设计

2.1 整体架构

Addressables 的整体架构可以分为以下几个层次:

sql 复制代码
Addressables 架构
├── API 层
│   ├── AddressableAssetAPI:统一的资源加载 API
│   ├── AddressableAssetSettings:全局配置
│   └── AddressableAssetGroup:资源分组管理
├── 资源分组层
│   ├── Static Group:静态分组
│   ├── Dynamic Group:动态分组
│   └── Composite Group:组合分组
├── 资源打包层
│   ├── AssetBundle Builder:资源打包器
│   ├── Bundle Manifest:Bundle 清单
│   └── Addressable Asset Entry:资源条目
└── 资源加载层
    ├── Resource Provider:资源提供者
    ├── Resource Location Data:资源定位数据
    └── Async Operation Handle:异步操作句柄

API 层 :API 层是 Addressables 与开发者交互的接口,提供了统一的资源加载 API。开发者通过 Addressables.LoadAssetAsync<T>() 等方法加载资源,而不需要关心资源的具体位置和依赖关系。

资源分组层:资源分组层负责将资源组织成不同的分组(Group),每个分组可以独立打包、更新和加载。Addressables 支持静态分组、动态分组和组合分组三种分组方式。

资源打包层:资源打包层负责将资源打包成 AssetBundle 文件,并生成 Bundle Manifest 和 Addressable Asset Entry。Bundle Manifest 记录了所有 Bundle 的依赖关系,Addressable Asset Entry 记录了每个资源的地址和位置信息。

资源加载层:资源加载层负责从 AssetBundle 中加载资源,并管理资源的生命周期。Resource Provider 是资源加载的核心组件,负责从不同的数据源(如 AssetBundle、Resources 目录等)加载资源。

2.2 Provider 系统详解

Provider 是 Addressables 的核心插件化组件,每种资源类型都有对应的 Provider:

Provider 类型 功能 适用场景
Asset Provider 加载 Asset 资源 模型、贴图、预制体等
Scene Provider 加载场景 场景资源
Atlas Provider 加载 Sprite Atlas 图集资源
Bundle Provider 加载 AssetBundle 底层 Bundle 文件

Provider 系统的设计允许开发者自定义资源加载逻辑。例如,可以实现一个自定义的 HTTP Provider 来支持特殊的下载策略。

2.3 ResourceManager 工作原理

ResourceManager 是 Addressables 的核心管理器,它负责:

  • 资源的加载和卸载
  • 引用计数管理
  • 缓存管理
  • 异步操作调度

ResourceManager 使用引用计数来管理资源的生命周期。每次加载资源时引用计数加 1,每次调用 Release() 时引用计数减 1。当引用计数为 0 时,资源才会被真正卸载。

2.4 异步操作体系

Addressables 的异步操作体系基于 AsyncOperationHandle 构建:

  • AsyncOperationHandle:异步操作的句柄,用于跟踪操作状态和结果
  • AsyncOperationHandle.Completed:操作完成事件
  • AsyncOperationHandle.Status:操作状态(Succeeded、Failed、WaitingForDependency 等)

2.5 资源分组机制

Addressables 使用分组(Group)来组织资源,每个分组可以独立打包、更新和加载。Addressables 支持以下分组方式:

分组类型 描述 优点 缺点
Static Group 静态分组,资源在打包时确定 打包速度快,管理简单 不够灵活
Dynamic Group 动态分组,资源在运行时确定 灵活,支持运行时添加资源 打包速度慢
Composite Group 组合分组,由多个子分组组成 灵活,支持复杂分组策略 管理复杂

静态分组详解

静态分组是 Addressables 最常用的分组方式。在静态分组中,所有资源在打包时就已经确定,打包完成后不能再添加或移除资源。静态分组的优点是打包速度快,管理简单,适合大多数项目。

动态分组详解

动态分组允许在运行时动态添加或移除资源,适合需要动态加载资源的项目。动态分组的缺点是打包速度慢,管理复杂,适合小型项目或原型开发。

组合分组详解

组合分组由多个子分组组成,可以将多个子分组合并为一个逻辑分组。组合分组的优点是灵活,支持复杂分组策略,缺点是管理复杂,适合大型项目。

2.6 资源定位机制

Addressables 使用资源定位(Location)机制来管理资源的地址和位置信息。每个资源都有一个唯一的地址(Address),开发者可以通过这个地址加载资源。

资源定位流程

  1. 资源地址注册:在打包时,每个资源都会被分配一个唯一的地址
  2. 资源定位数据生成:打包时生成资源定位数据(Resource Location Data),记录每个资源的地址和位置信息
  3. 资源定位数据加载:在运行时,加载资源定位数据
  4. 资源地址解析:通过资源地址解析出资源的位置信息
  5. 资源加载:根据位置信息加载资源

资源定位数据详解

资源定位数据(Resource Location Data)是 Addressables 的核心数据结构,记录了每个资源的地址和位置信息。资源定位数据包含以下关键信息:

  • 地址(Address):资源的唯一地址,用于加载资源
  • GUID:资源的唯一标识符,用于资源引用
  • 路径(Path):资源在 AssetBundle 中的路径
  • Bundle 名称:资源所在的 AssetBundle 名称
  • 依赖列表:资源依赖的其他资源列表

三、Addressables 打包原理

3.1 打包流程

Addressables 的打包流程可以分为以下几个步骤:

  1. 资源分组:将资源组织成不同的分组(Group)
  2. 依赖分析:分析资源之间的依赖关系
  3. 资源打包:将每个分组打包成 AssetBundle 文件
  4. 清单生成:生成 Bundle Manifest 和 Addressable Asset Entry
  5. 资源定位数据生成:生成资源定位数据

3.2 依赖分析

Addressables 的依赖分析与 AssetBundle 类似,但更加自动化。Addressables 会自动分析资源之间的依赖关系,并生成依赖图。

依赖分析流程

  1. 资源扫描:扫描每个分组中的所有资源
  2. 依赖提取:提取每个资源的直接依赖
  3. 依赖图构建:将资源之间的依赖关系构建为有向图
  4. 循环依赖检测:检测依赖图中是否存在循环依赖
  5. 共享资源提取:识别被多个分组共享的资源,提取为独立 Bundle

依赖分析的关键点

  1. 自动依赖分析:Addressables 自动分析资源之间的依赖关系,不需要手动配置
  2. 共享资源处理:Addressables 自动处理共享资源,避免资源重复
  3. 循环依赖检测:Addressables 自动检测循环依赖并抛出错误

3.3 打包策略

Addressables 支持多种打包策略,开发者可以根据需求选择合适的策略:

打包策略 描述 优点 缺点
单独打包 每个资源单独打包成一个 AB 文件 粒度细,更新灵活 AB 文件数量多,管理复杂
分组打包 每个分组打包成一个 AB 文件 粒度适中,管理简单 更新不够灵活
类型打包 同一类型的资源打包成一个 AB 文件 便于类型管理 可能导致资源冗余

四、Addressables 加载原理

4.1 加载流程

Addressables 的加载流程可以分为以下几个步骤:

  1. 资源定位:通过资源地址解析出资源的位置信息
  2. 依赖加载:加载资源依赖的其他资源
  3. 资源加载:从 AssetBundle 中加载指定的资源
  4. 资源返回:将加载的资源返回给开发者

4.2 加载 API

Addressables 提供了多种加载 API:

csharp 复制代码
// 异步加载单个资源
var handle = Addressables.LoadAssetAsync<GameObject>("MyPrefab");
handle.Completed += (op) => {
    GameObject prefab = op.Result;
    Instantiate(prefab);
};

// 异步加载多个资源
var handles = Addressables.LoadAssetsAsync<GameObject>("MyPrefab", null);
handles.Completed += (op) => {
    foreach (var prefab in op.Result) {
        Instantiate(prefab);
    }
};

// 异步加载场景
var sceneHandle = Addressables.LoadSceneAsync("MyScene");
sceneHandle.Completed += (op) => {
    if (op.Status == AsyncOperationStatus.Succeeded) {
        Debug.Log("场景加载成功");
    }
};

API 详解

  • LoadAssetAsync<T>():异步加载单个资源
  • LoadAssetsAsync<T>():异步加载多个资源
  • LoadSceneAsync():异步加载场景
  • InstantiateAsync():异步实例化资源
  • UnloadAsync():异步卸载资源

4.3 异步操作句柄

Addressables 使用异步操作句柄(Async Operation Handle)来管理异步操作的生命周期。每个异步操作都会返回一个句柄,开发者可以通过句柄来管理异步操作。

异步操作句柄的关键方法

  • Completed:异步操作完成时的回调
  • Result:异步操作的结果
  • Status:异步操作的状态
  • WaitForCompletion():等待异步操作完成(同步阻塞)
  • Release():释放异步操作句柄

五、Addressables 内存管理

5.1 引用计数机制

Addressables 使用引用计数机制来管理资源的生命周期。每次调用 LoadAssetAsync<T>() 时,引用计数加 1。每次调用 Release() 时,引用计数减 1。当引用计数为 0 时,资源会被自动卸载。

引用计数机制的注意事项

  1. 引用计数不会自动减少:如果资源被其他对象引用,引用计数不会自动减少
  2. 需要手动释放句柄 :开发者需要手动调用 Release() 释放句柄
  3. 使用 using 语句:建议使用 using 语句自动释放句柄

5.2 内存优化技巧

资源共享

csharp 复制代码
// 多个地方共享同一个资源
var handle1 = Addressables.LoadAssetAsync<GameObject>("MyPrefab");
var handle2 = Addressables.LoadAssetAsync<GameObject>("MyPrefab");
// handle1 和 handle2 共享同一个资源对象

资源池

csharp 复制代码
// 使用对象池管理频繁创建和销毁的资源
public class ResourcePool<T> where T : Object
{
    private Queue<T> _pool = new Queue<T>();
    
    public T Get()
    {
        if (_pool.Count > 0)
            return _pool.Dequeue();
        return null;
    }
    
    public void Put(T obj)
    {
        _pool.Enqueue(obj);
    }
}

5.3 缓存机制

Addressables 内建了资源缓存系统:

  • 内存缓存:已加载的资源对象缓存在内存中
  • 磁盘缓存:从网络下载的资源缓存在磁盘上
  • 缓存策略:支持自定义缓存过期策略

六、Addressables 与 AssetBundle 的对比

特性 AssetBundle Addressables
依赖管理 手动管理 自动管理
资源定位 通过 Bundle 名称和资源名称 通过唯一地址
加载 API 复杂,需要手动处理依赖 简单,统一 API
内存管理 手动管理引用计数 自动管理引用计数
热更新 需要手动实现 内置支持
学习曲线 陡峭 平缓
灵活性
性能 中(有额外开销)

七、总结

Addressables 是 Unity 官方提供的资源管理系统,基于 AssetBundle 构建,但提供了更高级的 API 和更自动化的依赖管理。理解 Addressables 的工作原理,对于掌握 YooAsset 等上层资源管理方案具有重要意义。

在实际项目中,建议:

  1. 合理选择分组策略:根据项目需求选择合适的分组策略
  2. 优化依赖关系:避免循环依赖,减少依赖层级
  3. 及时释放句柄:在合适的时机释放异步操作句柄
  4. 监控内存使用:使用 Profiler 等工具监控内存使用情况
  5. 使用异步加载:避免阻塞主线程,提升用户体验

通过深入理解 Addressables 的原理,我们可以更好地利用 Unity 的资源管理功能,开发出性能更优、体验更好的游戏。


下一篇资源打包流程详解

相关推荐
甲维斯17 小时前
0代码,0建模,3句话开发一个3D游戏!
前端·游戏·游戏开发
_zhourui_h_18 小时前
Unity 内存数据极限防护:一个 int 藏 8 份密文,修改器到底还能怎么改?
unity3d
SmalBox1 天前
02-01-原理篇-Unity原生AssetBundle原理深度解析
unity3d·游戏开发
fujisheng6611 天前
FUI 验证实战:从 Prefab 节点改名到生成诊断与构建门禁
unity3d
_zhourui_h_2 天前
Unity AssetBundle 打包极限治理:依赖环、重复资源、包体膨胀,怎么在上线前全部揪出来?
unity3d
_zhourui_h_3 天前
Unity AssetBundle 极限管理:依赖、异步加载、引用计数、自动卸载到底怎么串起来?
unity3d
SmalBox3 天前
01-09-认知篇-对比-方案选型矩阵
unity3d·游戏开发
鑫鑫哥adam4 天前
用一个 struct 给游戏关键数值上锁:ProtectedInt 反作弊实践
unity3d
fujisheng6614 天前
FUI 导航实践:拆开 Layer、History、Coverage 与 Cache 的组合语义
c#·unity3d