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

0 阅读10分钟

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 的整体架构可以分为以下几个层次:

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:

// 异步加载单个资源
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 内存优化技巧

资源共享

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

资源池

// 使用对象池管理频繁创建和销毁的资源
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 的对比

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

七、总结

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

在实际项目中,建议:

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

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


下一篇资源打包流程详解