02-03-原理篇-资源打包流程详解

0 阅读9分钟

资源打包流程详解

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


一、引言

资源打包是 Unity 资源管理流程的核心环节,它决定了资源如何被组织、压缩和分发。一个高效的打包流程可以显著减少构建时间、优化资源体积、提高资源复用率。本章将深入解析资源打包的完整流程,包括资源准备、依赖分析、打包规则选择和构建优化,结合 YooAsset 的实际实现进行详细说明。

资源打包不仅仅是将资源文件压缩成一个包那么简单。它涉及到资源的分类、依赖分析、共享资源处理、压缩策略选择等多个方面。理解这些原理,可以帮助开发者设计出更合理的资源管理方案。


二、资源打包概述

2.1 打包目标

资源打包的主要目标包括:

  • 减小安装包体积:通过压缩和去重,减小安装包体积
  • 实现按需加载:将资源分成多个小包,根据需要动态加载
  • 支持热更新:将频繁更新的资源单独打包,支持热更新
  • 优化加载性能:通过合理的分包策略,优化加载性能
  • 提高资源复用率:通过共享资源处理,减少资源冗余

2.2 打包流程

资源打包的完整流程如下:

资源打包流程
├── 1. 资源选择
│   ├── 选择需要打包的资源
│   └── 设置资源的 AssetBundle 标签
├── 2. 依赖分析
│   ├── 分析资源之间的依赖关系
│   └── 构建依赖图
├── 3. 资源分组
│   ├── 根据依赖关系分组
│   └── 处理共享资源
├── 4. 数据序列化
│   ├── 将资源数据序列化为二进制格式
│   └── 处理引用关系
├── 5. 压缩处理
│   ├── 根据配置的压缩算法压缩数据
│   └── 生成压缩后的数据块
├── 6. 文件组装
│   ├── 将压缩后的数据组装成 AssetBundle 文件
│   └── 生成资源清单和依赖信息
└── 7. 清单生成
    ├── 生成全局资源清单
    └── 生成依赖信息文件

2.3 打包系统架构

YooAsset 的打包系统由以下几个核心组件组成:

YooAsset 打包系统架构
├── AssetBundleCollector:资源收集器
│   ├── MainAssetCollector:主资源收集器
│   ├── StaticAssetCollector:静态资源收集器
│   └── DependAssetCollector:依赖资源收集器
├── BundlePackRule:打包规则
│   ├── PackSeparately:单独打包
│   ├── PackDirectory:目录打包
│   ├── PackTopDirectory:顶级目录打包
│   └── PackGroup:分组打包
├── BuildPipeline:构建管线
│   ├── EditorSimulateBuildPipeline:编辑器模拟构建
│   ├── BuiltinBuildPipeline:内置构建管线
│   ├── ScriptableBuildPipeline:可编程构建管线
│   └── RawFileBuildPipeline:原生文件构建管线
└── BuildOutput:构建输出
    ├── AssetBundle 文件
    ├── Manifest 文件
    └── 依赖信息文件

三、资源选择

3.1 资源选择策略

资源选择是打包的第一步,需要决定哪些资源参与打包。常见的资源选择策略包括:

  • 按路径选择:选择指定路径下的所有资源
  • 按类型选择:选择指定类型的资源
  • 按标签选择:选择具有特定标签的资源
  • 手动选择:手动选择需要打包的资源

3.2 资源收集器

YooAsset 提供了三种资源收集器:

收集器类型描述适用场景
MainAssetCollector主资源收集器,写入资源清单需要代码加载的资源
StaticAssetCollector静态资源收集器,参与打包但不写入清单定制化打包策略
DependAssetCollector依赖资源收集器,不写入清单未被引用时自动剔除

MainAssetCollector 详解

MainAssetCollector 是最常用的资源收集器。它会将收集的资源写入资源清单,开发者可以通过代码加载这些资源。MainAssetCollector 适用于需要动态加载的资源,如预制体、场景、纹理等。

StaticAssetCollector 详解

StaticAssetCollector 会将资源打包到 AssetBundle 中,但不会写入资源清单。这种收集器适用于需要定制化打包策略的资源,如 Shader、音频等。

DependAssetCollector 详解

DependAssetCollector 会将资源打包到 AssetBundle 中,但不会写入资源清单。当资源没有被其他资源引用时,它会被自动剔除。这种收集器适用于可选资源,如 DLC 内容。

3.3 资源标签

资源标签是用于标识资源所属 Bundle 的标记。在 YooAsset 中,可以通过设置资源的 AssetBundle 属性来指定其所属的 Bundle。

// 设置资源的 AssetBundle 标签
AssetImporter importer = AssetImporter.GetAtPath(assetPath);
importer.assetBundleName = "mybundle";
importer.assetBundleVariant = "bundle";

四、依赖分析

4.1 依赖分析原理

依赖分析是打包过程中最关键的一步。Unity 在打包时会递归地分析资源之间的依赖关系,确保所有依赖的资源都被正确打包。

依赖分析流程

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

依赖分析的关键点

  1. 递归分析:依赖分析是递归进行的,确保所有层级的依赖都被找到
  2. 去重处理:使用 HashSet 避免重复分析同一个资源
  3. 循环依赖检测:Unity 会检测循环依赖并抛出错误
  4. 外部依赖处理:对于不在当前 Bundle 中的依赖,需要单独处理

4.2 依赖图

依赖图是描述资源之间依赖关系的有向图。在依赖图中,每个节点代表一个资源,每条边代表一个依赖关系。

依赖图示例:
Prefab1 -> Texture1
Prefab1 -> Material1
Material1 -> Texture1
Prefab2 -> Texture1
Prefab2 -> Material1

依赖图:
Prefab1 --> Texture1
Prefab1 --> Material1 --> Texture1
Prefab2 --> Texture1
Prefab2 --> Material1

4.3 共享资源处理

当多个 Bundle 共享同一个资源时,需要将共享资源提取为独立的 Bundle,避免资源重复。

共享资源处理流程

  1. 识别共享资源:扫描所有 Bundle,识别被多个 Bundle 共享的资源
  2. 提取共享资源:将共享资源提取为独立的 Bundle
  3. 更新依赖关系:更新所有引用共享资源的 Bundle 的依赖关系

共享资源处理策略

策略描述优点缺点
提取为独立 Bundle将共享资源提取为独立 Bundle避免资源重复增加依赖关系
复制到每个 Bundle将共享资源复制到每个 Bundle简化依赖关系资源重复
合并到第一个 Bundle将共享资源合并到第一个 Bundle简化依赖关系第一个 Bundle 变大

五、数据序列化

5.1 序列化原理

序列化是将对象的状态转换为可以存储或传输的格式的过程。Unity 使用自定义的序列化格式来存储资源数据。

序列化流程

  1. 读取资源文件:读取原始资源文件
  2. 解析资源数据:解析资源文件中的数据
  3. 序列化数据:将资源数据序列化为二进制格式
  4. 写入 Bundle:将序列化后的数据写入 AssetBundle 文件

5.2 引用处理

在序列化过程中,需要处理资源之间的引用关系。Unity 使用引用索引来管理资源之间的引用关系。

引用处理流程

  1. 收集所有引用:收集资源中所有引用的其他资源
  2. 分配引用索引:为每个被引用的资源分配唯一的索引
  3. 写入引用列表:将引用列表写入序列化数据中

六、压缩处理

6.1 压缩算法

AssetBundle 支持多种压缩算法:

压缩算法压缩率解压速度适用场景
LZMA60-80%网络传输
LZ440-60%极快运行时加载
LZ4HC50-70%平衡场景
无压缩0%最快首包资源

6.2 压缩流程

压缩流程如下:

  1. 读取序列化数据:读取序列化后的数据
  2. 选择压缩算法:根据配置的压缩算法选择压缩方式
  3. 压缩数据:使用选择的压缩算法压缩数据
  4. 写入 Bundle:将压缩后的数据写入 AssetBundle 文件

七、文件组装

7.1 文件组装流程

文件组装是将压缩后的数据和元数据组装成 AssetBundle 文件的过程。

文件组装流程

  1. 写入文件头:写入 AssetBundle 文件头
  2. 写入资源数据:写入压缩后的资源数据
  3. 写入依赖信息:写入依赖信息
  4. 写入资源清单:写入资源清单
  5. 写入类型树:写入类型树

7.2 资源清单

资源清单是 AssetBundle 的索引部分,记录了 Bundle 中所有资源的名称、偏移量、大小和类型信息。

资源清单格式

资源清单
├── 资源数量
├── 资源 1
│   ├── 资源名称
│   ├── 资源偏移量
│   ├── 资源大小
│   └── 资源类型
├── 资源 2
│   ├── 资源名称
│   ├── 资源偏移量
│   ├── 资源大小
│   └── 资源类型
└── ...

八、清单生成

8.1 全局资源清单

全局资源清单记录了所有 Bundle 的信息,包括 Bundle 名称、依赖关系、资源列表等。

全局资源清单格式

全局资源清单
├── Bundle 1
│   ├── Bundle 名称
│   ├── Bundle 大小
│   ├── Bundle 依赖
│   └── 资源列表
├── Bundle 2
│   ├── Bundle 名称
│   ├── Bundle 大小
│   ├── Bundle 依赖
│   └── 资源列表
└── ...

8.2 依赖信息文件

依赖信息文件记录了所有 Bundle 之间的依赖关系,用于在加载时验证依赖关系。


九、构建优化

9.1 增量构建

增量构建可以显著减少构建时间:

  • 变化检测:检测哪些资源发生了变化
  • 增量打包:只重新打包发生变化的资源
  • 缓存复用:未变化的资源直接从缓存复用

9.2 并行构建

并行构建可以充分利用多核 CPU:

  • 模块并行:不同模块的打包可以并行执行
  • 资源并行:同一模块内的资源打包可以并行执行
  • 依赖管理:确保并行执行时依赖关系正确

9.3 压缩优化

压缩优化可以在体积和加载速度之间取得平衡:

  • LZMA 压缩:适合网络传输,压缩率高
  • LZ4 压缩:适合运行时加载,解压速度快
  • 混合压缩:不同资源使用不同的压缩方式

十、总结

资源打包是 Unity 资源管理的核心环节,理解打包流程对于优化资源管理和加载性能至关重要。在实际项目中,建议:

  1. 合理选择资源:根据项目需求合理选择需要打包的资源
  2. 优化依赖关系:避免循环依赖,减少依赖层级
  3. 选择合适的压缩算法:根据场景选择合适的压缩算法
  4. 监控打包结果:使用 Profiler 等工具监控打包结果

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


下一篇资源组织与依赖分析