烘焙、打包与 Zen 加载器
Cooking, Packaging & Zen Loader
打包是一组构建操作:编译 (Build) → 烘焙 (Cook,把资产转成目标平台格式) → 暂存 (Stage) → 打包 (Package) → 可选的部署 (Deploy)。UE5 的运行时加载器 Zen Loader 会在暂存阶段离线算好包与对象的依赖图,并把数据装进 .utoc/.ucas 容器(IoStore),由 I/O 分发器以很低的 CPU 开销定位数据块;.pak 只用于不常访问的零散文件。
打包流水线
flowchart LR B["Build<br/>编译代码、插件"] --> C["Cook<br/>资产转平台格式"] --> S["Stage<br/>复制到暂存目录<br/>并离线计算依赖"] --> K["Package<br/>打成可分发文件<br/>.pak / .utoc / .ucas"] --> D["Deploy(可选)<br/>推到目标设备"]
官方:打包把项目内容编译成能在目标平台运行的格式,本身是一个构建操作;Build、Cook、Stage 都是参与打包的构建操作。Build 为目标平台编译代码、插件和二进制;Cook 把几何体、材质、纹理、蓝图、音频等资产保存成目标平台能运行的格式;Stage 把编译好的代码和烘焙后的内容复制到开发目录之外的暂存目录;Package 把它们打成可分发的文件(Windows 上是可执行文件加一个装有 .pak 的文件夹);最后可选 Deploy。为主机开发和打包需要源码版引擎,不能用 Launcher 预编译版;Linux、移动端、XR 等平台可能还需要额外的 SDK。(Packaging Your Project)
为什么要烘焙
引擎内部用 PNG(纹理)、WAV(音频)等格式存资产,但各平台可能使用专有格式、不支持这些格式,或者有更省内存、性能更好的格式,因此要转换,这个过程叫烘焙 (cooking)。(Cooking Content in Unreal Engine)纹理烘焙成平台压缩格式就是 纹理压缩 的落地。
| Cook 命令行参数 | 作用 |
|---|---|
-iterate | 只烘焙过期的内容;不加时会删掉沙盒目录、全部重新烘焙 |
-cookonthefly | 以服务器模式启动,等游戏连上后按需提供烘焙数据 |
-UnVersioned | 保存不带版本号的包 |
-Compressed | 压缩烘焙后的包 |
-cookall | 烘焙全部内容 |
(Cooking Content in Unreal Engine)迭代开发时用 -iterate 可以大幅缩短烘焙时间;正式出包时更稳妥的做法是全量重新烘焙(这一点是工程经验归纳)。
Zen Loader 与 IoStore 容器
- Zen Loader 是 UE5 新的运行时加载器。它在暂存阶段离线计算经过优化的包和对象依赖图,以降低 CPU 开销。它不再把所有资产打进 .pak,而是把包数据、bulk data 和着色器数据放进一组 .utoc / .ucas 容器文件:.utoc 描述容器(块大小与偏移、压缩格式、是否加密),.ucas 存实际数据;.pak 只用于不常访问的零散文件。挂载 Pak 时,对应的容器也会一并挂载。(Zen Loader in Unreal Engine)
- 容器绕开了文件系统抽象,由新的 I/O 抽象层 I/O dispatcher 以最小的 CPU 开销找到数据块。UAT 在暂存时默认生成容器,运行时检测到容器就会启用 Zen Loader。(Zen Loader in Unreal Engine)
- 坑:用 Zen Loader 时,初次序列化完成后 BulkData 不能再加载内联数据,也就是说数据一旦卸载就无法再取回。UnrealPak 可以检查容器:
-list、-diff、-extract,IoStore 下还有-describe(查看依赖、包、导入导出)。(Zen Loader in Unreal Engine) - 依赖图离线算好之后,运行时的异步加载(见 资产管理与异步加载)就不必临时解析依赖,这是 Zen Loader 省 CPU 的来源。
包结构:导入表与导出表
官方没有讲 .uasset 文件布局的概述页(FPackageFileSummary 的 API 页返回 403),以下只依据 5.8 API 参考中的链接器 (Linker) 类型说明。(FLinkerTables / FPackageIndex / FObjectExport (API))
| 表 | 内容 |
|---|---|
ExportMap (FObjectExport) | 包内定义、可以被其他包引用的对象 |
ImportMap (FObjectImport) | 本包引用的外部对象 |
| DependsMap | 每个导出各自的依赖列表 |
| SoftPackageReferenceList | 本包软引用的包 |
包内的对象引用都用 FPackageIndex 编码:
每条导出记录里有 ClassIndex、SuperIndex、TemplateIndex(也是 FPackageIndex)、对象标志,以及 SerialOffset/SerialSize:这是该对象数据在文件读取存档 (archive) 中的起始位置和字节数。ScriptSerializationStart/EndOffset 标出其中使用带标签属性序列化 (Tagged Property Serialization) 的那一段。bNotForClient/bNotForServer 控制客户端或服务器是否加载这个导出。(FLinkerTables / FPackageIndex / FObjectExport (API))
文件头 FPackageFileSummary(依据 UE 5.6 API;5.8 版本页返回 403):官方称之为包文件的「目录 (table of contents)」,存放在文件开头。(FPackageFileSummary)
| 字段组 | 字段(官方说明) |
|---|---|
| 魔数 | Tag:与 PACKAGE_FILE_TAG 比较,确认这是 Unreal 包 |
| 版本 | FileVersionUE(UE 文件版本)、FileVersionLicenseeUE(授权方版本)、CustomVersionContainer(按各组件唯一标签记录的自定义版本)、SavedByEngineVersion、CompatibleWithEngineVersion;bUnversioned 表示保存时不带版本号 |
| 表的位置 | NameCount/NameOffset、ImportCount/ImportOffset、ExportCount/ExportOffset、DependsOffset、SoftPackageReferencesCount/Offset、SearchableNamesOffset、PreloadDependencyCount/Offset |
| 其他数据 | TotalHeaderSize(创建 FLinkerLoad 需要读入的全部信息大小)、BulkDataStartOffset、PayloadTocOffset、ThumbnailTableOffset、AssetRegistryDataOffset、GatherableTextDataCount/Offset(可收集文本,供本地化使用)、WorldTileInfoDataOffset |
| 标识 | PackageName、PersistentGuid、PackageFlags、Generations(历史版本信息)、SavedHash(保存时包字节的哈希)、ChunkIDs(流式安装分块) |
版本检查函数:IsFileVersionTooOld()/IsFileVersionTooNew() 判断包版本是否低于最低支持版本或高于当前进程支持的版本;IsFileVersionValid() 在包未带版本号且当前进程不支持时返回 false。(FPackageFileSummary)也就是说,加载器先读文件头,用 Offset 定位各张表,再按导出表中的 SerialOffset 读取对象数据(读取顺序是根据字段含义归纳的)。PACKAGE_FILE_TAG 的数值在官方 API 页(5.3–5.8 各版本都只返回目录)中查不到。非官方来源(第三方博客引用的引擎源码 Core/Public/UObject/ObjectVersion.h)给出的定义是 #define PACKAGE_FILE_TAG 0x9E2A83C1,字节序相反时为 PACKAGE_FILE_TAG_SWAPPED 0xC1832A9E,即 .uasset 文件的前 4 个字节;引擎加载时先检查这个值,再反序列化 PackageFileSummary。(Unreal Resource Management: A raw uasset Encryption Scheme,非官方,未经 Epic 文档核实)
GEA 的视角:何时做平台相关处理(卷 II 16.4.2.1)
GEA 把资产处理分为"导出到平台无关中间格式"和"处理成平台优化格式"两步,并比较第二步的时机:Source、Quake 在运行前烘焙关卡时做;Halo 在首次加载进引擎时做并缓存结果;而对 UnrealEd,GEA 写的是"在资产首次导入编辑器时"完成,好处是关卡迭代快,代价是修改源资产更麻烦。
分歧:本页依据 UE 5.8 文档,资产在引擎内部以 PNG、WAV 等格式保存,转换为平台格式发生在 Cook 阶段。两者并列记录;GEA 的描述可能针对较早的 UnrealEd,书中没有说明版本。
对比表与世界编辑器的其余功能见 数据驱动与游戏世界编辑器。
出处
- Cooking Content in Unreal Engine UE 5.8 文档
- Packaging Your Project UE 5.8 文档
- Zen Loader in Unreal Engine UE 5.8 文档
- FLinkerTables / FPackageIndex / FObjectExport (API) UE 5.8 API 参考(链接器表)
- FPackageFileSummary UE 5.6 API(FPackageFileSummary)
- Unreal Resource Management: A raw uasset Encryption Scheme 【非官方】第三方博客引用源码
- Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
- Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 16 章 16.4.2.1 节