探讨UE4中的UBT和UHT
前言
UBT和UHT是编译工具,谁定义的呢,虚幻引擎自己定义的,拿来做什么呢,UBT和UHT是UE4用来简化多平台编译,去除用户自定义平台编译项目的操作
我们写的UE4代码不是标准的C++代码,是基于UE4源代码层层改装了很多层的,UHT将UE4代码转换成标准的C++代码,而UBT负责调用UHT来实现这个转化工作,转化完之后UBT调用标准的C++代码的编译器来将UHT转化后的标准C++代码完成编译成二进制文件,整体上看,UHT是UBT的编译流程的一部分
UBT
UBT:Unreal Build Tool
Unreal Build Tool由C#编写,且作为整个虚幻编译过程中第一个编译步骤,当你运行"GenerateProjectFiles"(一个批处理文件,用于Window平台下生成Visual Studio的解决方案和工程),第一个步骤就是在Source/Programs/UnrealBuildTool/UnrealBuiltTool.csproj工程下执行MSBuild来编译这个"Unreal Build Tool",所以可以理解UBT其实就是一个命令行程序,却可以完成很多事情,比如生成工程文件、执行UBT、为各种不同的平台构建风格来调用编译器(Compiler)和链接器(Linker)


接下来深入了解下UBT的几个方面:Target
、Modules
、BuildConfigration
、IWYU
Target
Target是通过C#源文件声明的,扩展名为.target.cs,并存储在项目的Source目录下。每个.target.cs文件都声明一个类,从TargetRules基类衍生而来
类的名称必须与在其中声明这个类的文件的名称相匹配,后跟"Target"(MyProject.target.cs定义类"MyProjectTarget")
Modules
模块是UE4的构建模块。引擎是由大量模块集合实现的,开发游戏的时候提供自己的模块来进行扩充,每个模块都包含了一组功能,并且可以提供公共接口和编译环境(包括宏、路径等)来让其他模块进行使用。(如.Build.cs文件中的PublicIncludePaths、PrivateIncludePaths、PublicDependencyModuleNames、PrivateDependencyModuleNames、DynamicallyLoadedModuleNames)
模块是通过C#源代码声明的,扩展名为.build.cs,存储在项目的Source目录下。属于一个模块的C++源代码与.build.cs文件并列存储(Private和Public一般分别存放.h和.cpp)
一个标准模块ModuleA.build.cs的内容

这些build.cs文件都由UBT编译,并被构造来确定整个编译环境
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class ModuleA : ModuleRules
{
public ModuleA(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Projects",
"InputCore",
"UnrealEd",
"ToolMenus",
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"DesktopPlatform",
"EditorStyle",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
模块间不能循环引用,如在ModuleB.uplugin下添加中ModuleA
,此时ModuleB可以访问ModuleA.Build.cs文件中的PublicInclude中的头文件路径,ModuleA暴露这些头文件给ModuleB进行使用,但是如果此时ModuleA也想要使用ModuleB中的头文件呢,如果直接在Modules中进行添加,会编译错误,因为这会导致循环引用,此时需要将头文件中需要引入XXXX_API宏定义才可以暴露给其他模块进行使用

BuildConfiguration
除了添加到Config/UnrealBuildTool文件夹下生成的UE4项目之外,UnrealBuildTool还会从Windows上以下位置的XML配置文件读取设置:
- Engine/Saved/UnrealBuildTool/BuildConfiguration.xml
- User Folder/AppData/Roaming/Unreal Engine/UnrealBuildTool/BuildConfiguration.xml
- My Documents/Unreal Engine/UnrealBuildTool/BuildConfiguration.xml

生成项目代码
首先,要做的是运行GenerateProjectFiles
- Unreal Build Tool被构建了
- 批处理文件调用了类似如下的命令(取决于您的项目,Visual Studio版本,平台等)
-ProjectFiles -nodummyconfigs -game -engine -2017 "-project=Path\To\Your\Project.uproject" -Platforms=Win64+XboxOne+UWP64 -noSolutionSuffix - Unreal Build Tool会在引擎和游戏目录搜索所有带有.Build.cs拓展的文件来发现所有定义的模块
- Unreal Build Tool会搜索所有带有.Target.cs拓展的文件来发现所有定义的目标
- 它将生成一个包含所有目标作为构建配置和所有模块作为项目的解决方案
C#项目只是源文件夹中的.csproj文件。C ++项目并不完全是“标准”项目。它不再调用MSBuild,而是调用UnrealBuildTool
构建C++项目
在Unreal中构建C++项目时,您可以看到(基于vcxproj的NMakeBuildCommandLine属性)将调用与此类似的命令行
C:\Path\To\Your\Engine\Build.bat TargetName Win64 Debug "$(SolutionDir)$(ProjectName).uproject" -waitmutex $(AdditionalBuildArguments) -2017
它的背后其实又调用了UnrealBuildTool
它的背后其实又调用了UnrealBuildTool
那么,UnrealBuildTool在这儿的作用是:
- 编译目标。它在运行时编译了.Target.cs代码(使用C#编译器)来获取构建属性。这是UnrealBuildTool从中获取大部分定义和平台信息的地方。某些属性(例如bBuildEditor)表示你需要的是构建编辑器。它会创建一个WITH_EDITOR定义,然后由编译器转发到源文件。以实现源代码中的条件编译:#if WITH_EDITOR 条件编译
- 解析所有依赖模块,包含来自.Target.cs和.Build.cs(模块)的依赖
- 将编译所有依赖模块的Build.cs,以获取有关如何构建每个模块的额外属性
- 解析哪些模块使用了共享编译头(即.Build.cs文件中包含SharedPCHHeaderFile属性,比如CoreUObject,Core,Engine等)
- 解析哪些模块依赖于UObject模块
- 对所有依赖于UObject的模块运行Unreal Header Tool,这时虚幻引擎会注入一些行为到你的类中,强制你在文件中加入由Unreal Header Tool生成的“.generated.h”头文件
- 基于Unreal Header Tool生成的代码,解析所有Include路径
- 基于解析后的路径、定义、外部库等,生成一系列会在目标环境执行的命令列表
- 为共享预编译头调用编译器(CL.EXE)
- 调用编译器来编译源文件(CL.EXE)
- 调用链接器(LINK.EXE)
- 调用所有这些操作
反射机制
反射在Java和C#等语言中比较常见,概况的说,反射数据描述了类在运行时的内容。这些数据所存储的信息包括类的名称、类中的数据成员、每个数据成员的类型、每个成员位于对象内存映像的偏移,此外,它也包含类的所以成员函数信息。
C++本身不支持反射,Unreal engine在C++基础上搭建了自己的一套反射机制。具体来看,对于一个类(UClass),我们可以获得这个类的所有属性和方法,而对于一个类对象,我们可以调用它所拥有的方法和属性,前提是这些属性和方法被纳入到UE4的反射系统。
虚幻4使用反射可以实现序列化、editor的details panel、垃圾回收、网络复制、蓝图/C++通信、相互调用、蓝图结构体导出JSON文件、把JSON文件写入到类的结构体变量、修改和读取任意UPROPERTY宏标记的变量数据等功能。
反射系统是选择加入的,只有主动标记的类型、属性、方法会被反射系统追踪,Unreal Header Tool会收集这些信息,生成用于支持反射机制的C++代码,然后再编译工程。
UHT
UHT:Unreal Header Tool
Unreal engine背后强大的反射机制离不开UHT
UENUM()、UCLASS()、USTRUCT()、UFUNCTION()、UPROPERTY()来标记不同的类型和成员变量
也可以标记一个含有反射类型的头文件,需要添加一个特殊的#include
#include "FileName.generated.h"
将反射数据保存为C++代码的一个好处为可以与二进制文件保持一致,永远不会加载过期的反射数据,因为它们参与编译,永远也不会加载陈旧或过时的反射数据。UHT被设计为一个独立的程序,自己本身不使用任何的generated headers,因此避免了先有鸡还是先有蛋的问题。
.generated.h中生成的函数包含了XXX_Implementation之类的补全,也包含了用于蓝图中调用C++函数的转换函数,并通过GENERATED_BODY()安插到我们编写的类中。
注意:虽然UHT实现了近似C++解析器的功能,但毕竟只能理解一部分语法,不要用#if/#ifndef把标记抱起来,会出现错误。UE4提供了一些特殊的宏来兼容反射系统,比如WITH_EDIROT和WITH_EDITORONLY_DATA。
参考资料
https://ericlemes.com/2018/11/23/understanding-unreal-build-tool/
https://www.zhihu.com/search?type=content&q=unreal%20uht
https://zhuanlan.zhihu.com/p/57186557
探讨UE4中的UBT和UHT的更多相关文章
- 1.【使用EF Code-First方式和Fluent API来探讨EF中的关系】
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/relationship-in-entity-framework-using-code-firs ...
- UE4 中Struct Emum 类型的定义方式 笔记
UE4 基础,但是不经常用总是忘记,做个笔记加深记忆: 图方便就随便贴一个项目中的STRUCT和 Enum 的.h 文件 Note:虽然USTRUCT可以定义函数,但是不能加UFUNCTION 标签喔 ...
- UE4 中在 Actor 中动态 Create Component 与ChildActor 的 小笔记
Note:旧版本的UE4 的Attach 和12.13版本有些不一样 创建Component: UCpp_MyComponent* temp_imageCom = NewObject<UCpp_ ...
- UE4中使用数据表(Data Table)
本文依据官方文档数据驱动游戏性元素整理而来. 做过游戏的应该都清楚,如果游戏稍微有点规模,那么使用数据驱动来做游戏一般是必不可少的一步,一般也就是策划通过本表的方式来解决.下面我们来简单说一下UE4中 ...
- 充分利用 UE4 中的噪声
转自:https://www.unrealengine.com/zh-CN/blog/getting-the-most-out-of-noise-in-ue4 UE4 推出基于材质的程序式噪声已经有一 ...
- 3D游戏开发之在UE4中创建非玩家角色(NPC)
接着上节我们继续学习,现在我们来创建一些NPC(non-playable characters,非玩家角色).在这个游戏中,当我们靠近NPC时,它们会做出相应的反应. 一 创建C++类 1) 在UE编 ...
- UE4中的集合:TSet容器
好久没有更新了,最近一直在老家过年,网络不通的,今天才有时间更新一集. 一.TSet<T>是什么 UE4中,除了TArray动态数组外,还提供了各种各样的模板容器.这一节,我们就介绍集合容 ...
- UE4中的单映射:TMap容器
一.TMap<T>是么 TMap<T>是UE4中的一种关联容器,每个键都关联着一个值,形成了单映射关系.因此你可以通过键名来快速查找到值.此外,单映射要求每个键都是唯一的.类似 ...
- 【转载】 [unreal4入门系列之七] UE4中的Actor类和Pawn类
原文地址: http://www.52vr.com/article-558-1.html 现在我们开始进入UE4的代码开发工作.首先,UE4的类框架是非常庞大的,看起来有点让人措手不及.不过正因为UE ...
随机推荐
- 在windows的情况下面右键添加vim
1, 首先打开注册表,然后打开下面路径: HKEY_CLASSES_ROOT\*\Shell 2, 右键新建项(也就是右键的文字): 用vim编辑 3, 在"用vim编辑"下面再 ...
- 关于使用Flex中图片处理
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="ht ...
- SpringCloud:eureka的'eurekaAutoServiceRegistration'报错解决方法
报错信息如下: org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with ...
- SpringMVC(4)数据绑定-1
在SpringMVC(3)URL请求到Action的映射规则我们介绍了请求是如何映射到一个action上的,下一步当然是如何获取到请求中的数据,这就引出了本篇所要讲的内容-数据绑定. 首先看一下都有哪 ...
- Redisson 分布式锁源码 11:Semaphore 和 CountDownLatch
前言 Redisson 除了提供了分布式锁之外,还额外提供了同步组件,Semaphore 和 CountDownLatch. Semaphore 意思就是在分布式场景下,只有 3 个凭证,也就意味着同 ...
- Linux云计算-07_Linux文件服务器之vsftpd服务器
本章向读者介绍企业vsftpd服务器实战.匿名用户访问.系统用户访问及虚拟用户实战等. 1 vsftpd服务器企业实战 文件传输协议(File Transfer Protocol,FTP),基于该协议 ...
- python05篇 json和函数
一.json json就是一个字符串,只不过是所有语言能解析这个字符串.1.1 把python的数据类型转为json import json d = {'name': 'xiaohei', 'cars ...
- 日志采集工具Flume的安装与使用方法
安装Flume,参考厦门大学林子雨教程:http://dblab.xmu.edu.cn/blog/1102/ 并完成案例1 1.案例1:Avro source Avro可以发送一个给定的文件给Flum ...
- 【系统配置】Ubuntu和Windons系统安装配置深度学习环境
Ubuntu系统 1.备份 在服务器上整个装系统之前,需要做好一个工作,也就是相关重要数据的备份,这里主要是将固态中的数据备份到机械硬盘或移动硬盘里,可能在备份的过程中会遇到无法写入的问题,是因为文件 ...
- Linux服务器相关性能的命令
Linux服务器相关性能的命令 一.查看服务器性能信息的相关命令 1.cpu信息查看 cpu分为物理cpu和逻辑cpu 物理cpu:实际物理服务器插槽上cpu的个数,可以通过physical id不重 ...