UE5 反射系统

UE5 反射系统详解


一、什么是反射系统?

在 C++ 中,语言本身不提供运行时类型信息(RTTI)的完整支持,无法在运行时获取一个类有哪些属性、哪些方法、属性叫什么名字、类型是什么等信息。

UE5 自己实现了一套完整的运行时反射系统,核心能力包括:

  • 运行时获取类的属性、方法、类型信息
  • 支持序列化 / 反序列化(存档、网络同步)
  • 支持蓝图与 C++ 互通
  • 支持垃圾回收(GC)追踪 UObject 引用
  • 支持编辑器属性面板(Details Panel)自动显示

二、核心机制:UHT + 宏标记

UE5 反射系统的实现分两个阶段:编译前预处理运行时访问

2.1 UHT(Unreal Header Tool)

UHT 是 UE 的代码生成工具,在正式编译 C++ 之前运行。

流程如下:

你写的 .h 文件
    ↓ UHT 扫描宏标记
生成 xxx.generated.h 和 xxx.gen.cpp
    ↓ 一起参与 C++ 编译
最终二进制包含完整反射数据

每个启用反射的头文件末尾必须包含:

#include "MyClass.generated.h"  // UHT 生成的文件,必须是最后一个 include

2.2 核心宏

用途
UCLASS() 标记一个类参与反射
USTRUCT() 标记一个结构体参与反射
UENUM() 标记一个枚举参与反射
UPROPERTY() 标记一个成员变量参与反射
UFUNCTION() 标记一个成员函数参与反射
GENERATED_BODY() 插入 UHT 生成的模板代码(必须写在类体最开头)

示例:

UCLASS(BlueprintType, Blueprintable)
class MYGAME_API AMyActor : public AActor
{
    GENERATED_BODY()

public:
    // 暴露给蓝图读写,并在编辑器面板显示
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
    float Health;

    // 暴露给蓝图调用
    UFUNCTION(BlueprintCallable, Category = "Stats")
    void TakeDamage(float Amount);
};

三、运行时类型系统:UClass / UStruct / UEnum

UHT 生成的数据最终以 UClassUScriptStructUEnum 等对象的形式存在于内存中,这些对象本身也是 UObject

3.1 UClass

每个 UCLASS 标记的类,在运行时都对应一个 UClass 实例:

// 获取 UClass 对象
UClass* ClassInfo = AMyActor::StaticClass();

// 运行时动态获取(从实例)
UClass* ClassInfo = SomeActor->GetClass();

// 打印类名
UE_LOG(LogTemp, Log, TEXT("Class: %s"), *ClassInfo->GetName());

UClass 内部存储了:

  • 所有 UPROPERTY → 以 FProperty 链表形式存储
  • 所有 UFUNCTION → 以 UFunction 对象存储
  • 父类指针(支持继承链查询)
  • CDO(Class Default Object,类默认对象)

3.2 CDO(Class Default Object)

每个 UCLASS 都有一个 CDO,是该类在引擎启动时创建的默认实例,用于:

  • 存储属性默认值
  • 序列化时做差量存储(只保存与 CDO 不同的部分)
  • 编辑器中"重置为默认值"的数据来源
AMyActor* CDO = GetDefault<AMyActor>();
UE_LOG(LogTemp, Log, TEXT("Default Health: %f"), CDO->Health);

四、属性遍历(运行时读写属性)

反射系统最实用的能力之一:在不知道具体类型的情况下,遍历和读写属性

void PrintAllProperties(UObject* Obj)
{
    UClass* Class = Obj->GetClass();

    // 遍历所有 UPROPERTY
    for (TFieldIterator<FProperty> It(Class); It; ++It)
    {
        FProperty* Prop = *It;
        FString PropName = Prop->GetName();

        // 以 float 为例,读取属性值
        if (FFloatProperty* FloatProp = CastField<FFloatProperty>(Prop))
        {
            float Value = FloatProp->GetPropertyValue_InContainer(Obj);
            UE_LOG(LogTemp, Log, TEXT("%s = %f"), *PropName, Value);
        }
    }
}

常用的 FProperty 子类:

类型 对应 FProperty 子类
float FFloatProperty
int32 FIntProperty
FString FStrProperty
bool FBoolProperty
UObject* FObjectProperty
TArray<> FArrayProperty
TMap<> FMapProperty

五、函数调用(运行时动态调用 UFUNCTION)

// 按名字找到函数
UFunction* Func = Obj->FindFunction(FName("TakeDamage"));

if (Func)
{
    // 构造参数结构体(内存布局必须与函数签名匹配)
    struct FParams
    {
        float Amount;
    };

    FParams Params { 50.0f };
    Obj->ProcessEvent(Func, &Params);
}

ProcessEvent 是 UE 反射系统调用函数的底层入口,蓝图调用 C++ 函数、RPC 网络调用本质上都走这条路。


六、类型检查与转换

UE 提供了自己的类型转换,不推荐用原生 dynamic_cast

// 安全向下转型(失败返回 nullptr)
AMyActor* MyActor = Cast<AMyActor>(SomeActor);

// 判断是否是某个类或其子类
if (SomeActor->IsA(AMyActor::StaticClass()))
{
    // ...
}

// 判断两个 UClass 的继承关系
bool bIsChild = ChildClass->IsChildOf(ParentClass);

七、UPROPERTY 修饰符详解

UPROPERTY 的修饰符控制了属性的可见性、可编辑性、蓝图访问权限、网络同步等行为:

UPROPERTY(
    EditAnywhere,        // 在任何地方的 Details 面板可编辑
    BlueprintReadWrite,  // 蓝图可读可写
    Replicated,          // 网络同步
    Category = "Combat", // 编辑器分类
    meta = (ClampMin = "0.0", ClampMax = "100.0")  // 元数据限制
)
float Health;

常用修饰符速查:

修饰符 含义
EditAnywhere 原型和实例都可在编辑器编辑
EditDefaultsOnly 只能在原型(CDO)编辑
EditInstanceOnly 只能在场景实例编辑
VisibleAnywhere 编辑器只读显示
BlueprintReadWrite 蓝图可读写
BlueprintReadOnly 蓝图只读
Replicated 网络同步
Transient 不序列化(不存档)
SaveGame 参与存档系统

八、反射与垃圾回收的关系

UE 的 GC 依赖反射系统追踪 UObject 引用:

  • 只有被 UPROPERTY 标记的 UObject* 指针,GC 才能感知到引用关系
  • 裸指针(没有 UPROPERTY)不会被 GC 追踪,对象可能被回收导致野指针
// ❌ 危险:GC 不追踪这个指针,对象可能被回收
UMyObject* RawPtr;

// ✅ 安全:GC 追踪此引用,对象存活
UPROPERTY()
UMyObject* SafePtr;

九、总结

宏标记(UCLASS/UPROPERTY/UFUNCTION)
        ↓
    UHT 预处理
        ↓
  生成反射元数据(.gen.cpp)
        ↓
运行时 UClass / FProperty / UFunction 对象
        ↓
支撑:蓝图互通 / 编辑器面板 / 序列化 / GC / 网络同步 / 动态调用

UE5 的反射系统是整个引擎的基础设施,理解它能帮助你更好地理解蓝图原理、网络同步、存档系统、编辑器扩展等几乎所有上层系统的工作方式。

posted @ 2026-04-16 20:51  Reed_Chill  阅读(204)  评论(0)    收藏  举报