Unreal Slate框架随笔

2019-12-26  本文已影响0人  Mr_约科
小项 备注
时间 2019 年 12 月 26 日
修改时间 2020 年 3 月 22 日、2020年4月11日
城市 北京市房山区
天气
心情 凑合

UE4 的 编辑器都是建立在 Slate整个框架下,包括UE4 用于Runtime游戏的 UMG 这套这是基于Slate系统的。

Slate用户界面系统为开发者提供了 引擎源码 和 编辑器Editor 的直接访问权(开发者工具——Widget Reflecor)

0. Slate控件(S前缀的类)继承关系【SlateCore模块】

至于为什么是S开头的类,戳下面官方的链接:

https://docs.unrealengine.com/en-US/Programming/Introduction/index.html

继承关系.png

下面我们把注意力放在 FSlot类型上

(1)SCompoundWidget

SCompoundWidget 中含有一个Slot,意味着他可以添加一个 children widget

SCompoundWidget.png

(2)SLeafWidget

SLeafWidget 中没有Slot成员,也就是不含widget

SLeafWidget.png

(3)Spanel

Spanel 中可以含有多个 widget,这个没本身没有加 Slot 结构,但是 继承于他的继承类,会添加自己的 Slot结构,例如 Soverlay、SBoxPanel派生类

(3.1)Soverlay
SOverlay.png
(3.2)SBoxPanel
SBoxPanel.png
(3.2.1)SVerticalBox
SVerticalBox.png
(3.2.2)SHorizontalBox
SHorizontalBox.png

关于FSlot 的作用,在其基类中可以看出,它重载了操作符 [ ],所以我们可以 使用下面这种写法,其添加 childrenwidhet,而控件的父子关系是依托Slot实现的。Slate除了底层的渲染功能实现之外,定义了一套自己的语法,目的就是定义 UI 的层级结构和布局—也就是Slot。

ChildSlot
    [
        SNew(...)
    ]
TSlotBase.png

每一个 slot 可以存放一个widget,有的控件只有一个 slot,所以它只有一个孩子,有的控件有多个孩子,就有一个 slotArray对应,而他的排列就跟重写 SWidget基类的 OnArrangeChildren纯虚函数有关了。

多个Slot.png

1. 控件参数声明

(1)声明

Slate 声明式语法使得开发者可以直接构建用户界面,而不需要添加中间层进行处理。提供了一套 完整的宏 来简化声明及创建控件的过程。
宏 SLATE_BEGIN_ARGS宏 SLATE_END_ARGS 之间的声明参数。常用的参数类型有 SLATE_ATTRIBUTE(属性)、SLATE_EVENT(事件)、SLATE_ARGUMENT(参数)、SLATE_NAMED_SLOT(插槽) 和 SLATE_DEFAULT_SLOT

DECLARE_DELEGATE_OneParam(FMyEvent, FString);

class EXSLATEWIDGET_API SMyCompoundWidget : public SCompoundWidget
{
public:
        SLATE_BEGIN_ARGS( SSubMenuButton )
            : _ShouldAppearHovered( false )
            {
 
            }
          
            SLATE_ATTRIBUTE( bool, ShouldAppearHovered )

            SLATE_ATTRIBUTE( FString, Label )
          
            SLATE_EVENT( FOnClicked, OnClicked )
         
            SLATE_NAMED_SLOT( FArguments, FSimpleSlot, Content )

            SLATE_EVENT(FMyEvent, MyEvent)       
        SLATE_END_ARGS()
};

(2)初始化

按上面用相应的宏声明之后,实际上参数名变为"_" + "宏里面的变量名",如上面 “ShouldAppearHovered ” 参数名为“_ShouldAppearHovered ”. SCompoundWidget的参数初始化和C++类差不多,如同下面:

SLATE_BEGIN_ARGS(SSubMenuButton )
{
    _ShouldAppearHovered = false ;
}

SLATE_BEGIN_ARGS(SSubMenuButton ):
        _ShouldAppearHovered ( false )
    {
 
    }

而在 上述的实例中采用了第二种初始化的方式。

2. 构建对象

通过 宏 声明的参数 与 类成员变量 之间的转移(参数 与 变量的区别)

在构建 Slate 对象的时候,传入的参数,并不是直接通过 上面我们通过宏去定义的那些参数去直接执行的,上面定义的那些参数 仅仅是为了 方便传递,所以还需要在 定义类的 成员变量(或委托变量)
上面实例中的类,重新定义为:

以 Label 这个声明参数为例,在类中增加了 这个声明参数 对应的 成员变量

DECLARE_DELEGATE_OneParam(FMyEvent, FString);

class EXSLATEWIDGET_API SMyCompoundWidget : public SCompoundWidget
{
public:
        SLATE_BEGIN_ARGS( SSubMenuButton )
            : _ShouldAppearHovered( false )
            {
 
            }
          
            SLATE_ATTRIBUTE( bool, ShouldAppearHovered )

            SLATE_ATTRIBUTE( FString, Label )
          
            SLATE_EVENT( FOnClicked, OnClicked )
         
            SLATE_NAMED_SLOT( FArguments, FSimpleSlot, Content )

            SLATE_EVENT(FMyEvent, MyEvent)       
        SLATE_END_ARGS()

public:
        void Construct(const FArguments& InArgs);

private:
        FString Label ;

};

/////////////////////////////////////////////////////////
void SMyCompoundWidget::Construct(const FArguments& InArgs)
{
         
        Label = InArgs._Label ;
        /*
        ChildSlot
        [
            // Populate the widget
        ];
        */

}

实际构建的代码写法

ContextualEditingWidget->AddSlot()
.Padding( 2.0f )
[
    SNew( SDetailSection )
    .SectionName("StaticMeshSection")
    .SectionTitle( LOCTEXT("StaticMeshSection", "Static Mesh").ToString() )
    .Content()
    [
        SNew( SVerticalBox )
        + SVerticalBox::Slot()
        .Padding( 3.0f, 1.0f )
        [
            SNew( SHorizontalBox )
            + SHorizontalBox::Slot()
            .Padding( 2.0f )
            [
                SNew( SComboButton )
                .ButtonContent()
                [
                    SNew( STextBlock )
                    .Text( LOCTEXT("BlockingVolumeMenu", "Create Blocking Volume") ) 
                    .Font( FontInfo )
                ]
                .MenuContent()
                [
                    BlockingVolumeBuilder.MakeWidget()
                ]
            ]
        ]

    ]
];

看 一下 的定义,就知道这些写法,以及 Construct 函数中 FArguments 参数是怎么回事了 :

宏的定义.png

这些写法 就是在对属性 赋值,并且这些声明的变量都会存在 FArguments 结构下面,
void Construct(const FArgument& InArgs);
在初始化函数可以使用到 SNew 时候传来的参数。

对于想了解 SNew 背后的逻辑的朋友,可以看下面的链接:

https://www.yuanmas.com/info/rxzKZA9ORe.html

(1)风格

在构建时,代码的具体写法中,你可以创建风格,并将其应用到一个 控件的各个部分上,这使得用户界面上迭代处理组件的外观、共享及重用风格变得更加容易。

在UE4中,主要的风格被定义在一下两个源文件中:

// Tool bar
{
    Set( "ToolBar.Background", FSlateBoxBrush( TEXT("Common/GroupBorder"), FMargin(4.0f/16.0f) ) );

    Set( "ToolBarButton.Normal", FSlateNoResource() );      // Note: Intentionally transparent background
    Set( "ToolBarButton.Pressed", FSlateBoxBrush( TEXT("Old/MenuItemButton_Pressed"), 4.0f/32.0f ) );
    Set( "ToolBarButton.Hovered", FSlateBoxBrush( TEXT("Old/MenuItemButton_Hovered"), 4.0f/32.0f ) );

    // Tool bar buttons are sometimes toggle buttons, so they need styles for "checked" state
    Set( "ToolBarButton.Checked", FSlateBoxBrush( TEXT("Old/MenuItemButton_Pressed"),  4.0f/32.0f, FLinearColor( 0.3f, 0.3f, 0.3f ) ) );
    Set( "ToolBarButton.Checked_Hovered", FSlateBoxBrush( TEXT("Old/MenuItemButton_Hovered"),  4.0f/32.0f ) );
    Set( "ToolBarButton.Checked_Pressed", FSlateBoxBrush( TEXT("Old/MenuItemButton_Pressed"),  4.0f/32.0f, FLinearColor( 0.5f, 0.5f, 0.5f ) ) );

    // Tool bar button label font
    Set( "ToolBarButton.LabelFont", FSlateFontInfo( TEXT("Roboto-Regular"), 8 ) );
}

构建的代码写法改为

SNew( SBorder )
.BorderImage( FEditorStyle::GetBrush( "ToolBar.Background" ) )
.Content()
[
    SNew(SHorizontalBox)

    // Compile button (faked to look like a multibox button)
    +SHorizontalBox::Slot()
    [
        SNew(SButton)
        .Style(TEXT("ToolBarButton"))
        .OnClicked( InKismet2.ToSharedRef(), &FKismet::Compile_OnClicked )
        .IsEnabled( InKismet2.ToSharedRef(), &FKismet::InEditingMode )
        .Content()
        [
            SNew(SVerticalBox)
            +SVerticalBox::Slot()
            .Padding( 1.0f )
            .HAlign(HAlign_Center)
            [
                SNew(SImage)
                .Image(this, &SBlueprintEditorToolbar::GetStatusImage)
                .ToolTipText(this, &SBlueprintEditorToolbar::GetStatusTooltip)
            ]
            +SVerticalBox::Slot()
            .Padding( 1.0f )
            .HAlign(HAlign_Center)
            [
                SNew(STextBlock)
                .Text(LOCTEXT("CompileButton", "Compile"))
                .Font( FEditorStyle::GetFontStyle( FName( "ToolBarButton.LabelFont" ) ) )
                .ToolTipText(LOCTEXT("CompileButton_Tooltip", "Recompile the blueprint"))
            ]
        ]
    ]
]

参考文章
https://docs.unrealengine.com/zh-CN/Programming/Slate/index.html
https://docs.unrealengine.com/zh-CN/Engine/UMG/index.html
https://blog.csdn.net/qq_29523119/article/details/98475938
https://blog.csdn.net/pizi0475/article/details/50471207
https://zhuanlan.zhihu.com/p/56127773

上一篇 下一篇

猜你喜欢

热点阅读