小项 | 备注 |
---|---|
时间 | 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
下面我们把注意力放在 FSlot类型上
(1)SCompoundWidget
SCompoundWidget 中含有一个Slot,意味着他可以添加一个 children widget
(2)SLeafWidget
SLeafWidget 中没有Slot成员,也就是不含widget
(3)Spanel
Spanel 中可以含有多个 widget,这个没本身没有加 Slot 结构,但是 继承于他的继承类,会添加自己的 Slot结构,例如 Soverlay、SBoxPanel派生类
(3.1)Soverlay
(3.2)SBoxPanel
(3.2.1)SVerticalBox
(3.2.2)SHorizontalBox
关于FSlot 的作用,在其基类中可以看出,它重载了操作符 [ ],所以我们可以 使用下面这种写法,其添加 childrenwidhet,而控件的父子关系是依托Slot实现的。Slate除了底层的渲染功能实现之外,定义了一套自己的语法,目的就是定义 UI 的层级结构和布局—也就是Slot。
ChildSlot
[
SNew(...)
]
每一个 slot 可以存放一个widget,有的控件只有一个 slot,所以它只有一个孩子,有的控件有多个孩子,就有一个 slotArray对应,而他的排列就跟重写 SWidget基类的 OnArrangeChildren纯虚函数有关了。
1. 控件参数声明
(1)声明
Slate 声明式语法使得开发者可以直接构建用户界面,而不需要添加中间层进行处理。提供了一套 完整的宏 来简化声明及创建控件的过程。
在 宏 SLATE_BEGIN_ARGS 和 宏 SLATE_END_ARGS 之间的声明参数。常用的参数类型有 SLATE_ATTRIBUTE(属性)、SLATE_EVENT(事件)、SLATE_ARGUMENT(参数)、SLATE_NAMED_SLOT(插槽) 和 SLATE_DEFAULT_SLOT
例如:SLATE_ATTRIBUTE(属性)
声明格式:SLATE_ATTRIBUTE(变量类型,变量名)*例如:SLATE_EVENT(事件)
声明格式:SLATE_EVENT(变量类型,变量名)*
注意:这里的变量类型为 UE4 的委托类型实例
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 参数是怎么回事了 :
这些写法 就是在对属性 赋值,并且这些声明的变量都会存在 FArguments 结构下面,
void Construct(const FArgument& InArgs);
在初始化函数可以使用到 SNew 时候传来的参数。
对于想了解 SNew 背后的逻辑的朋友,可以看下面的链接:
(1)风格
在构建时,代码的具体写法中,你可以创建风格,并将其应用到一个 控件的各个部分上,这使得用户界面上迭代处理组件的外观、共享及重用风格变得更加容易。
在UE4中,主要的风格被定义在一下两个源文件中:
- Engine \ Source \ Runtime \ SlateCore \ Public \ Styling \ CoreStyle.h
- Engine \ Source \ Editor \ EditorStyle \ Private \ SlateEditorStyle.cpp
// 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