UAT 调用
从 UI 点击,触发回调,最终调用到 FLauncherUATTask::PerformTask
,它内部就是唤起了一个 UAT Command
1 | FString UATCommandLine; |
以安卓为例
打包时,调用的 UAT 进程的参数是
1 | L"/c ""<my position>/UnrealEngine/Engine/Build/BatchFiles/RunUAT.bat" -ScriptsForProject="<my position>/<my project name>/<my project name>.uproject" BuildCookRun -nocompileeditor -nop4 -project="<my position>/<my project name>/<my project name>.uproject" -cook -stage -archive -archivedirectory="<my position>/<my project name>AndroidPackage" -package -ue4exe="<my position>\UnrealEngine\Engine\Binaries\Win64\UE4Editor-Win64-DebugGame-Cmd.exe" -compressed -ddc=DerivedDataBackendGraph -pak -prereqs -nodebuginfo -targetplatform=Android -cookflavor=ETC2 -build -target=<my project name> -clientconfig=Development -utf8output"" |
不太理解为什么写成 -utf8output""
,那个后面的 ""
是啥意思
分析一下我要是想自己调用的话,该怎么做
1 | "<my position>/UnrealEngine/Engine/Build/BatchFiles/RunUAT.bat" -ScriptsForProject="<my position>/<my project name>/<my project name>.uproject" BuildCookRun -nocompileeditor -nop4 -project="<my position>/<my project name>/<my project name>.uproject" -cook -stage -archive -archivedirectory="<my position>/<my project name>AndroidPackage" -package -ue4exe="<my position>\UnrealEngine\Engine\Binaries\Win64\UE4Editor-Win64-DebugGame-Cmd.exe" -compressed -ddc=DerivedDataBackendGraph -pak -prereqs -nodebuginfo -targetplatform=Android -cookflavor=ETC2 -build -target=<my project name> -clientconfig=Development -utf8output |
这样是 work 的
具体到 RunUAT.bat 里面看,他只是转发参数给 AutomationToolLauncher.exe
于是在 IDE 中传入调试 UE 时获得的命令行参数。它最终转发参数给 AutomationTool.exe
UAT 打包生成 apk
AutomationTool.exe 最终会分析参数,分析出来 BuildCookRun
它会根据字符串查询类型,最终创建出来 public class BuildCookRun : BuildCommand
这个类
他最终会执行 protected void DoBuildCookRun(ProjectParams Params)
该函数调用构建、烘培、打包程序
自然地,打包函数是虚函数,随平台而不同
具体到安卓平台是 AndroidPlatform
实现了 Package
函数
实际调试发现,Package
函数并不是简单的包揽所有,它只是负责 obb 文件的创建与更新,还有 apk 文件的创建的准备工作
之后还有别的接口
1 | protected void DoBuildCookRun(ProjectParams Params) |
这里 Deploy 似乎是没有执行。它是把 apk 部署到当前平台,但是我这里是 windows 所以他没有部署?
重新跑一遍,是在 Project.Archive(Params);
执行之后,apk 和 obb 文件就都生成了
点进来看,是在 public static void Archive(ProjectParams Params)
的 ApplyArchiveManifest(Params, SC);
这里创建出文件
这仅仅是从文件浏览器的角度来看,我感觉可能之前对于文件的准备工作还是会很多
不过目前还是从这个明显的创建出文件的这个函数作为入口来看
进到 ApplyArchiveManifest
发现他只是拷贝文件而已。拷贝文件还可以只拷贝增量,神奇。
所以是在这之间文件已经在 \Binaries\Android 打包好了,现在只是拷贝到输出目录。
于是重新调试,发现 \Binaries\Android 里面的 apk, obb 确实是由 Project.Package(Params, WorkingCL);
创建
于是还是回到 AndroidPlatform.Package
,看到
1 | string StageDirectoryPath = Path.Combine(SC.StageDirectory.FullName, SC.ShortProjectName); |
于是发现这里似乎是已经有文件可以处理了,这个文件看上去还是 pak 包
于是看到 FilesForObb
的成员,看到 <my ue position>\<my project name>\Saved\StagedBuilds\Android_ETC2\<my project name>\Content\Paks\<my project name>-Android_ETC2.pak
于是发现,在 UAT 打包之前,我的 Saved\StagedBuilds\Android_ETC2 文件夹里面已经有 pak 文件了?
那这个暂且搁置,回来看看 UAT 的打包是否都是复制文件
一路调试到 Deploy.PrepForUATPackageOrDeploy
,在这里执行之后生成了 apk
重定向到 UEDeployAndroid.PrepForUATPackageOrDeploy
,这里面的大头就是 MakeApk
最终调试到
1 | RunCommandLineProgramWithExceptionAndFiltering(UE4BuildGradlePath, ShellExecutable, ShellParametersBegin + "\"" + GradleScriptPath + "\" " + GradleOptions + ShellParametersEnd, "Making .apk with Gradle..."); |
是他调用了 Gradle 去生成 apk
之后就是调用 CopyFile(LocalObbName, ObbName);
去拷贝 obb
UnrealPak 调用流程
再回来单步调试,BuildCookRun.DoBuildCookRun
中的 Project.CopyBuildToStagingDirectory(Params);
负责生成 Saved\StagedBuilds\Android_ETC2 中所有内容
其中,Project.ApplyStagingManifest
生成了内容
其中是各种 Manifest 的类型,假设先不管每个条件是什么,根据调试,进入 Project.CreatePakUsingStagingManifest
其中根据 manifest 有一些 rules 的处理,处理完之后调用 Project.CreatePaks
前面部分是压缩设置,优先级,然后就是遍历 PakParamsList
处理每个要打包的 pak
PakParamsList
中的一个 CreatePakParams
成员对应一个 pak
CreatePakParams
的 UnrealPakResponseFile
成员列出了需要打包的文件列表
遍历 PakParamsList
时,开头是计算文件路径,然后是判断是否可以复用已经生成的 pak,判断是否生成增量补丁
如果不能复用,那么开始生成 pak
为了生成 pak,又要调用别的程序,所以要准备一下传递的参数
最终调用为记录 command
1 | Commands.Add(GetUnrealPakArguments( |
进入 Project.GetUnrealPakArguments
可以看到 Project.WritePakResponseFile
也是写 txt 文件,把要打包什么写入到 txt 文件中,应该是要传递给 UnrealPak
每个 pak 都记录一个 command,收集起来成为一个列表,然后并行调用
1 | // Actually execute UnrealPak |
最终调用 UnrealPak 的参数就是
1 | <my ue position>\<my project name>\<my project name>.uproject <my ue position>\<my project name>\Saved\StagedBuilds\Android_ETC2\<my project name>\Content\Paks\<my project name>-Android_ETC2.pak -create=<my ue location>\UnrealEngine\Engine\Programs\AutomationTool\Saved\Logs\PakList_<my project name>-Android_ETC2.txt -cryptokeys=<my ue position>\<my project name>\Saved\Cooked\Android_ETC2\<my project name>\Metadata\Crypto.json -secondaryOrder=<my ue position>\<my project name>\Build\Android_ETC2\FileOpenOrder\CookerOpenOrder.log -platform=Android -compressionformats=Oodle -compressmethod=Kraken -compresslevel=3 -multiprocess -abslog=<my ue location>\UnrealEngine\Engine\Programs\AutomationTool\Saved\Logs\UnrealPak-<my project name>-Android_ETC2-2025.05.09-19.04.31.txt -compressionblocksize=256KB |
之后就是 UnrealPak 内部的逻辑了。直接把这段命令行传给 UnrealPak 项目就可以调试了
UnrealPak 内部流程
UnrealPak 是 cpp 项目,是对引擎内 ExecuteUnrealPak
函数的包装
前面的部分是一些功能,比如生成补丁。除了这些功能,打包的逻辑最终是
先收集打包文件列表 CollectFilesToAdd(FilesToAdd, Entries, OrderMap, CmdLineParameters);
根据文件列表创建 PAK bool bResult = CreatePakFile(*PakFilename, FilesToAdd, CmdLineParameters, KeyChain);
CreatePakFile
里面涉及到挂载点、压缩机制、加密机制、对齐和填充、异步和多线程、索引结构,这个打包的细节还是很多的