虚幻的反射
反射是虚幻引擎中的基础技术,虚幻中的细节面板、序列化、垃圾回收、蓝图与C++交互等功能都依赖于反射。但原生C++并不支持反射,因此虚幻拥有一套自己的反射系统。
本文将对虚幻的反射机制进行浅析,更多信息可以查看工程文件中的generated和Marcos源码文件,里面有对反射更详细的解释。
反射的简要机制
Unreal Build Tool(UBT)和Unreal Header Tool(UHT)两个协同工作来生成运行时反射需要的数据。UBT通过扫描头文件,记录任何至少有一个反射类型的头文件的模块。如果其中任意一个头文件从上一次编译起发生了变化,那么UHT就会被调用来利用和更新反射数据。UHT分析头文件,创建一系列反射数据,并且生成包含反射数据的C++代码,放到每一个模块的generated文件中,使用标记宏标记的数据编译后可以在generated文件中看到,使用了一些宏对数据进行包装。
用生成的C++代码来存储反射数据的一个最大好处就是,它可以保证跟二进制做到同步,不会加载陈旧或者过时的反射数据,因为它是跟引擎的其它代码同时编译的,并且它会在程序启动的时候使用C++表达式来计算成员偏移等,而不是通过针对特定平台/编译器/优化的组合中进行逆向工程。
反射数据的层次
属性系统的层次结构:
UStruct是聚合体的基础类型。
UClass继承自UStruct对应原生C++中的class,包括Property、Function、ChildClass等。
UScriptStruct对应原生C++中的struct,仅仅包括Property。
UFunction对应原生C++中的Function,同样仅仅包括Property。
UEnum对应原生C++中的enum
UProperty对应原生C++中的变量和函数参数
反射支持
UObject为反射系统的核心,每一个继承自UObject且支持反射系统的类型都有一个相对应的UClass类或其子类,如果要实现类支持反射,必须遵守相关的规则,比如使用UCLASS(),USTRUCT(),UENUM(),UPROPERTY(),FUNCTION()等标记宏进行标记,以便UHT可以根据宏生成支持反射的代码。
UCLASS的反射支持
分为两步完成:
- 通过IMPLEMENT_CLASS()这个宏用于启动时注册这个类,包括生成UClass类以及注册C++原生函数。
- 通过static FCompiledInDeter创建一个静态变量,向DeferredCompiledInRegistration这个静态数组中添加注册函数以初始化默认反射属性(包括Func,Property,metaData)。
USTRUCT的反射支持
同样是通过两步完成:
- 通过static FCompiledInDeferStruct存储一个用于构建结构体的一个单例函数,用于在程序启动时调用。
- 此外还会创建一个静态对象,这个对象在构造函数中调用UScriptStruct::DeferCppStructOps函数,它用来向这DeferredCppStructOps注册一个动态管理结构体构造析构的类。
UENUM的反射支持
枚举实现支持只有一步:
通过static FCompiledInDeferEnum创建一个静态变量存储一个用于构建枚举反射对象的单例函数,用于在程序启动时调用。
关于反射宏可以在ObjectMacros.h中查看。
注册
所有的注册内容都会在ProcessNewlyLoadedUObjects()函数中进行注册。
这个函数中包含以下内容:
UClassRegisterAllCompiledInClasses()用于注册所有要加载的类。
在IDE中源码解释为:Register all loaded classes
UObjectProcessRegistrants()用于处理自动注册的对象,并添加到UObject array中用于后期检索。
在IDE中源码解释为:Process the auto register objects adding them to the object array
UObjectLoadAllCompiledInStructs()用于注册结构体和玫瑰的反射信息。
在IDE中源码解释为:Call StaticStruct for each struct…this sets up the internal singleton and important works correctly with hot-reload
该函数还与热重载有关
UObjectLoadAllCompiledInDefaultProperties()用于注册类的反射信息并创建一个默认对象。
在IDE中源码解释为:Load any outstanding compiled in default properties
UClassReplaceHotReloadClass()用于完成C++的热重载。
在IDE中源码解释为:Re-instance all existing classes that have change during hot-reload
总结
虚幻的反射构建可分为以下四个阶段:
生成阶段:借助UHT生成UClass代码,包括UClass构造、注册属性以及函数。
收集阶段:利用Static自动注册的方式,在模块加载时将所有UClass登记并添加入array中管理。
注册阶段:在模块初始化时将array中所有UClass中的Function和Property注册。
链接阶段:生成动态链接库,编辑器加载该库。
更多内容可以查看官方文档:https://docs.unrealengine.com/5.2/zh-CN/reflection-system-in-unreal-engine/
- 本文作者: KongXinQing
- 本文链接: https://13114987559.github.io/2023/09/23/essay/虚幻反射机制/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!