虚幻4随笔4 从工程开始
前文说到UE3开始,虚幻就使用了UnrealBuildTool(以下简称UBT)来编译和生成代码。
为什么这么做而不是使用VS是很好理解的:因为VS跨平台会比较麻烦。像虚幻这样体量的工程,单为工程做一次VS配置就基本是一天的时间
而且UE4还不像UE3那样就十几个工程,把所有uproject都看做工程的话,得几十了。依赖关系复杂度几何增长,用VS的工具去维护……而且要维护各个平台和配置……再加上维护完后Mac、Linux还得维护一遍……
但是为什么不使用成熟的CMAKE呢,私以为可能是因为UBT里有一系列错综复杂的规则,用CMAKE制作出来,即便可读性OK,调试也比较麻烦。而且CMAKE对引擎使用者提出了一定的要求,而UBT则相对简单——只要你不纠结它如何实现。
官网的这篇文章详细解释了为什么要做一个Build Tool出来:
What are the advantages of generating project files?
可以先看看UBT的基本规则:
https://docs.unrealengine.com/latest/INT/Programming/UnrealBuildSystem/index.html
目前说来,虚幻的所有代码集中在下面几个文件夹里:
<Root>的Source,这个文件夹里主要是引擎代码。
其中:
Source/Runtime里主要是引擎的核心代码。
Source/Developer里似乎主要是一些工具工程。
Source/Editor里是编辑器相关代码。
Source/Programs里是引擎使用中需要用到的工具。比如UBT、UnrealHeaderTool、Swarm(分布式光照计算系统)等等。
Source/ThirdParty里是各种第三方库。
<Root>的Plugin,这个文件夹里有各式各样的Plugin实现。特殊的是Plugin的组织中需要多一个uplugin,可能是Plugin下可能会有一些资源什么的吧。我们后面再来看uplugin。
<工程项目>的Source,如果是代码工程的话。
UBT目前只认这几个文件夹,也就是说,如果你要为引擎扩展功能,您只能在这些文件夹里创建自己的工程。这一点是在UBT里写死的,有代码的可以关注一下UBT工程的FindAllRulesSourceFiles这个方法。
在这些文件夹里,您可以搜索到大量的*.Build.cs文件,这些Build.cs就是虚幻的工程组织核心,基本上,每个Build文件都可以被视为一个工程文件,而Build文件所在的文件夹可以被视为此工程的根目录。接下来,我们不妨称这些拥有Build.cs的文件夹为工程。UBT一开始会先去找所有的Build.cs,把它们放在一起生成一个临时的dll。然后基于它们逐个进行一系列的代码分析工作,最后调用命令行进行编译和连接过程。
对于每个工程而言,代码一般都散落在下面几个文件夹:
Classes:如果你在工程根目录下写了个Classes,就相当于告诉UBT这些文件是要用UnrealHeaderTool来生成运行时反射信息的。所以,这个文件夹里头文件的写法必须符合可反射类的写法规范。
还记得虚幻的哪些类是可反射的吗?对了,所有UObject的派生类包括AActor的派生类。具体是否有所验证还没看,不过最好是按照这个节奏来。
规范上无非主要就是USTRUCT、UCLASS这些宏,抄几个就能找到感觉,或者用编辑器的类生成功能也可。
看起来,虚幻引擎发布时(不是通过编译生成,而是通过Launcher下载的那种),这些Classes文件是随引擎发布的,方便不具备全代码的Mod爱好者和BluePrint开发者们来制作游戏。
Public:公共头文件,跟Classes一样随着引擎发布而发布,所以这里一般都是些比较开放的接口,比如模块入口、功能核心接口什么的。基本上这些接口没有废话,很清晰,跟实现相关的细节隐藏得非常好。与Classes相同,如果你的文件夹里有Public,那么里面的.h就会被当作Public来。
Private:这个文件夹似乎不是虚幻定死的,也就是似乎可以不用Private的名字,或者多来几个文件夹什么的。除了UHT中有一段代码与之有一定关联之外,UBT里是完全没有跟这个有关的东西。它里面基本上就是各种实现代码,以及要在实现间共享的头文件。你也可以创建其他类似的文件夹,只需要Build.cs里写上相应的文件夹名即可:
此外还有需要注意的地方是Source根目录下的Target.cs文件,Target的最终目标一般都是可执行文件,可以说,Target是整个生成期的入口,生成会首先从找到Target开始,如果没有Target或者找不到,就会直接失败。
此外,需要注意的是,Target.cs里面写的类的类名,必须是Target.cs的文件名加Target,例如:Sample.Target.cs,其类名必须是SampleTarget。UBT的GetTargetTypeAndRulesInstance方法里印证了这一点。
UBT里面还是有不少限定用法的,Target就是其中之一,虚幻是一个比较强调命名的引擎,改名有很多麻烦,最好是能够一步到位。
今天抽空看了看文档,大概跟了跟UBT的流程,明天继续。