UC++ 之 反射机制
一、什么是反射机制?
反射的概念是由 Smith 在1982年首次提出的。
主要是指程序在 运行时 可以访问、检测和修改其本身状态或行为的一种能力。
二、UE 的反射机制
UE中的反射机制主要用于以下几个方面:
1.序列化:反射可以帮助保存和加载游戏状态,包括游戏进度、配置设置等。
2.编辑器的细节面板:通过反射,编辑器可以显示和编辑对象的所有属性。
3.垃圾回收:UE的垃圾回收系统可以通过反射来跟踪哪些对象正在被使用,哪些对象可以安全地删除。
4.网络复制:在网络游戏中,反射被用来同步不同玩家的游戏状态。
5.蓝图/C++通信和相互调用:反射允许蓝图(UE的视觉脚本系统)和 C++ 代码相互通信和调用。
6.事件系统:可以在蓝图中创建自定义事件,并在C++代码中触发这些事件。
7.插件系统:通过反射,插件可以在运行时被动态加载,并且可以访问和修改游戏的状态。
8.控制台命令:可以在运行时通过控制台输入命令来修改游戏的状态。
9.属性绑定:可以使用反射来创建属性绑定。如将UI元素(如文本和滑块)绑定到游戏状态,从而实时更新。
10.细节面板:在 UE Editor 中(开启那刻就算程序运行了)对各种Actor的属性进行修改。
三、反射系统简单分析
1. 反射宏标记
编码过程中,使用 UCLASS()、USTRUCT()、UENUM()、UFUNCTION()、UPROPERTY() 等宏来标记类和成员变量,可以将其添加到反射系统;之后,基于 UE 反射的 垃圾回收(GC)系统等可以管理我们的反射数据。
在宏标记中,我们可以选择性的添加标识符,以决定被反射数据可以在哪里使用、如何被使用。
有选择性添加标识符,可以减少不必要的反射数据,这样做可以节省元数据所占用的内存。
举例:主动用 UPROPERTY 标记成员,GC 会帮我们管理该成员指针,或者使用 AddToRoot 方法,可以防止GC。
1.1 UPROPERTY
添加后,公开成员变量给蓝图。
注意:嵌套模板和部分类型不能被反射,如TArray、TMap的嵌套。
使用方法:
在属性声明前使用

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Combat")
float Damage;
常用属性说明符(更多的可以查看官方文档)

1.2 UFUNCTION
公开函数方法给蓝图,可以实现蓝图与C++的交互调用。
使用方法:
在函数声明前使用
UFUNCTION(BlueprintCallable, Category="Damage")
void CalculateValues();
常用属性说明符(更多的可以查看官方文档)

若在C++中提供默认实现方法,实现方法的命名需要类似于<函数名>_Implementation()
void AMyActor::CalledFromCpp_Implementation()
{
// 这里添加实现代码
}
2. 反射数据
被反射的类或变量等数据将会被保存在元数据当中。
通过元数据对象,我们可以获取到被反射类或成员的所有数据,
比如类名、继承关系、包含的成员变量、类型信息等。元数据类的继承关系如下:

2.1 简要继承图


2.2 获取元数据

获取类和结构体的元数据
- 对于继承 UObject 类的子类,我们可以使用静态方法 StaticClass() 获取该类的反射对象(元数据)。
- 对于结构体,可以使用 StaticStruct() 获取结构体的反射数据。
- 继承 UObject 类的实例,也可以使用 GetClass() 来获取元数据,而结构体对象则不可以。
获取被反射的成员变量的元数据
继承 UStruc t的 UClass 和 UScriptStruct 是聚合结构体的元数据;
可以通过它们得到类包含的成员,例如遍历获取类包含的被反射的成员变量:
for(TFieldIterator<UProperty> It(GetClass()); It; ++It)
{
UProperty* Prop = *It;
}
获取被反射函数方法的元数据
如果要获取类的反射方法,可以调用类对象的FindFunction 方法或者类元数据的 FindFunctionByName 方法来获取反射方法的元数据。
UFunction* Func = ObjectSelf->FindFunction(FName FuncName);
UFunction* Func = ObjectSelf->GetClass()->FindFunctionByName(FName FuncName);
获取被反射枚举的元数据
通过 FindObject 可以获取任意的 UObject 类对象,枚举的反射数据被保存在 UEnum 类实例中,而UObject又是UEnum的祖父类。所以,可以通过 FindObject 来获取枚举类的反射数据对象。
//所所有包中查找名为EnumName的枚举类的反射数据,并精准匹配。
UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, *EnumName, true);
3. 反射离不开的两个工具:UBT(UnrealBuildTool)、UHT(UnrealHeadTool)
UE 的反射是宏反射,有大量的代码使用了宏定义,两个工具分别负责预处理宏替换、编译配置和链接。
UBT 编译和链接时使用
- 主要负责编译和链接源代码,将代码转换为可执行的游戏或应用程序;
- 集成了编译器和链接器,可以与各种不同的编译器(如 MSVC、Clang、GCC 等)进行交互,完成代码的编译和链接过程。
UHT 预处理时使用
- 在预处理阶段,UHT 会解析头文件中的标记宏(如 UCLASS、USTRUCT 等),并根据这些宏生成对应的元数据。
- 生成的临时文件通常以 .generated.h 后缀命名,包含类声明、结构体声明、枚举声明等信息。
- 用于帮助编译器和链接器正确处理和链接对象的属性、方法等内容。

浙公网安备 33010602011771号