UPROPERTY 说明符示例

发布于 2022-08-12  253 次阅读


UPROPERTY 说明符示例

反射是程序在运行时进行自检的一种能力。它非常有用并且是虚幻引擎中的基础技术,支撑了诸如编辑器中的细节面板、序列化、垃圾回收、网络复制、以及蓝图与C++交互等功能。
C++原生并不支持任意形式的反射,因此虚幻引擎有它自己的系统用来利用、查询以及操作关于C++类、结构体、函数 、成员变量以及枚举的信息。

给暴露给反射系统的类型或属性添加注解,这样 Unreal Header Tool (UHT) 就会在编译工程的时候利用那些信息生成特定的代码。目前可以使用 UENUM()、UCLASS()、USTRUCT()、UFUNCTION()、以及 UPROPERTY() 来在头文件中注解不同的类型以及成员变量。

这些宏可以标记在类型或者成员变量的前面,并且可以包含额外的说明符(又称修饰符)和关键字,使用这些说明符,可以基于反射快速的完成一些特殊的功能,对于编辑器开发而言,基于这些说明符,也能让我们快速完成细节面板的开发,因此本文也主要针对 UPROPERTY() 与 UFUNCTION() 在编辑器细节面板开发中常用的几种说明符使用方法进行讨论。

一、热编译

在插件开发的过程中,尤其是 UI 的开发过程中,可以使用 UE 的热编译功能,这样在我们简单的修改了说明符后,不需要重新启动工程就可以查看 UI 变化效果。

热编译控制入口在引擎的右下方,单击此处进入 Settings...

在红框处,可以填写我们需要热编译的插件,提升编译速度。修改后提示重启,重启即可。

重启后在我们在代码里进行修改后,在 VS 或 Rider 按键盘快捷键 Ctrl + Alt + F11 (或点击刚刚右下角的热编译按钮)即可开始热编译,不需要在 IDE 重新编译工程,重启 UE。

二、系统案例

1.系统案例

UE 自己提供了一个 UPROPERTY 说明符的参考案例,但是不知道为什么,无论官方文档,或是其他博客都很少提到这一点,笔者在官方论坛的一条回复里发现了这一功能。
在 UE 的 cmd 键入 testprops,即可打开示例界面

这里的功能应该能满足大部分的开发需求,不能满足的可能需要 DetailCustomization 来完成。
此示例源码位于:Engine\Source\Editor\UnrealEd\Classes\Editor\PropertyEditorTestObject.h

2.说明符定义

说明符定义:Engine\Source\Runtime\CoreUObject\Public\UObject\ObjectMacros.h
系统中定义说明符时添加的注释也挺详细,也可以在这里查找相关用法。

三. 常用说明符总结

笔者记录此文,主要还是想总结下开发中经常用到的一些说明符,也是避免遗忘。

官方文档链接如下,虽为官方文档,但也不全,仅供参考
属性关于为Gameplay类创建和实现属性的参考。

此处文章总结的很全 benui UPROPERTY
不仅给出了示例,很多示例还贴出了 gif,和笔者的示例比起来属实是小巫见大巫了,既然如此,笔者也便不再赘述相关说明符的使用方式,可以查看上述文章。
这里只保留部分链接中未提到的部分。

4.使用自定义 Tag 对需要显示的属性进行过滤

直接反射系统类的细节面板时,可能有很多属性是我们所不需要的,这种时候可能需要对我们想要显示的属性进行过滤,可以通过添加自定义 Tag 的方式来实现。
我们想要的属性标记一个我们自定义的统一的 Tag。
反射生成自定义面板时,指定属性显示的代理绑定到一个控制显隐的函数即可:

DetailsView->SetIsPropertyVisibleDelegate(FIsPropertyVisible::CreateRaw(this, &FXXXCtrl::GetIsPropertyVisible));

函数里,利用我们自定义的统一的 Tag 即可完成过滤。

UPROPERTY(EditDefaultsOnly, meta = (MyCustomTag))
FLinearColor Color;

bool FXXXCtrl::GetIsPropertyVisible(const FPropertyAndParent& PropertyAndParent) const
{
    if (PropertyAndParent.Property.HasMetaData("MyCustomTag"))
    {
        return true;
    }
    return false;
} 
5. 使用 USTRUCT 对数据进行管理

系统中很多的属性会使用 USTRUCT 来进行管理,那么如何使用 USTRUCT 又能保证效果与直接定义在头文件中相同呢?这里与一些说明符的作用机制有关。

  1. 自定义 Tag
    经笔者测试,上文中使用自定义 Tag 过滤显示属性的方式对于 USTRUCT 包裹的属性亦能生效,只有在 USTRUCT 属性说明符与 USTRUCT 内部属性说明符均指定 Tag 的情况下才会被过滤出来。

  2. ShowOnlyInnerProperties
    此说明符用于控制 UPROPERTY struct 属性的层级显示方式,添加此说明符后,struct 内部属性不会以层叠方式展开,而是提升到与外部 Category 一个层级。

    USTRUCT()
    struct FCat
    {
        GENERATED_BODY()
        UPROPERTY(EditDefaultsOnly, meta = (MyCustomTag))
        FString Name;
        UPROPERTY(EditDefaultsOnly, meta = (MyCustomTag))
        int32 Age;
        UPROPERTY(EditDefaultsOnly, meta = (MyCustomTag))
        FLinearColor Color;
    };
    UPROPERTY(EditAnywhere, Category="Cat Without ShowOnlyInnerProperties", meta = (MyCustomTag))
    FCat Cat1;
    UPROPERTY(EditAnywhere, Category="Cat2 With ShowOnlyInnerProperties", meta=(ShowOnlyInnerProperties), meta = (MyCustomTag))
    FCat Cat2;

    效果如下:

  3. 下拉列表

    benui UPROPERTY 文章指出,GetOptions 会从继承关系中寻找可以使用的函数,找到第一个作为返回列表的方法使用,因此我们应该可以在持有 Struct 的 Section 类中定义数组以及返回对应数组的 UFUNCTION,指定 Struct 的 GetOptions 说明符来完成下拉列表的需求。