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),开发者可以通过这个地址加载资源。
资源定位流程:
- 资源地址注册:在打包时,每个资源都会被分配一个唯一的地址
- 资源定位数据生成:打包时生成资源定位数据(Resource Location Data),记录每个资源的地址和位置信息
- 资源定位数据加载:在运行时,加载资源定位数据
- 资源地址解析:通过资源地址解析出资源的位置信息
- 资源加载:根据位置信息加载资源
资源定位数据详解:
资源定位数据(Resource Location Data)是 Addressables 的核心数据结构,记录了每个资源的地址和位置信息。资源定位数据包含以下关键信息:
- 地址(Address):资源的唯一地址,用于加载资源
- GUID:资源的唯一标识符,用于资源引用
- 路径(Path):资源在 AssetBundle 中的路径
- Bundle 名称:资源所在的 AssetBundle 名称
- 依赖列表:资源依赖的其他资源列表
三、Addressables 打包原理
3.1 打包流程
Addressables 的打包流程可以分为以下几个步骤:
- 资源分组:将资源组织成不同的分组(Group)
- 依赖分析:分析资源之间的依赖关系
- 资源打包:将每个分组打包成 AssetBundle 文件
- 清单生成:生成 Bundle Manifest 和 Addressable Asset Entry
- 资源定位数据生成:生成资源定位数据
3.2 依赖分析
Addressables 的依赖分析与 AssetBundle 类似,但更加自动化。Addressables 会自动分析资源之间的依赖关系,并生成依赖图。
依赖分析流程:
- 资源扫描:扫描每个分组中的所有资源
- 依赖提取:提取每个资源的直接依赖
- 依赖图构建:将资源之间的依赖关系构建为有向图
- 循环依赖检测:检测依赖图中是否存在循环依赖
- 共享资源提取:识别被多个分组共享的资源,提取为独立 Bundle
依赖分析的关键点:
- 自动依赖分析:Addressables 自动分析资源之间的依赖关系,不需要手动配置
- 共享资源处理:Addressables 自动处理共享资源,避免资源重复
- 循环依赖检测:Addressables 自动检测循环依赖并抛出错误
3.3 打包策略
Addressables 支持多种打包策略,开发者可以根据需求选择合适的策略:
| 打包策略 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| 单独打包 | 每个资源单独打包成一个 AB 文件 | 粒度细,更新灵活 | AB 文件数量多,管理复杂 |
| 分组打包 | 每个分组打包成一个 AB 文件 | 粒度适中,管理简单 | 更新不够灵活 |
| 类型打包 | 同一类型的资源打包成一个 AB 文件 | 便于类型管理 | 可能导致资源冗余 |
四、Addressables 加载原理
4.1 加载流程
Addressables 的加载流程可以分为以下几个步骤:
- 资源定位:通过资源地址解析出资源的位置信息
- 依赖加载:加载资源依赖的其他资源
- 资源加载:从 AssetBundle 中加载指定的资源
- 资源返回:将加载的资源返回给开发者
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 时,资源会被自动卸载。
引用计数机制的注意事项:
- 引用计数不会自动减少:如果资源被其他对象引用,引用计数不会自动减少
- 需要手动释放句柄 :开发者需要手动调用
Release()释放句柄 - 使用 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 等上层资源管理方案具有重要意义。
在实际项目中,建议:
- 合理选择分组策略:根据项目需求选择合适的分组策略
- 优化依赖关系:避免循环依赖,减少依赖层级
- 及时释放句柄:在合适的时机释放异步操作句柄
- 监控内存使用:使用 Profiler 等工具监控内存使用情况
- 使用异步加载:避免阻塞主线程,提升用户体验
通过深入理解 Addressables 的原理,我们可以更好地利用 Unity 的资源管理功能,开发出性能更优、体验更好的游戏。
下一篇 :资源打包流程详解