虚幻引擎(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  PropIter(GetClass()); PropIter; ++ PropIter)
{
	UProperty* property= *PropIter;
	//todo
}

TFieldIterator的模板参数用作过滤器(因此您可以使用UField或仅使用一个或另一个查看属性和函数)。迭代器构造函数的第二个参数指示您是只希望遍历在指定的类/结构中的字段还是在父类/结构中的字段(默认);它对功能没有任何影响。

你可能感兴趣的:(UE4,C++和蓝图,UE4,VR,UE引擎,反射,RPROPERTY)