UE通过UHT编译器插件解析UPROPERTY()宏生成反射代码,要求类继承UObject、字段类型合法、元数据正确,且必须完整重建工程才能生效。

UProperty 在 C++ 里怎么被 UE 看见
UE 的反射系统不是靠运行时扫描符号表,而是靠编译器插件(UnrealHeaderTool)在构建前期解析 C++ 头文件,提取 UPROPERTY() 宏标记的成员变量信息,生成额外的 C++ 代码(比如 StaticClass()、GetDefaultObject() 相关逻辑),再让原生 C++ 类“带反射能力”地参与引擎生命周期。
常见错误现象:UPROPERTY() 写在 private 区域但没加 public: 或 protected: 前缀,UHT 直接跳过;或者用了不支持的类型(比如裸指针 MyStruct* 而非 TObjectPtr<mystruct></mystruct>),导致编译通过但编辑器里不显示、GC 不回收。
-
UPROPERTY()必须写在类定义内,且所在类需继承自UObject或其子类 - 基础类型(
int32、FString)、引擎容器(TArray、TMap)、UObject 派生类指针(UTexture2D*)都支持,但原生 STL 容器(std::vector)和裸 C++ 类型(MyCustomStruct)必须加USTRUCT()且字段也用UPROPERTY() - 编辑器可见性依赖元数据,比如
UPROPERTY(EditAnywhere)才能在细节面板改,漏写元数据就等于“反射了但不可见”
UFunction 怎么让蓝图调用到 C++ 函数
UFUNCTION() 不是魔法钩子,它只是告诉 UHT:“这段函数签名要导出”,UHT 随后生成一个 UFunction 对象描述符,并在类的 StaticClass() 初始化阶段注册进反射表。蓝图调用时,引擎不直接 call C++ 函数指针,而是走统一的 UFunction::Invoke() 调度路径,中间完成参数压栈、GC 引用管理、线程检查等。
使用场景:想让蓝图调用、想在编辑器里设为事件响应、想用 CallFunctionByNameWithArguments 动态触发——这些都强依赖 UFUNCTION() 及其元数据。
立即学习“C++免费学习笔记(深入)”;
- 函数必须是
UCLASS内的成员函数,不能是 static 或 free function - 返回值只能是
void或基本/引擎支持类型(bool、FVector、UObject*),不支持std::optional或引用返回 - 参数列表不能有默认值(
int32 X = 5会报错),也不能有未标记const&的非 UObject 指针参数(UHT 解析失败) - 想在蓝图中暴露为事件,得加
BlueprintImplementableEvent;想允许蓝图重写,得加BlueprintNativeEvent;漏掉这两者之一,蓝图节点就出不来
为什么改了 UPROPERTY 却没出现在编辑器里
不是代码写错,大概率是构建流程卡在中间环节:UHT 没重新运行,或生成的 *.generated.h 文件没被包含,或类没加 UCLASS() 导致整个反射上下文缺失。
性能影响很小,但兼容性陷阱明显:比如在构造函数里直接读 UPROPERTY() 初始值,实际此时反射数据还没加载(PostInitProperties() 才算真正初始化完毕),拿到的是零值;又比如在 BeginPlay() 之前用 GetClass()->FindPropertyByName() 查找字段,可能返回 null——因为 UClass 还没完全初始化。
- 每次修改
UPROPERTY()或UFUNCTION()后,必须重新生成 Visual Studio 工程(GenerateProjectFiles.bat)并完整 rebuild,否则*.generated.h不更新 - 确保头文件末尾有
#include "YourClass.generated.h",且该行必须在所有其他 include 之后 - 如果类从
UObject改成UActorComponent或反过来,记得检查基类宏是否匹配(UCLASS()对应UObject,UCLASS(ClassGroup=...)才支持 Actor 子类)
反射数据什么时候能安全访问
不是所有生命周期都能随便查反射信息。UClass 和 UProperty 指针在 StaticClass() 返回前都是空的;而对象实例的属性值,在 PostInitProperties() 之后才被反射系统真正赋初值(包括 ConstructorHelpers 加载的资源)。
容易踩的坑:在 AActor::AActor() 构造函数里调用 GetClass()->FindPropertyByName(),返回 null;或在 BeginDestroy() 里还试图访问 UPROPERTY() 字段,此时 GC 可能已清空引用。
- 安全访问反射结构体(
UClass、UProperty)的最早时机是StaticClass()调用后,典型位置是UCLASS()宏展开后的静态初始化块 - 安全访问实例属性值的最早时机是
PostInitProperties(),常见做法是在该函数里做反射驱动的初始化(比如根据某个UPROPERTY(EditAnywhere)的值自动创建子组件) - 运行时动态查找字段请用
FindField<UProperty>(Class, TEXT("FieldName")),别硬写Cast<UProperty*>(Class->FindPropertyByName(...))——类型不匹配会 crash
反射不是实时的,它是一次性构建 + 运行时只读查询。最常被忽略的是:你改了 C++ 里的 UPROPERTY() 名字,但蓝图里还连着旧名字的引脚,这时候编辑器不会报错,只会静默断开——得手动重连。

















