虽然不像Unity3D那样以多平台支持作为宣传点,虚幻本身依然是多平台支持的,如Windows,IOS,安卓等等。同时为了应对开发和部署的需要,编译的配置也是一个花样繁多的过程。
本篇文章就初步得从概念和工具的角度对虚幻的编译做一定的总结。
文章目录
- 编译系统
- UnrealBuildTool
- UnrealHeaderTool
- 编译类型
- 总结
- 参考
本文使用的环境和工具为:
- Windows 10
- ue5
- Rider or Unreal Engine 2021.3
编译系统
编译
是指从源程序到产生目标程序的过程,是指从高级语言翻译成机器可以识读的二进制语言的过程,更通俗来讲,它可以是把我们编写的代码、资源,整合成一个可以运行的可以玩的游戏的过程。
编译通常会涉及到一项强有力的工具——编译器,当然标准的编译流程中还会又预处理阶段(由预处理器负责,是和编译器分开的),现在各种功能强大的IDE通常会把这些工作整合到一起在后台做掉,我们一般只需要点一下Build
或者Debug
又或者Run
即可。
当然,虚幻引擎本身及其游戏内容都是由c++写成,其项目编译当然也会遵循这个过程。只不过,虚幻引擎本身的代码量非常之巨大,属性和方法组成了类,类和类相互配合搭建起模块,模块和模块相互依赖构成了系统,而虚幻引擎中类和模块的数量已经难以计数,所以它也推出了自己的一套管理代码的工具():UnrealBuildTool(UBT,C#,编译虚幻的各个模块,处理依赖),和UnrealHeaderTool(UHT,C++,头文件解析和代码生成,为反射系统提供支持)。
UnrealBuildTool
UBT的源码位于:
UE_5.0\Engine\Source\Programs\UnrealBuildTool
代码是用C#写成,完整开发的话需要安装相应的.Net桌面开发框架,需要的可以到相应位置查找(一般我们不会去动这个工具,但是相应的,如果需要做一些轻量的修改,可以到BuildConfiguration.cs中探索可供用户配置的编译选项)。而对应的可执行程序的位置在:
UE_5.0\Engine\Binaries\DotNET\AutomationTool
这个才是真正编译好的,发挥作用的工具。
对于UBT,我们需要知道的是,当我们去Build或者Rebuild项目时(以及右键点击项目的.uproject
文件选择Generate Visual Studio project files时),就会去运行UnrealBuildTool.exe,并传入几个参数(Main函数的参数):
static class UnrealBuildTool
{...private static int Main(string[] ArgumentsArray){...}
}
这里的ArgumentsArray即是通过采样C++构建系统NMake的Build Command Line来确定几个参数,主要是项目名称,编译的Target(目标),目标平台等等。
然后读取build.cs,确定模块与模块之间的依赖关系(需要去编译哪些模块,需要忽略哪些模块的编译工作,模块的没有变动的话会跳过)。
随后,UBT调用UHT执行头文件解析和代码生成的工作,UHT会根据反射标签生成相应的代码(如对应GENERATED_BODY()
的宏替换代码就位于生成的.generated.h
中,又如UPROPERTY()
标记的属性被序列化到蓝图中,等等,详情参考讲解反射的相关文章)。
最后,UBT通过确定好的依赖关系调用编译器编译变更模块的C++代码。
UnrealHeaderTool
UHT的源码位于:
UE_5.0\Engine\Source\Programs\UnrealHeaderTool
UHT工具与虚幻的反射机制息息相关,可以这样说,UHT是构建于虚幻UObject系统基础上的宏生成和替换工具(当然,还是用原文“头文件解析工具”更贴切)。
反射是很多游戏引擎都依赖的特性,像Unity3D借用了C#的反射。当然不同的是,C++本身并不支持反射(原生C++有RTTI,Runtime Type Identification,即运行时类型识别),而是虚幻在普通C++上实现了这样的一套反射机制。这样,便使运行时获取类的元数据变得简单,从实现GC,序列化等一些引擎需要的功能。
举个例子,用UPROPERTY()
标记的属性,可以序列化在蓝图中,通过其中的说明符可以进一步定制该属性的特性(详情参阅说明符关键字相关文章)。
编译类型
编译虚幻项目时,可以是简单的选择项目,然后右键Build,然后运行Debug;但是此时可能就会遇到一些问题,为什么有些代码我打了断点,却无法进入断点呢?
这个就是虚幻C++项目的一个小坑了,关键就在于编译的配置。
我们可以发现在每个模块都有对应的.Target.cs
,包括我们的游戏模块。其内容就是对该模块应该面向哪一种编译的目标。
这里这个编译的Target,我们可以理解为我们出于什么目的来编译这个模块,是为了让别人在游戏中直接使用?还是别人可以在编辑器中应用调整该模块?等等。
例如我们的游戏模块,我们知道这个游戏是冲着打成包给别人玩的,那么就有专门的.Target.cs
:
Type = TargetType.Game;
这里的Target为[empty]
此外,这个游戏模块还要供我们在编辑器内编辑使用,可能还会用到调试,那么还有另外一个__Editor.Target.cs
:
Type = TargetType.Editor;
这里的Target为Editor。
此外Target的可选项还有Client和Server,分别对应着客户端和服务器的编译目标。
Target | 描述 |
---|---|
[empty] | 不带编辑器的一个独立可执行版本,需要提前打包烘培内容资源 |
Editor | 在虚幻编辑器里打开并可编辑游戏项目,所有代码的更改也会反映到编辑器中 |
Client | 多人联网功能中的客户端版本,需要在项目中提供__Client.Target.cs (参照__Editor.Target.cs 的写法) |
Server | 多人联网功能中的服务器版本,需要在项目中提供__Server.Target.cs (参照__Editor.Target.cs 的写法) |
除了Target外,还有另外一关键字需要注意——State(通常State在前,Target在后,即[State] [Target])。
State是用来描述我们当前引擎和游戏项目的状态,我们的游戏要准备打包发行了,就使用Shipping;我还处在游戏开发阶段,需要通过代码进行某些游戏代码的调试,就使用DebugGame;而若是只需要通过编辑器的反射来查看代码的更改的话,那么Development一般就可满足条件(这也是默认的状态,虚幻官方推荐新手以蓝图开发为主,代码开发为辅)。
State | Engine | Game | 描述 |
---|---|---|---|
Debug | Debug | Debug | 会同时去构建引擎和游戏两者的代码,是最全面的构建,但同时也会比较费时间 |
DebugGame | Release | Debug | 以最优的方式构建引擎,同时保持游戏部分的代码支持调试,适用于仅调试代码的模式 |
Development | Release | Release | 可以在编辑器内通过反射的方式查看代码所带来的更改,是开发和性能最平衡的模式 |
Shipping | Release | Release | 最佳性能配置,用于交付游戏,无控制台命令、统计数据和性能分析工具 |
Test | Release | Release | 同Shipping模式,但是启用了控制台命令、统计数据和性能分析工具 |
这两个关键字大部分情况都可以组合使用,但是还是有一些例外情况。
编译配置——编译解决方案(有引擎源码版),此时是将引擎源代码和游戏项目的源代码一同编译,其可选配置如下:
Debug | DebugGame | Development | Shipping | Test | |
---|---|---|---|---|---|
[empty] | ✓ | ✓ | ✓ | ✓ | ✓ |
Editor | ✓ | ✓ | ✓ | ||
Client | ✓ | ✓ | ✓ | ✓ | ✓ |
Server | ✓ | ✓ | ✓ | ✓ | ✓ |
编译配置——编译项目(无引擎源码版),此时是将游戏项目的源代码单独编译,其可选配置如下:
Debug | DebugGame | Development | Shipping | Test | |
---|---|---|---|---|---|
[empty] | ✓ | ✓ | ✓ | ||
Editor | ✓ | ✓ | |||
Client | |||||
Server |
总结
UBT也好,UHT也好,甚至于IDE也好,作为工具来说是他们都是非常强大可靠的,同时其强大的代价就是其内部项目的驳杂,会在我们希望深入内部时造成一定的阅读学习困难。
好在,一方面,作为游戏开发者、引擎使用者来说,我们不是必须要把这些工具的内部代码理解通透,而是掌握其基本特性和工作流程已是不错,随着分工不断得细化,我们程序也会走向不同的分工,可能确实有一些同时需要切实掌握并且具备修改的能力,但是对于大部分Gameplay和System的编程者来说,能够完全掌握本文及链接所述内容已是不俗。
另一方面,网络上也越来越多得出现技术和知识的共享者,本文的成文就是综合多方阅读资源进行总结的结果,这里也表达我的谢意。
最后,如果文中有一些技术性或者表述性的错误,还请大家多予指点。
参考
虚幻 5.0 Documentation - Build Configurations Reference
虚幻 5.0 Documentation - Compiling Game Projects
知乎作者 大钊 的文章《InsideUE4》
知乎作者 雪流星 的文章《虚幻编译系统总结》