虚幻引擎(UE4) 反射系统(Reflection)
反射是程序在运行时检查自身的能力。这是非常有用的,并且是虚幻引擎的基础技术,可以为许多系统提供动力,例如编辑器中的详细信息面板,序列化,垃圾收集,网络复制以及Blueprint / C ++通信。但是,C ++本身不支持任何形式的反射,因此Unreal拥有自己的系统来收集,查询和操纵有关C ++类,结构,函数,成员变量和枚举的信息。我们通常将反射称为属性系统,因为反射也是图形术语。
反射系统是可选的。您需要注释任何您希望在反射系统中可见的类型或属性,虚幻解析工具(UHT)会在编译项目时获取该信息。
标记
要将.h头文件标记为包含反射类型,请在头文件顶部添加一行特殊的包含目录:#include“ FileName.genic.h”,并且需要将此包含项放在其他include之后。这使UHT知道他们应该考虑该文件,并且对于系统的实现也是必需的。
现在,您可以使用宏:UENUM(),UCLASS(),USTRUCT(),UFUNCTION()和UPROPERTY()在头文件中注释不同类型的成员变量和函数。每个宏都位于类型或成员声明之前,并且可以包含其他说明符关键字。让我们看一个真实的例子(来自StrategyGame):
//移动单元(士兵)的基类 #include“ StrategyTypes.h” #include“ StrategyChar.genic.h” UCLASS(描述) class AStrategyChar : public ACharacter, public IStrategyTeamInterface { GENERATED_UCLASS_BODY() /** Pawn死亡后获得多少资源收益 */ UPROPERTY(EditAnywhere, Category = “Pawn”) int32 ResourcesToGather; /** 设置武器插槽附件 */ UFUNCTION(BlueprintCallable, Category=“Attachment”) void SetWeaponAttachment(class UStrategyAttachment* Weapon); UFUNCTION(BlueprintCallable, Category=“Attachment”) bool IsWeaponAttached(); protected: /** 进展动画 */ UPROPERTY(EditDefaultsOnly, Category=“Pawn”) UAnimMontage* MeleeAnim; /** 装备插槽 */ UPROPERTY() UStrategyAttachment* ArmorSlot; /** 团队编号 */ uint8 MyTeamNum; };
此头文件声明了一个继承自ACharacter的新类,称为AStrategyChar。它使用UCLASS()来表示它已被反射,它也与下面的类AStrategyChar定义中的宏GENERATED_UCLASS_BODY()配对。反射类或结构中需要GENERATED_UCLASS_BODY()或者GENERATED_USTRUCT_BODY()宏,因为这样做,虚幻将会把引擎额外的内容(包括函数和Typedef宏)注入到类主体中。
显示的第一个属性是ResourcesToGather,它用EditAnywhere和Category = Pawn注释。这意味着该属性可以在编辑器的任何详细信息面板中进行编辑,并将显示在Pawn类别中。带有BlueprintCallable函数,这意味着可以从蓝图Blueprints中调用它们。
MyTeamNum未申明反射属性,这里要注意,非反射属性对于所有依赖反射的系统都是不可见的(例如,存储未反射的原始UObject指针)通常很危险,因为垃圾收集器看不见你的引用)。
每个说明符关键字(例如EditAnywhere或BlueprintCallable)都在ObjectMacros.h中进行枚举,并对含义或用法进行简短注释。如果不确定关键字的作用,选中关键字,Alt + G通常可以带您进入ObjectMacros.h中的定义(它们不是真正的C ++关键字,但是Intellisense或VAX依然能带你查看定义)。
常见的UPROPERTY说明符关键字如下:
-
Category:“类别”。指定变量属于哪个类别,这个非常有用,当你使用C++派生一个蓝图类时,蓝图中显示太多的成员变量会让你眼花缭乱,Category会让这些变量被归类并显示到蓝图中,让你很快就能查找到变量。
-
EditAnywhere:任何地方可编辑。声明了此关键字,你将会在蓝图中的任何地方编辑此变量的值。否则不能编辑它。
-
VisibleAnywhere:任何地方可见。声明了此关键字,你将会在蓝图的任何地方可见此变量。否则是不可见的。
-
BlueprintReadOnly:蓝图中只读。
-
BlueprintReadWrite:蓝图中可读可写。
-
BlueprintCallable:蓝图中可以调用。
常见的UFUNCTION说明符关键字如下:
-
Category:和UPROPERTY中的Category关键字一样,唯一不同就是这里是为函数分类,上面是成员变量分类。
-
BlueprintCallable:蓝图中可以调用。
局限性
UHT不是真正的C ++解析器。它能理解该语言的一部分,并积极地尝试跳过所有可能的文本。只关注反射的类型,函数和属性。但是,有些事情仍让会它迷惑,因此在将反射类型添加到现有头文件时,您可能不得不重新编写某些单词或将其包装在 #if CPP ... #endif 中。您还应该避免在任何带注释的属性或函数周围使用#if /#ifdef(WITH_EDITOR和WITH_EDITORONLY_DATA除外),因为生成的代码会引用它们,并且在定义不正确的任何配置中都会导致编译错误。
使用反射
大多数游戏代码可以在运行时忽略属性系统,从而享受其功能强大的系统的好处,但是在编写工具代码或构建游戏系统时,您可能会发现它很有用。
反射属性系统的类型层次结构如下所示:
-
UField
-
UStruct
-
UClass(C ++类)
-
UScriptStruct(C ++结构)
-
UFunction(C ++函数)
-
-
UEnum(C ++枚举)
-
-
UProperty(C ++成员变量或函数参数)
-
-
(许多不同类型的子类)
UStruct是复合结构的基本类型,因此不应该与C++结构(UScriptStruct)混淆。UClass可以包含函数或属性作为其子级,而UFunction和UScriptStruct仅限于属性。
你可以用过调用UTypeName::StaticClass()或FTypeName::StaticStruct()来获取对象的类型(UClass或UScriptStruct),同时你也可以用过实例Instance->GetClass()来获取UObject的类型。遍历UStruct的所有成员,使用TFieldIterator :
-
for (TFieldIterator <UProperty> PropIter(GetClass()); PropIter; ++ PropIter)
-
{
-
UProperty* property= *PropIter;
-
//todo
-
}
TFieldIterator的模板参数用作过滤器(因此您可以使用UField或仅使用一个或另一个查看属性和函数)。迭代器构造函数的第二个参数指示您是只希望遍历在指定的类/结构中的字段还是在父类/结构中的字段(默认);它对功能没有任何影响。
文章来源: blog.csdn.net,作者:呦呦鹿鸣.,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/zhang1461376499/article/details/113337105
- 点赞
- 收藏
- 关注作者
评论(0)