BlueRoseNote/03-UnrealEngine/UI/Slate学习笔记(一):Slate动态控制与其他技巧.md
2023-06-29 11:55:02 +08:00

199 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Slate学习笔记Slate动态控制与其他技巧
date: 2021-02-19 9:22:10       
tags: Slate
rating: ⭐️
---
## 前言
最近在写MessageBox控件想兼容UMG与c++中调用Slate。略有心得遂有此文。
## 动态Slate
首先Slate控件树中创建占位用的NullWidget并使用.Expose()绑定Slot指针使得可以进行后续操作。
```c++
ChildSlot
.VAlign(VAlign_Fill)
.HAlign(HAlign_Fill)
[
SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Expose(UseMenuSlot) // Expose it to a pointer so we can change the widget at runtime
[
SNullWidget::NullWidget
]
];
```
之后就可以通过函数对Slot进行操作了下面演示如何动态添加或者删除Button
```c++
UseMenuSlot->AttachWidget(
InText.Get().IsEmpty() ?
SNullWidget::NullWidget
:
SNew(SButton)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
[
SNew(STextBlock)
]
);
```
### 支持Expose()的容器
常用的:
SHorizontalBox
SVerticalBox
SOverlay
其他还有若干也支持的
## 设置指定长度的Widget
有的时候需要让控件保持指定的大小使用SBox的WidthOverride与HeightOverride即可。其他的控件都不支持这2个属性。
```c++
ChildSlot
[
SNew(SBox)
.WidthOverride(256)
.HeightOverride(100)
[
SNew(SButton)
]
];
```
## 指定大小控件的UMG与Slate兼容问题
UMG的根节点是一个CanvasPanel(UCanvasPanelSlot)。我认为直接给控件加一个SBox来控制大小的方式不太好。这会使得在UMG中的调整大小变得麻烦。所以我的方法是使用c++调用Slate控件时在外面再套一层SConstraintCanvas。
UCanvasPanelSlot本质上是SConstraintCanvas。UMG编辑器中显示的Size实际上是Offset。所以想要指定大小就需要设置Offset的Right与Bottom。
```c++
CurrentWidget = SNew(SConstraintCanvas)
+ SConstraintCanvas::Slot()
.Anchors(0.5)
.Alignment(FVector2D(0.5,0.5))
.Offset(FMargin(0,0,500,400))
[
SNew(SCustomWidget)
];
GetGameInstance()->GetGameViewportClient()->AddViewportWidgetContent(CurrentWidget.ToSharedRef());
```
## c++获取UMG控件
```c++
UPROPERTY(Meta = (BindWidget)) //直接获取蓝图中的按钮1
UButton* ButtonOne;
```
```c++
bool UFWAffectWidget::Initialize()
{
if (!Super::Initialize()) return false;
//方法1
RootPanel = Cast<UCanvasPanel>(GetRootWidget()); //获取根控件
if (RootPanel)
{
BGImage = Cast<UImage>(RootPanel->GetChildAt(0)); //下标1的BGImage控件
}
//方法2
UButton* ButtonTwo = (UButton*)GetWidgetFromName(TEXT("ButtonTwo"));
//绑定按钮事件方法1
ButtonOne->OnClicked.__Internal_AddDynamic(this,&UFWAffectWidget::ButtonOneEvent,FName("ButtonOneEvent"));
//绑定事件方法2委托FScriptDelegate
FScriptDelegate ButTwoDel;
ButTwoDel.BindUFunction(this, "ButtonTwoEvent");
ButtonTwo->OnReleased.Add(ButTwoDel);
return true;
}
```
参考文章https://blog.csdn.net/weixin_44200074/article/details/109100521
## 从UMG控件中获取Slate
```c++
TSharedRef<SWidget> UWidget::TakeWidget()
```
## 反射
### 遍历UProperty
```c++
for(TFiledIterator<UProperty> It(/*UClass**/)It;++It)
{
UProperty *newProperty=It;
}
```
### 根据名称寻找UProperty
```c++
UProperty *foundProperty = FindField<UProperty>(/*UClass**/,TEXT("PropertyName"));
```
### Get/Set UProperty
```c++
//根据PropertyPath获取属性值
FCachedPropertyPath PropertyPath(FString(TEXT("PropertyName")));
T Value;
ProperthPathHelpers::GetPropertyValue(Object,PropertyPath,Value);
//有获取就有设置
ProperthPathHelpers::SetPropertyValue(Object,PropertyPath,Value);
//还可以获取成String
FString PropertyValueStr;
ProperthPathHelpers::GetPropertyValueAsString(Object,PropertyPath,PropertyValueStr);
//同样,反向也可以
ProperthPathHelpers::SetPropertyValueFromString(Object,PropertyPath,PropertyValueStr);
//实际上我们平时在UE引擎中复制粘贴属性值就是将属性值获取为String以及从String这只属性值的整个过程。
```
引用文章https://zhuanlan.zhihu.com/p/121006155
更多反射技巧https://bebylon.dev/ue4guide/engine-programming/uobject-reflection/uobject-reflection/
## SCompoundWidget、SPanel、SLeafWidget的区别
### SCompoundWidget
SCompoundWidget对应于UMG中的“WidgetBlueprint控件蓝图用来作为控件容器。当我们在C++类向导中创建Slate类时创建的就是SCompoundWidget。
可以通过成员ChildSlot结合重载的[]操作符往控件里面添加其他控件
```c++
void SStandardSlateWidget::Construct(const FArguments& InArgs)
{
ChildSlot
[
SNew(STextBlock)
.Font(FSlateFontInfo("Veranda", 100))
.Text(NSLOCTEXT("HelloSlate", "HelloSlateText", "Hello, Slate!"))
];
}
```
### SPanel
即为SPanel子布局的属性包括Padding、Size、Horizontal Align、Vertical Align。
使用Slot添加子控件可以使用“+ Slot()”也可以调用AddSlot函数还是以教程1中的SStandardSlateWidget为例
```c++
void SStandardSlateWidget::Construct(const FArguments& InArgs)
{
ChildSlot
[
SAssignNew(VerticalBoxPtr, SVerticalBox)
+ SVerticalBox::Slot()
.Padding(1.0)
.FillHeight(0.3f)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
[
SNew(SButton)
]
+ SVerticalBox::Slot()
.FillHeight(0.5f)
.HAlign(HAlign_Center)
[
SNew(SButton)
]
];
VerticalBoxPtr->AddSlot()
.FillHeight(0.2f)
[
SNew(SButton)
];
}
```
### SLeafWidget
叶子控件故名思意该控件不能添加子控件。常用的为STextBlock与SImage。