导语:周四下午,策划小李凑过来问:今晚能出一个测试包吗?我说行,点开 Project Launcher,Configuration 下拉框里躺着 Development、Test、Shipping、Debug、DebugGame 五个选项。我下意识选了 Development,Pak 文件没勾,压缩也没开,符号文件更是忘得一干二净。包是出来了,两百多兆,启动就闪退。测试组没有符号文件,定位不到崩溃点,只能把日志截图发到群里。那晚我们加班到十一点,才发现是 Shipping 配置下被裁剪掉的一段校验代码在 Development 包里根本没触发。从那以后,我决定把 Build Configuration 的每个开关都写进 checklist。
这篇文章围绕 UE5.8 的打包配置展开。我们会把 Configuration、Cook、压缩、符号文件、Target.cs、Build.cs、平台设置、包体优化和发布流程串成一条线。目标是在你下次点下 Package Project 之前,能清楚知道每个选项背后会发生什么。
1. Development / Test / Shipping 到底是什么
Unreal Engine 把构建配置分成几组,最常用的是 Development、Test、Shipping,外加 Debug 和 DebugGame。它们的核心差异在于编译优化、断言开关、控制台命令可用性以及调试信息保留程度。
Development 面向日常开发调试。它开启 UE_BUILD_DEVELOPMENT 宏,保留 check、ensure、UE_LOG 大部分输出,允许控制台命令和蓝图调试。编译器优化级别较低,编译速度快,运行时堆栈信息完整。这个配置适合本机调试、编辑器外运行和内部快速验证,但不适合给玩家。
Test 介于 Development 和 Shipping 之间。它仍然保留一部分调试接口,但会关闭一些开发期专用的断言,同时启用更多的性能分析特性。QA 阶段常用这个配置跑自动化测试,因为它既有一定的优化,又不会把崩溃信息完全吞掉。
Shipping 是面向玩家的最终配置。它定义 UE_BUILD_SHIPPING,关闭绝大多数 check 和 ensure,移除控制台命令,剥离大量调试符号,启用最高级别的编译器优化。Shipping 包启动更快、体积更小、运行更稳,但出问题后定位也更难。
Debug 和 DebugGame 则用于源码级调试。Debug 会对引擎和项目代码都关闭优化,DebugGame 只对项目代码关闭优化而保持引擎优化,适合在接近 Shipping 的环境下排查项目逻辑问题。
选择配置时可以参考下面这张流程图:
flowchart TD
A["开始打包"] --> B{给谁用?}
B -->|"内部调试"| C[Development]
B -->|"QA 测试"| D[Test]
B -->|"玩家上线"| E[Shipping]
B -->|"源码调试"| F["Debug / DebugGame"]
C --> G["保留日志与断言"]
D --> H["保留部分调试接口"]
E --> I["关闭调试接口
启用最高优化"]
F --> J["关闭优化
保留完整符号"]这组选择直接影响后续的符号文件、Pak 策略和压缩设置。很多人在项目前期一直用 Development,到了上线前一周才切 Shipping,结果编译错误、蓝图运行时失效、插件链接失败一起涌来。建议在项目里程碑早期就定期跑一遍 Shipping 构建,把问题提前暴露。
UE 的构建宏在 C++ 代码里随处可见。UE_BUILD_DEVELOPMENT 会保留 WITH_EDITOR 之外的大部分调试能力,UE_BUILD_TEST 会开启 WITH_AUTOMATION_TESTS 并保留性能计数器,UE_BUILD_SHIPPING 则同时关闭 ALLOW_CONSOLE 和 ENABLE_PGO_DATA。这些宏不仅影响日志输出,还会改变网络同步、输入处理和文件加载路径。比如 Shipping 下默认禁用 Exec 命令,很多在 Development 里通过控制台调参数的做法会直接失效。提前做 Shipping 构建,就是提前发现这些依赖。
另外,Development 和 Shipping 的蓝图编译结果也可能不同。Shipping 会剥离编辑器专用节点,某些依赖 IsEditor 判断的蓝图逻辑在 Shipping 下行为会变化。如果项目大量使用蓝图,建议在 Test 配置下先跑一遍关卡流送和核心玩法验证。
2. 打包参数:Project Launcher 与命令行
UE5.8 的打包入口有两个:编辑器里的 Project Launcher 和命令行里的 RunUAT.bat BuildCookRun。Project Launcher 适合可视化操作,命令行适合 CI/CD 和批量构建。
一次完整的 Windows 打包命令大致如下:
RunUAT.bat BuildCookRun ^
-project="D:\Work\MyGame\MyGame.uproject" ^
-noP4 ^
-platform=Win64 ^
-clientconfig=Shipping ^
-serverconfig=Shipping ^
-cook ^
-allmaps ^
-stage ^
-pak ^
-compressed ^
-archive ^
-archivedirectory="D:\Builds\MyGame"参数逐项拆解:
-project:指定.uproject文件路径。-platform:目标平台,如 Win64、Linux、Android、IOS、Mac。-clientconfig/-serverconfig:客户端和服务端使用的构建配置。-cook:把资源转换为目标平台格式。-allmaps:Cook 项目里的所有地图。-stage:把 Cook 结果复制到 Staging 目录。-pak:把文件打包成.pak文件。-compressed:在 Pak 内部启用 Zlib 压缩。-archive:把最终产物复制到归档目录。-archivedirectory:归档目录路径。
如果只做 Cook 不打包,可以去掉 -stage、-pak、-archive;如果只想生成 Staging 目录而不压缩进 Pak,可以去掉 -pak。
命令行里还有一些参数值得记住:-distribution 用于 Google Play 等商店分发包;-prereqs 会把运行时依赖打包进安装程序;-nodebuginfo 可以进一步剥离调试信息;-ddc=DerivedDataBackendGraph 指定共享 DDC。如果项目很大,还可以加上 -fileserver 或 -distrust 来配合 Cook 服务器。这些参数在 CI 脚本里往往比 Project Launcher 更可控。
下面这张图展示了 BuildCookRun 各阶段之间的整体依赖关系:
flowchart TD
subgraph source ["源代码与资源"]
uproject["uproject 文件"]
code["C++ / Blueprint"]
assets["Static Mesh / Texture / Sound"]
end
subgraph engine ["引擎构建层"]
ubt["UBT 编译"]
uat["UAT BuildCookRun"]
end
subgraph output ["输出产物"]
exe["可执行文件"]
pak["Pak 文件"]
symbols["符号文件"]
end
uproject --> ubt
code --> ubt
ubt --> uat
assets --> uat
uat --> exe
uat --> pak
uat --> symbols3. 压缩设置:Pak 与 Oodle
UE5.8 的压缩主要发生在两个层面:Pak 文件内部的块压缩,以及纹理、音频等资源的独立压缩。
Pak 文件默认使用 Zlib。开启 -compressed 后,UAT 会在打包阶段对 Pak 块做压缩。压缩率取决于内容类型:代码和文本类资源通常能压到 30% 以下,已经压缩过的纹理和音频收益有限。
Oodle 是 UE5 引入的一套更现代的压缩方案。它提供 Kraken、Mermaid、Selkie、Leviathan 等多种编码器,在解压速度和压缩率之间做权衡。主机和高端 PC 通常选 Leviathan 或 Kraken,移动端更关注解压速度,可以选 Mermaid 或 Selkie。
在 DefaultEngine.ini 里可以配置 Oodle:
[/Script/Engine.PakFileUtilities]
CompressionFormats=Oodle
OodleAlgorithm=Kraken
OodleLevel=Normal压缩级别越高,构建时耗时越长,但 Pak 体积越小。这里有一个简单估算:如果原始 Pak 大小为 ,压缩后大小为 ,则压缩比为
在实测项目中,Zlib 通常把 Pak 压到 60% 到 70%,Oodle Kraken 可以压到 45% 到 55%。解压开销方面,Kraken 在现代 CPU 上基本不会成为瓶颈。
音频压缩也值得关注。Sound Wave 的压缩覆盖设置可以在 Sound Cue 或项目设置里调整。对于不需要高质量回放的 UI 音效,可以把压缩质量从默认 100 降到 60 或 40,体积能减一半以上。
构建时间和压缩率之间存在明显权衡。Oodle Leviathan 的压缩率最高,但构建阶段可能消耗两倍于 Zlib 的时间。对于每晚自动构建的测试包,用 Zlib 或 Kraken 更划算;对于最终上线包,值得用 Leviathan 再压一遍。可以在 BuildGraph 里按阶段选择不同算法:测试阶段用快算法,发布阶段用慢算法。
4. 符号文件与调试信息
符号文件决定了崩溃后你能定位到哪一行代码。Development 配置默认会生成 .pdb 文件,Shipping 默认不会。如果上线后没有符号文件,拿到玩家的崩溃日志也基本等于天书。
Windows 上的符号文件是 .pdb。要生成 Shipping 的符号文件,需要在 Target.cs 里显式开启:
bUsePakFile = true;
bCompressed = true;
bIncludeSymbols = true;
bDisableLinking = false;或者更常见的做法是在 BuildConfiguration.xml 里统一设置:
<BuildConfiguration>
<bUsePDBFiles>true</bUsePDBFiles>
</BuildConfiguration>符号文件会显著增加产物体积。一个 Shipping 可执行文件可能只有 50 MB,对应的 .pdb 能达到 500 MB 甚至更多。发布时不要把 .pdb 打进安装包,单独上传到符号服务器或内部存储即可。
Android 使用 .so 文件和对应的 .sym 或 .dbg 符号。Play Console 和 Firebase Crashlytics 都需要上传符号文件才能还原堆栈。UE5.8 在 Android 打包时会生成 symbols.zip,里面按 ABI 分目录存放。
iOS 和 macOS 使用 .dSYM。Xcode 可以把 dSYM 上传到 App Store Connect,或者手动保存到版本管理系统里。
下面这张序列图展示了崩溃数据上报与符号还原的完整流转:
sequenceDiagram
participant Player as "玩家客户端"
participant Symbol as "符号服务器"
participant CI as "构建系统"
participant Dev as "开发人员"
CI->>Symbol: 上传对应版本的 .pdb / .sym / .dSYM
Player->>Player: 发生崩溃,生成 minidump / 调用栈哈希
Player->>Dev: 上报崩溃日志
Dev->>Symbol: 根据版本号请求符号文件
Symbol-->>Dev: 返回符号文件
Dev->>Dev: 还原完整调用栈,定位问题保留符号文件还有一个隐性收益:性能分析。Unreal Insights 和第三方的 Superluminal、RenderDoc 都需要符号来把地址解析成函数名。即使不上线,团队内部做 profiling 时也要保证符号可用。
UE5.8 还支持把符号文件自动打包成 zip。Windows 下开启 bZipSymbols 后会生成 MyGame-WindowsNoEditor-Symbols.zip,里面包含所有 dll 和 exe 的 pdb。CI 可以直接把这个 zip 上传到符号服务器,无需手动整理。Android 的 symbols.zip 同样需要按版本保存,Play Console 上传 AAB 时会提示是否同时上传符号文件。iOS 的 dSYM 建议按版本号建立目录结构,避免多个版本混在一起。
5. Target.cs 与 Build.cs 配置
每个 UE C++ 项目至少有一个 *.Target.cs 和一个 *.Build.cs。Target.cs 描述构建目标,Build.cs 描述模块依赖。
一个典型的游戏项目 Target.cs 长这样:
using UnrealBuildTool;
public class MyGameTarget : TargetRules
{
public MyGameTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V5;
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_8;
ExtraModuleNames.Add("MyGame");
bUsePakFile = true;
bCompressed = true;
bUseChunks = true;
bBuildHttpChunkInstallData = false;
}
}关键字段说明:
Type:目标类型,Game、Client、Server、Editor、Program。DefaultBuildSettings:使用 UE5.8 的默认构建设置。IncludeOrderVersion:头文件包含顺序版本,建议锁到 Unreal5_8。bUsePakFile:是否使用 Pak 文件。bCompressed:是否开启 Pak 压缩。bUseChunks:是否把 Pak 拆分成 Chunk,用于补丁和流送。
Build.cs 则管理模块依赖:
using UnrealBuildTool;
public class MyGame : ModuleRules
{
public MyGame(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject", "Engine", "InputCore",
"GameplayAbilities", "GameplayTags", "GameplayTasks"
});
PrivateDependencyModuleNames.AddRange(new string[] {
"Projects", "Slate", "SlateCore"
});
if (Target.Configuration == UnrealTargetConfiguration.Shipping)
{
OptimizeCode = CodeOptimization.InShippingBuildsOnly;
}
}
}通过 Target.Configuration 可以在 Build.cs 里做条件编译。比如只在 Shipping 下启用更激进的优化,只在 Development 下引入测试模块。这种写法比到处写 #if UE_BUILD_SHIPPING 更集中,也更容易维护。
BuildConfiguration.xml 的位置取决于系统:Windows 在 %APPDATA%\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml,Linux 和 macOS 在 ~/.config/Unreal Engine/UnrealBuildTool/。这个文件控制全局构建行为,比如并行编译任务数 ProcessorCountMultiplier、PCH 模式、是否启用 Fast PDB 等。团队内建议把这份配置也纳入版本控制,避免不同构建机行为不一致。如果某个构建机配置了 bUsePCHFiles=false 而另一台没有,同一份代码编译出的产物可能在运行时表现不同。
6. 平台专属设置
不同平台的打包规则和限制差异很大。下面按常见平台逐一梳理。
Windows
Windows 是最宽松的平台。需要关注长路径问题:Windows 默认 MAX_PATH 为 260 字符,引擎中间文件很容易突破。构建机要开启长路径支持,项目也尽量不要放在深层目录。
DirectX Shader Compiler 在打包时会编译大量 shader,耗时可能占整个构建的 30% 到 50%。共享 DDC 能显著减少这部分时间。
Android
Android 需要配置 NDK、JDK、SDK 路径。UE5.8 支持的 NDK 版本在 SetupAndroid.bat 里有说明,不要用太新的版本。打包时通过 -packageformat=apk 或 -packageformat=aab 选择输出格式,Google Play 现在要求使用 AAB。
Android 的纹理格式按设备芯片分为 ASTC、ETC2、DXT 等。在项目设置里启用 ASTC 能覆盖大部分中高端设备,但包体会增加。可以通过 Chunk 把不同格式纹理拆分开,按需下载。
iOS / macOS
iOS 打包需要 Mac 或配置了远程构建的 Windows 机器。Provisioning Profile 和证书必须在打包前配置好。Bundle Identifier、Version、Build Number 要和 App Store Connect 一致。
Metal 的 shader 编译在 iOS 打包时也很重。可以开启 Metal Shader Cache 共享,减少重复编译。
Linux
Linux 常用于Dedicated Server。UE5.8 支持交叉编译,Windows 构建机可以出 Linux Server 包。需要在 Engine/Extras/ 下安装对应的交叉编译工具链。
Linux Server 通常不需要客户端资源,可以在 Target.cs 里设置 bBuildRequiresCookedData = false 或精简 Cook 内容,把包体压到最小。
主机平台
PS5 和 Xbox Series X|S 有严格的 SDK 和认证要求。它们的打包流程类似,但需要平台专用插件和 SDK。Trademark、Trophy/Achievement、平台在线服务都需要在打包前配置完毕。主机平台的 Shipping 包通常需要经过平台方的最终验证工具,不能直接用编辑器里的 Package Project 按钮产出。
跨平台构建时,最容易出错的是行尾符和大小写。Windows 默认不区分大小写,但 Linux Server 区分。如果项目里某个资源路径用了大写,在 Windows 本地 Cook 通过,到 Linux 上就会报找不到文件。统一用 lowercase 路径命名,并在 CI 里跑一遍 Linux 构建,能尽早发现这类问题。
7. 包体大小优化
包体大小直接影响下载转化率和存储成本。优化策略可以分成资源、代码、压缩和分包四类。
资源层面:
- 纹理:按平台设置最大分辨率,移动端不要把 4K 纹理全放进去。
- 音频:UI 和背景音效使用不同压缩质量。
- 材质:减少不必要的材质实例和复杂 shader。
- 关卡:把非首次启动内容放到后续 Chunk。
代码层面:
- 关闭未使用的插件。
- 在 Shipping 下剥离调试代码。
- 使用
UFUNCTION(BlueprintCallable)而不是反射调用,减少反射开销。
压缩层面已经在第三节讲过。分包层面可以通过 Chunk 和 Pak 拆分实现。Chunk 的划分策略通常以首次启动必需内容为基础包,后续关卡、语音包、高清材质作为可选下载。这样能把商店首包压到最小,玩家进入游戏后再按需拉取。
剔除未引用资源是另一个容易忽略的点。编辑器里删除一个资源,不一定能从项目里彻底清除,尤其是被其他资源间接引用或被代码动态加载的情况。可以定期跑 Editor Utility Blueprint 扫描孤立资源,或者用 Reference Viewer 检查引用链。对于确定不再用的旧关卡、旧材质球,直接移出项目或放进单独的 Deprecated 目录,避免被打包进去。
下面这张图展示了包体优化的决策关系:
graph LR
A["包体过大"] --> B["资源优化"]
A --> C["代码裁剪"]
A --> D["压缩策略"]
A --> E["Chunk 分包"]
B --> B1["纹理 LOD"]
B --> B2["音频质量"]
B --> B3["剔除未引用资源"]
C --> C1["关闭未用插件"]
C --> C2["剥离调试代码"]
D --> D1["Oodle 算法"]
D --> D2["Pak 块压缩"]
E --> E1["基础包"]
E --> E2["DLC / 补丁"]估算包体时可以把总大小拆成几部分。假设资源大小为 ,可执行文件大小为 ,压缩后 Pak 大小为 ,则最终安装包大小 约为
其中 是符号文件、中间文件和其他开销。发布时符号文件不应该算进 ,但 CI 产物总占用要把 算进去。
8. 发布构建流程
一个完整的发布构建流程应该包括:代码提交、CI 触发、编译、Cook、打包、符号归档、测试、分发。下面是一个我们在 UE5.8 项目里使用的 checklist:
- 确认当前分支版本号和 commit hash。
- 清理本地 DDC 和 Intermediate 目录,或确认共享 DDC 可用。
- 更新子模块和 Marketplace 插件。
- 跑一遍 Development 构建,确保没有编译错误。
- 切到 Shipping,执行完整 BuildCookRun。
- 检查产物目录,确认
.exe、.pak、符号文件都在。 - 在干净机器上启动一次,验证没有缺失运行时依赖。
- 把符号文件上传到符号服务器。
- 把安装包上传到分发平台或 CDN。
- 在内部群里发布版本号和变更日志。
这个流程里最容易漏的是第 6 步和第 8 步。很多人只把 .exe 和 Pak 文件发出去,符号文件留在构建机里。一个月后崩溃来了,构建机已经清理,只能重新从老 commit 出包打符号,费时费力。
建议把符号文件和安装包按版本号绑定命名,例如 MyGame_Shipping_1.2.3_pdb.zip。CI 里同时归档两份产物,长期保存符号文件。
发布前最后一次检查可以用下面这条命令验证 Pak 内容:
UnrealPak.exe D:\Builds\MyGame\MyGame-WindowsNoEditor.pak -List它会列出 Pak 里的所有文件,方便确认有没有把不该带的资源打进去。
除了 Pak 内容检查,还建议在干净环境做启动验证。构建机通常装了各种 SDK 和运行时,玩家机器上可能没有。把包复制到一台刚装完系统的虚拟机里启动,能发现缺少 VC++ Redist、DirectX Runtime 或 .NET Framework 等问题。Windows 下可以用 -prereqs 参数把这些依赖打进安装包。
结语
打包配置不是点一下 Package Project 就能搞定的事。Development、Test、Shipping 的选择会改变运行时行为;-compressed、Oodle 和音频质量会影响包体;符号文件决定了崩溃后能不能快速定位;Target.cs 和 Build.cs 则是整个构建过程的规则入口。平台差异和包体优化又把问题进一步放大。
这篇文章从一次 Development 包闪退的经历出发,把 UE5.8 的 Build Configuration 拆成了八个部分。Configuration 决定运行时行为,打包参数决定产物形态,压缩和符号文件影响发布后的维护成本,Target.cs 和 Build.cs 是构建规则的入口,平台差异和包体优化则决定了玩家实际拿到的安装包。每个环节都有明确的配置点和检查方法。
希望下次你面对 Project Launcher 的下拉框时,能明确知道该选哪一个,以及选完之后还需要做什么。