虚幻引擎(UE4) 反射系统(Reflection)

举报
Dummy 发表于 2021/11/19 00:57:06 2021/11/19
【摘要】 反射是程序在运行时检查自身的能力。这是非常有用的,并且是虚幻引擎的基础技术,可以为许多系统提供动力,例如编辑器中的详细信息面板,序列化,垃圾收集,网络复制以及Blueprint / C ++通信。但是,C ++本身不支持任何形式的反射,因此Unreal拥有自己的系统来收集,查询和操纵有关C ++类,结构,函数,成员变量和枚举的信息。我们...

反射是程序在运行时检查自身的能力。这是非常有用的,并且是虚幻引擎的基础技术,可以为许多系统提供动力,例如编辑器中的详细信息面板,序列化,垃圾收集,网络复制以及Blueprint / C ++通信。但是,C ++本身不支持任何形式的反射,因此Unreal拥有自己的系统来收集,查询和操纵有关C ++类,结构,函数,成员变量和枚举的信息。我们通常将反射称为属性系统,因为反射也是图形术语。

反射系统是可选的。您需要注释任何您希望在反射系统中可见的类型或属性,虚幻解析工具(UHT)会在编译项目时获取该信息。

 

标记

要将.h头文件标记为包含反射类型,请在头文件顶部添加一行特殊的包含目录:#include“ FileName.genic.h”并且需要将此包含项放在其他include之后。这使UHT知道他们应该考虑该文件,并且对于系统的实现也是必需的。

现在,您可以使用宏:UENUM(),UCLASS(),USTRUCT(),UFUNCTION()和UPROPERTY()在头文件中注释不同类型的成员变量和函数。每个宏都位于类型或成员声明之前,并且可以包含其他说明符关键字。让我们看一个真实的例子(来自StrategyGame):


   
  1. //移动单元(士兵)的基类
  2. #include“ StrategyTypes.h”
  3. #include“ StrategyChar.genic.h”
  4. UCLASS(描述)
  5. class AStrategyChar : public ACharacter, public IStrategyTeamInterface
  6. {
  7. GENERATED_UCLASS_BODY()
  8. /** Pawn死亡后获得多少资源收益 */
  9. UPROPERTY(EditAnywhere, Category = “Pawn”)
  10. int32 ResourcesToGather;
  11. /** 设置武器插槽附件 */
  12. UFUNCTION(BlueprintCallable, Category=“Attachment”)
  13. void SetWeaponAttachment(class UStrategyAttachment* Weapon);
  14. UFUNCTION(BlueprintCallable, Category=“Attachment”)
  15. bool IsWeaponAttached();
  16. protected:
  17. /** 进展动画 */
  18. UPROPERTY(EditDefaultsOnly, Category=“Pawn”)
  19. UAnimMontage* MeleeAnim;
  20. /** 装备插槽 */
  21. UPROPERTY()
  22. UStrategyAttachment* ArmorSlot;
  23.   /** 团队编号 */
  24. uint8 MyTeamNum;
  25. };

此头文件声明了一个继承自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除外),因为生成的代码会引用它们,并且在定义不正确的任何配置中都会导致编译错误。

 

使用反射

大多数游戏代码可以在运行时忽略属性系统,从而享受其功能强大的系统的好处,但是在编写工具代码或构建游戏系统时,您可能会发现它很有用。

反射属性系统的类型层次结构如下所示:


  
  1. UField
  2. UStruct
  3. UClass(C ++类)
  4. UScriptStruct(C ++结构)
  5. UFunction(C ++函数)
  6. UEnum(C ++枚举)
  7. UProperty(C ++成员变量或函数参数)
  8. (许多不同类型的子类)

UStruct是复合结构的基本类型,因此不应该与C++结构(UScriptStruct)混淆。UClass可以包含函数或属性作为其子级,而UFunction和UScriptStruct仅限于属性。

你可以用过调用UTypeName::StaticClass()或FTypeName::StaticStruct()来获取对象的类型(UClass或UScriptStruct),同时你也可以用过实例Instance->GetClass()来获取UObject的类型。遍历UStruct的所有成员,使用TFieldIterator


   
  1. for (TFieldIterator <UProperty> PropIter(GetClass()); PropIter; ++ PropIter)
  2. {
  3. UProperty* property= *PropIter;
  4. //todo
  5. }

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

文章来源: blog.csdn.net,作者:呦呦鹿鸣.,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/zhang1461376499/article/details/113337105

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。