UE 学习思路
学习目标
- TArray、TSet、TMap
等各种容器 - FString、FName、FText
的操作互转 - TSharedPtr、TSharedRef、TWeakPtr
等各种智能指针 - 有能力理解
Delegate、TAttribute、TSubClassOf 的机制和用法 - 多看看设计模式,这样才能更好理解
UE4 代码的结构 - 多线程的知识,这样才能用好
FRunnable 等多线程同步 - 人脑展开宏..
源码阅读
1. TArray、TSet、TMap 等各种容器
在
2. FString、FName、FText 的操作互转
在
3. TSharedPtr、TSharedRef、TWeakPtr 等各种智能指针
在
4. Delegate、TAttribute、TSubClassOf 的机制和用法
在
5. 多看看设计模式,这样才能更好理解 UE4 代码的结构
6. 多线程的知识,这样才能用好 FRunnable 等多线程同步
Gameplay 部分
- Gameplay
的 C++ 编写 - Actor
的创建,组装 Component,Beginplay,Tick,碰撞输入事件的绑定 - 引擎
Gameplay 对象的继承组织使用 - UObject
自定义对象的组织管理(根据逻辑而定) - 引擎数据对象的使用,Config,DataTable
- 功能模块的
C++ 层次编写(UMG,AI,动画)注意有机结合
- Actor
这部分代码在
Module-UBT-CSharp
思考: 为什么 UE 要用 C# 来包含和库链接管理编译流程?
合适的头文件包含和库连接
大钊以前写的:
UE4
简单来说,就是我们在
一般来说,UBT
UnrealBuildTool (UBT,C#): UE4
UnrealHeaderTool (UHT): 解析生成工具,我们在代码里写的那些宏#include "*.generated.h"
都为
回答出两个问题:include
- 优点: C# 足够易读,C# 足够灵活定制逻辑,C# 可以动态编译,方便搜集信息,C# 足够强大可以调用其他工具
- 缺点: C# 和
C++ 混合经常搞得有些人糊涂,C++ 项目里混进 C# 没有智能提示不够友好 - 解决: 常见的错误都是这两者报出的,记住常用语法就行了,有问题再查
- UBT
用的是 NMake build system
Build 流程
- 学会链接模块,项目和插件可包含多个模块
- UBT
调用 UHT 生成代码,然后调用 MSBuild 编译代码 - Build.cs
是重点: ModuleRules.cs
当我们右键项目,选择
- UBT(CSharp
写的) 会收集 Source 目录下的 cs(CSharp) 文件,得到我们配置的模块信息 - 调用
UHT(C++ 写的),他是个文本解析工具,不是编译工具,会生成头部信息中的”*.generated.h”,存放在 Intermediate/Build/Win64/UE4Editor/Inc/Module 名 / 目录下 - 调用
MSBuild 编译代码,将 Souce 目录下的 cpp 文件和 UHT 生成的文件编译在一起,生成 dll 文件,存放在 Binaries/Win64/UE4Editor/ 目录下(后面这部分是 Ai 生成,没有验证是否正确)
ModuleRules.cs 模块链接
PublicDependencyModuleNames:
- public
链接的模块名称,最常用 - 在自己的
public 和 private 中包含对方的 public,扩充自己的 public
- public
PrivateDependencyModuleNames:
- private
私有链接的模块名称只引用不暴露 - 在
private 中包含对方的 public,不扩充自己的 public
- private
DynamicallyLoadedModuleNames:
- 动态链接的模块名称,在运行时被
ModuleManager 加载,保证先编译
- 动态链接的模块名称,在运行时被
ModuleRules.cs 头文件 include
PublicIncludePaths:
- public
包含的路径 - 定义自己向外暴露的
public,默认”Public” 和”Classes”
- public
PrivateIncludePaths:
- private
包含的路径 - 定义自己的
private,默认”Private”,给自己内部引用,一般用来定义 private 子目录。当然也可以路径包含 private/sub,但这是一种方便方式
- private
ModuleRules.cs 头文件 include 模块
PublicIncludePathModuleNames:
- public
包含的模块名称,可以只写模块名称,不用写路径
- public
PrivateIncludePathModuleNames:
- private
包含的模块名称,可以只写模块名称,不用写路径
- private
用途
- 只包含对方模块的.h
文件,比如模版 - 更多的是动态链接,先包含头文件信息,之后加载
- 只包含对方模块的.h
ModuleRules.cs 第三方库链接
- PublicAdditionalLibraries:
- 引用的第三方
lib 路径
- 引用的第三方
- PublicSystemLibrariePaths:
- 引用的系统
lib 路径,其实也只是 lib,只不过对于一些更” 系统” 底层的库用这个名字更友好一些
- 引用的系统
- PublicDelayLoadDLLs:
- 延迟加载的
DLL
- 延迟加载的
ModuleRules.cs 其他常用
- PublicDefinitions + PrivateDefinitions:
- 额外的其他
C++ 宏定义
- 额外的其他
- Target
- 得到当前的编译目标信息
- Platform: 当前编译平台,Win64,Android
等 - Configuration: 当前编译配置,Debug,Release
等
举个例子:
如果我们要包含一个文件PublicDependencyModuleNames.AddRange(new string[] { "Engine" });
,这样就可以直接包含这个模块了。
然后我们再去具体的.h#include "Components/BoxComponent.h"
,这部分就是路径
反射 UHT
- 理解.generated.h
和 gen.cpp 的作用 - 理解
MODULENAME_API 的含义,常见的犯错地方,这是用来做 DLL 导出的 - 掌握使用这些宏的含义和用法(反射的重要标记)(ObjectMacros.h)
- UCLASS
- USTRUCT
- UENUM
- UPROPERTY
- UFUNCTION
- UDELEGATE
- GENERATED_BODY()
- 清晰理解类型和对象的关系,类型是类的定义,对象是类的实例
- ClassReference
和 ObjectReference 的区别 - 理解
UCLASS、UScriptStruct、UFunction、UProperty、UField* 的作用 - 掌握通过反射遍历对象属性、读取写入(常见)
- 掌握通过反射遍历对象函数并调用的方式(少一点)
- 通过对象找类型,通过类型找对象的方式
- 理解”
对象用类型描述,类型也是对象” 的概念
CoreUObject
- GC: 理解对象之间的关系,标记清扫,有一些对象是
Root,不会被清扫 - 在用法上可以简单和
C# 类比 - 注意只有
UPROPERTY 标记的才参加 GC!因为要根据 UPROPERTY* 来分析引用链 - 注意
FMyStruct 和 UObject 的混用, FGCObject::AddReferencedObjects(),这个函数是用来添加引用的 - 自从用了
UE4,我再也不 new/delete 了,因为 GC 会自动处理
- 在用法上可以简单和
- CDO: ClassDefaultObject,类的默认对象,用来初始化类的默认属性,理解类型和对象实例化,模版
- 理解
ClassDefaults 作为模版的作用 - 理解
CDO 在序列化中的意义作用 - 通过
UClass::GetDefaultObject() 获取 CDO 信息
- 理解
- Package: 理解对象的相互组织方式
- 对象可以包含子对象
- 序列化时,把一系列对象用一个对象抱起来,这个对象叫做包
- Package
也可以相互引用,根据对象相对路径
套路 1
- UHT
的套路:宏 - 模块链接的套路: 几个常用属性.AddRange()
- Actor
创建的套路: - ConstructorHelpers
- CreateDefaultSubobject
- SetRootComponent
- Gameplay
继承的套路: - 尽量别在关卡蓝图里写逻辑
- 想要结构良好,尽量遵循引擎结构,各司其职
- GI、GM、GS、PC、Pawn、PS、继承一波带走
- 遵循推荐结构,会发现后期扩展和支持联机,天然优势
- C++、BP
交互的套路 - UPROPERTY,UFUNCTION
- C++
定义基类写逻辑,蓝图继承配置可视化,这是一种推荐易扩展高性能的方式 - 函数库是个好东西
- 事件绑定的套路:
- DELEGATE, MULTICAST_DELEGATE, EVENT, DYNAMIC
- Input: BindAction, BindAxis
- EnhancedInput
- Collision: Hit, Overlap, AddDynamic
- Slate&UMG Event: SLATE_EVENT(FOnClicked, OnClicked)
- FTimerManager
引擎常用方法的套路
- Engine/Class/Kismet
有好多库 - GameplayStatics
很常用,可以访问 Gameplay 的很多对象 - UKismetSystemLibrary,系统目录等功能,LineTrace
- UKismetMathLibrary,数学库,常用的数学计算
虚幻 C++ 有哪些学习难点?
- C++ 基础太弱!工具越便利,人们接触底层的机会就越少
- 数据结构算法,操作系统多线程
- 数学太渣:线性代数,向量矩阵,牛顿力学
- 游戏开发的知识不够:渲染,物理,AI,动画
- 项目开发经验不足:不懂得如何设计一个”
足够好” 的项目代码框架 - 知识要点太多:宏太多,API
太多记不住 - 翻找”
借鉴” 代码的能力不足:软件工程软能力不够