《Inside UE4》-1-基础概念
InsideUE4
创建测试项目
接上文的准备工作,双击生成的UE4Editor.exe,选择创建测试C++空项目Hello(以后的源码分析都会基于该最简单的项目)
项目文件结构
VS项目和文件目录:
可以看到,Config目录里带着3个最主要的配置,Editor,Engine,Game。代码方面自动生成了用于编译系统的3个.cs文件,C++代码方面生成了一个Hello "Game Module",和HelloGameMode。
文件目录:
- Binaries:存放编译生成的结果二进制文件。该目录可以gitignore,反正每次都会生成。
- Config:配置文件。
- Content:平常最常用到,所有的资源和蓝图等都放在该目录里。
- DerivedDataCache:“DDC”,存储着引擎针对平台特化后的资源版本。比如同一个图片,针对不同的平台有不同的适合格式,这个时候就可以在不动原始的uasset的基础上,比较轻易的再生成不同格式资源版本。gitignore。
- Intermediate:中间文件(gitignore),存放着一些临时生成的文件。有:
- Build的中间文件,.obj和预编译头等
- UHT预处理生成的.generated.h/.cpp文件
- VS.vcxproj项目文件,可通过.uproject文件生成编译生成的Shader文件。
- AssetRegistryCache:Asset Registry系统的缓存文件,Asset Registry可以简单理解为一个索引了所有uasset资源头信息的注册表。CachedAssetRegistry.bin文件也是如此。
- Saved:存储自动保存文件,其他配置文件,日志文件,引擎崩溃日志,硬件信息,烘培信息数据等。gitignore
- Source:代码文件。
编译类型
很多人在使用UE4的时候,往往只是依照默认的DevelopmentEditor,但实际上编译选项是非常重要的。
UE4本身包含网络模式和编辑器,这意味着你的工程在部署的时候将包含Server和Client,而在开发的时候,也将有Editor和Stand-alone之分;同时你也可以单独选择是否为Engine和Game生成调试信息,接着你还可以选择是否在游戏里内嵌控制台等。
依照官方介绍
每种编译配置包含两种关键字。第一种表明了引擎以及游戏项目的状态。第二个关键字表明正在编译的目标。
状态 | Engine | Game | 其他 |
---|---|---|---|
Debug (调试) | Debug | Debug | 必须在编辑器上加-debug参数才能反射查看代码更改 |
DebugGame (调试游戏) | Release | Debug | 适合只调试游戏代码 |
Development (开发) | Release | Release | 允许编辑器反射查看代码更改 |
Shipping (发行) | Release | Release | 无控制台命令,统计数据和性能分析 |
Test (测试) | Release | Release | 启用了一些控制台命令,统计数据和性能分析 |
目标 | 描述 |
---|---|
[empty] (空白) | 不带编辑器的一个独立可执行版本,需要提前打包烘培内容资源 |
Editor (编辑器) | 直接在编辑器里打开游戏项目 |
Client (客户端) | 多人联机项目,生成客户端版本,需要提供 <Game>Client.Target.cs 文件 |
Server (服务器) | 多人联机项目,生成服务器版本,需要提供 <Game>Server.Target.cs 文件 |
组合的各种情况:
Debug | DebugGame | Development | Shipping | Test | |
---|---|---|---|---|---|
[empty] | ✓ | ✓ | ✓ | ✓ | ✓ |
Editor | ✓ | ✓ | ✓ | ||
Client | ✓ | ✓ | ✓ | ✓ | ✓ |
Server | ✓ | ✓ | ✓ | ✓ | ✓ |
所以为了我们的调试代码方便,我们选择DebugEditor来加载游戏项目,当需要最简化流程的时候用Debug来运行独立版本。
命名约定
客观来说,相比其他引擎的源码,UE4的源码还是非常清晰的,模块组织也比较明了。但阅读源码的学习曲线依然陡峭,我想有以下原因:
1. UE4包含的模块众多,拢共有几十个模块,虽然采用了Module架构来解耦,但难免还是要有依赖交叉的地方,在阅读的时候就很难理清各部分的关系。
2. UE4的功能优秀,作为业界顶尖的成熟游戏引擎,在一些具体的模块内部实现上就脱离了简单粗暴,而是采用了各种设计模式和权衡。同时也需要阅读的人有相关的业务知识。比如材质编辑器编译生成Shader的过程就需要读者拥有至少差不多的图形学知识。
3. 被魔改后的C++,UE4为了各平台的编译和其他考量(具体以后说到编译系统的时候再细讨论),对标准的C++和编译,进行了相当程度的改造,在UHT代码生成和各种宏的嵌套之后,读者就很难一下子看清背后的各种的机制了。
但万丈高楼平地起,咱们也可以从最简单的一步步开始学起,直到了解掌握整个引擎的内部结构。
在阅读代码之前,就必须去了解一下UE4的命名约定,具体的自己去查看官网文档,下面是一些基本需要知道的:
- 模版类以T作为前缀,比如TArray,TMap,TSet
- UObject派生类都以U前缀
- AActor派生类都以A前缀
- SWidget派生类都以S前缀
- 抽象接口以I前缀
- 枚举以E开头
- bool变量以b前缀,如bPendingDestruction
- 其他的大部分以F开头,如FString,FName
- typedef的以原型名前缀为准,如typedef TArray FArrayOfMyTypes;
- 在编辑器里和C#里,类型名是去掉前缀过的
- UHT在工作的时候需要你提供正确的前缀,所以虽然说是约定,但你也得必须遵守。(编译系统怎么用到那些前缀,后续再讨论)
基础概念
和其他的3D引擎一样,UE4也有其特有的描述游戏世界的概念。在UE4中,几乎所有的对象都继承于UObject(跟Java,C#一样),UObject为它们提供了基础的垃圾回收,反射,元数据,序列化等,相应的,就有各种"UClass"的派生们定义了属性和行为的数据。
跟Unity(GameObject-Component)有些像的是,UE4也采用了组件式的架构,但细品起来却又有些不一样。在UE中,3D世界是由Actors构建起来的,而Actor又拥有各种Component,之后又有各种Controller可以控制Actor(Pawn)的行为。Unity中的Prefab,在UE4中变成了BlueprintClass,其实Class的概念确实更加贴近C++的底层一些。
Unity中,你可以为一个GameObject添加一个ScriptComponent,然后继承MonoBehaviour来编写游戏逻辑。在UE4中,你也可以为一个Actor添加一个蓝图或者C++ Component,然后实现它来直接组织逻辑。
UE4也支持各种插件。
其他的下篇再一一细说。
编译系统
UE4支持众多平台,包括Windows,IOS,Android等,因此UE4为了方便你配置各个平台的参数和编译选项,简化编译流程,UE4实现了自己的一套编译系统,否则我们就得接受各个平台再单独配置一套项目之苦了。
这套工具的编译流程结果,简单来说,就是你在VS里的运行,背后会运行UE4的一些命令行工具来完成编译,其他最重要的两个组件:
UnrealBuildTool(UBT,C#):UE4的自定义工具,来编译UE4的逐个模块并处理依赖等。我们编写的Target.cs,Build.cs都是为这个工具服务的。
UnrealHeaderTool (UHT,C++):UE4的C++代码解析生成工具,我们在代码里写的那些宏UCLASS等和#include "*.generated.h"都为UHT提供了信息来生成相应的C++反射代码。
一般来说,UBT会先调用UHT会先负责解析一遍C++代码,生成相应其他代码。然后开始调用平台特定的编译工具(VisualStudio,LLVM)来编译各个模块。最后启动Editor或者是Game.
更细的留待“编译系统”再细细讨论