前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UE4 Slate三 SlateUI代码讲解

UE4 Slate三 SlateUI代码讲解

作者头像
全栈程序员站长
发布2022-11-09 17:20:53
1.6K0
发布2022-11-09 17:20:53
举报

原创文章,转载请注明出处。

点击观看上一篇《UE4 Slate二 用UMG思想去理解Slate+Slate编码》 点击观看下一篇《UE4 Slate四 SlateUI如何做UI动画》

虚幻引擎 SlateUI介绍

1>前言

本篇文章主要是介绍Slate的编码,针对上一篇文章末尾的.h和.cpp做描述 点击观看上一篇《UE4 Slate二 用UMG思想去理解Slate+Slate编码》 先看下上篇文章代码的效果图

在这里插入图片描述
在这里插入图片描述

2>该继承自哪个基类来写呢?SCompoundWidget/SPanel/SLeafWidget

都知道UMG里面我们会继承自UUserWidget做一个自己的基类来写,那么Slate里面应该继承自谁? 在这个部分我们要了解一个插槽的概念: Slot概念很重要, 插槽就是说我们在他(控件)下面可以添加多少个子控件,下面有三个我们写Slate会经常继承的类。

2.1>基类:SUserWidget或者是SCompoundWidget

单个Slot的类,单个插槽。SUserWidget 或者 SCompoundWidget有一个插槽, 固定一个 比如 SCheckBox,SButton, SBox SUserWidget是继承自SCompoundWidget,这两个我们都可以继承,引擎更多的是继承自SCompoundWidget。其实引擎推荐我们自己写的单个插槽类的时候是继承自SUserWidget。 Slate二讲解中我们是继承自SCompoundWidget的,也一样的。

2.2>基类:SPanel

多个Slot的类,多个插槽。SPanel: 多个插槽 比如SVerticalBox, SHorizontalBox,SScrollBox

2.3>基类:SLeafWidget

没有插槽, 比如STextBlock

3>该如何新建一个S类组件呢?SNew和SAssignNew

在UMG里面我们是UButton* pBtn = NewObject, 那么Slate里面我们是如何构造控件? 可以把SNew和SAssignNew就理解成我们平常调用的NewObject,只不过它在slate里面就是要这么写。因为Slate都是S类,非U类。从这点也说明了我们头文件中为什么都是智能指针包着的S类。

这个是引擎内的SUserWidget给我们抛来的Demo代码

代码语言:javascript
复制
	 TSharedRef<SButton> MyButton = SNew(SButton);
 *        or
 *     TSharedPtr<SButton> MyButton;
 *     SAssignNew( MyButton, SButton );

3.1>SNew和SAssignNew

比如我们要新建一个Button,分别用SNew和SAssignNew写一下。 注意TSharedRef不能在.h的类内声明, UE4智能指针->智能指针详细介绍链接

代码语言:javascript
复制
TSharedRef<SButton> MyButton = SNew(SButton);
代码语言:javascript
复制
TSharedPtr<SButton> MyButton;
SAssignNew( MyButton, SButton );

直接说比较含蓄,需要写一下才知道 SNew: 1>返回值不同:返回共享引用 SAssignNew: 1>返回值不同:返回共享指针 2>使用方式不同:链式编程中直接获取值,直接赋值,在链式编程中想获取值就用SAssignNew

3.2>链式编程优缺点

优点: 1>效率比UMG要高,因为UMG封装的就是Slate 缺点: 1>不能断点调试,断点无法命中链式内部 2>编写界面制作麻烦且不易维护

4>代码入口,如何在这个插件里面将SMainSlate显示到我们的插件面板内

因为我们是基于UE4 Plugin创建了一个Editor Standlone Window, 在这个插件代码中(test5_EditorStandlonWindow.cpp)中的OnSpawnPluginTab()方法中, 就是创建Slate的部分,所以我们在这个位置写就好了。

代码语言:javascript
复制
TSharedRef<SDockTab> Ftest5_EditorStandlonWindowModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{ 

FText WidgetText = FText::Format(
LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"),
FText::FromString(TEXT("Ftest5_EditorStandlonWindowModule::OnSpawnPluginTab")),
FText::FromString(TEXT("test5_EditorStandlonWindow.cpp"))
);
//第一种方式
MyMainSlate = SNew(SMainSlate);
return SNew(SDockTab)
[
MyMainSlate->AsShared()
];
//第二种方式
/*SAssignNew(MyMainSlate, SMainSlate); return SNew(SDockTab) [ MyMainSlate->AsShared() ];*/
//第三种方式
/*return SNew(SDockTab) [ SNew(SMainSlate) ];*/
//第四种方式
/*return SNew(SDockTab) [ SAssignNew(MyMainSlate, SMainSlate) ];*/
//原来的代码注释掉了
//.TabRole(ETabRole::NomadTab)
//[
// // Put your tab content here!
// SNew(SBox)
// .HAlign(HAlign_Center)
// .VAlign(VAlign_Center)
// [
// SNew(STextBlock)
// .Text(WidgetText)
// ]
//];
}

5>Slate编程写法,以继承自SCompoundWidget的类举例

5.1>空类中必须要有如下的代码

代码语言:javascript
复制
//这个类的作用就是去用Slate代码实现一次这个UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')
class SMainSlate : public SCompoundWidget /*public SUserWidget*/
{ 

public:
//SLATE_BEGIN_ARGS+SLATE_END_ARGS 其实是一个结构体, 内部写的东西都相当于写在了一个结构体里面
SLATE_BEGIN_ARGS(SMainSlate)
{ 

}
SLATE_END_ARGS()
//外部执行SNew或者SAssignNew时候会调用Construct()
void Construct(const FArguments& InArgs);

5.2>宏讲解 SLATE_BEGIN_ARGS(){} SLATE_END_ARGS()

这个呢,其实就是一个结构体, 内部写的东西都相当于写在了一个结构体里面。 为什么要有这个? 为了方便我们在外部进行SNew/SAssignNew之后能直接传参数过来,写法是如下: 下面SNew完了之后的通过 .点出来的参数其实都是在基类的上面宏内(结构体)定义的 用用就明白了,关于里面定义参数的宏,有很多,可以转到Engine里面去看。 后面会介绍Widget拾取器,可以更便捷的找到我们想看的Engine_Slate的实现位置。

代码语言:javascript
复制
SNew(SButton)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.ContentPadding(0.f)
.OnClicked(this, &SMainSlate::OnFirstSButton_OnClicked)	//事件绑定的技巧, 转到定义, 看那边的代理是怎么定义的, 把参数和返回值拿过来定义一个函数即可
.OnPressed(this, &SMainSlate::OnFirstSButton_OnPressed)
.OnReleased(this, &SMainSlate::OnFirstSButton_OnReleased)
.OnHovered(this, &SMainSlate::OnFirstSButton_OnHovered)
.OnUnhovered(this, &SMainSlate::OnFirstSButton_OnUnhovered)

5.3>真正去构件我们的SlateUI,对照着UMG来写

这里介绍一下在SConstraintCanvas里面新建一个STextBlock,不会针对每一个控件都讲一下(太多啦)。其他的在我上传的代码里面都有。 建议自己做一个UMG,然后用Slate你去实现一下试试。很多就都明白了。

5.3.1>首先我们要在Construct()函数里面作为入口开写

代码语言:javascript
复制
void SMainSlate::Construct(const FArguments& InArgs)
{ 

5.3.2>在构造里面敲出ChildSlot[],在[]内进行S类的控件创建

代码语言:javascript
复制
ChildSlot
[
SNew(S类………………)
.属性设置
[
SNew(被上面S类包裹的子控件)
.子控件属性设置
[
//如果有想New的继续写
]
]
];

5.3.3> 对应我们的参考UMG,剖析代码

在这里插入图片描述
在这里插入图片描述

可以看到,我们的UMG最上层有一个Canvas Panel(UCanvasPanel类型),那么我们在代码中最开也去创建一个Canvas Panel(用它的主要目的是为了调整坐标方便)。

5.3.3.1> 如何找到U类用的是哪个S类?也就是说我们要SNew哪一个类

那么我们如何确定UCanvasPanel这个控件到底用的是哪一个S类呢? 1>首先在UMG编辑器中左侧UI列表上选中我们的Canvas Panel(UCanvasPanel类型), 2>然后再详细面板中跳转到我们这个类型的C++代码里面, 3>再Ctrl+End到头文件底部,就发现了一个被智能指针包裹的S类,没错,就是它了。 4>TIPS:通过这个方式我们确定了这个UCanvasPanel所用的S类是SConstraintCanvas,通过这种方式也可以找到其他UMG控件的实现S类。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

找到了SConstraintCanvas之后,所以就有了我们的创建SConstraintCanvas代码

5.3.3.2> 在ChildSlot[]中创建一个SConstraintCanvas(UMG里面的UCanvasPanel)
代码语言:javascript
复制
ChildSlot
[
SAssignNew(CanvasPanel_0, SConstraintCanvas)
//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的<Mesh合并界面>文本
+ SConstraintCanvas::Slot()
.Anchors(0.f)	//对应UMG这个文本控件上的Anchors属性,拷贝过来即可
.Offset(FMargin(20.0, 12.f, 600, 72))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
.AutoSize(false)			//对应AutoSize
.ZOrder(0)
[
SNew(STextBlock)
.Text(LOCTEXT("SMainSlate_TextBlock1", "Mesh合并界面"))
.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 53))
.ColorAndOpacity(FSlateColor(FLinearColor(1, 0, 0.361307, 1)))
]

CanvasPanel_0是我们在.h中定义的

代码语言:javascript
复制
//最外层的层, 就是我们默认创建UMG带的层
TSharedPtr<class SConstraintCanvas> CanvasPanel_0;
5.3.3.3> 在我们的SConstraintCanvas加一个(SNew一个)文本出来

我们想在CanvasPanel_0加一个STextBlock,写法如上(+ SConstraintCanvas::Slot()),没啥原因,按着写就好了。 但是参数是什么意思呢? 一般我们会使用(下面这句代码) 的方式做为添加子控件的开始

代码语言:javascript
复制
+ SConstraintCanvas::Slot()

然后直接通过.点出来一些属性,调整我们这个控件的属性,

代码语言:javascript
复制
+ SConstraintCanvas::Slot()
.Anchors(0.f)	//对应UMG这个文本控件上的Anchors属性,拷贝过来即可
.Offset(FMargin(20.0, 12.f, 600, 72))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
.AutoSize(false)			//对应AutoSize
.ZOrder(0)
5.3.3.4> 关于创建子控件的参数对应UMG做介绍
5.3.3.4.1> Anchors

比如下面的Anchors就是锚点,对应UMG的下图

在这里插入图片描述
在这里插入图片描述
5.3.3.4.2> Offset

比如下面的Offset就是FMargin(PositionX,PositionY,SizeX,SizeY) ,对应UMG的下图

在这里插入图片描述
在这里插入图片描述
5.3.3.4.3> Alignment

.Alignment(FVector2D(0.f, 0.f)),对应UMG的下图

在这里插入图片描述
在这里插入图片描述
5.3.3.4.4> AutoSize

.AutoSize(false) //对应AutoSize,对应UMG的下图

在这里插入图片描述
在这里插入图片描述
5.3.3.4.5> ZOrder

.ZOrder(0),对应UMG的下图

在这里插入图片描述
在这里插入图片描述
5.3.3.5> 创建子控件

然后再再[]中添加我们的控件,比如 像一些文本颜色字体的设置都是如下的写法。其他的S类比如会有其他的属性和事件绑定,也是类似的写法。

代码语言:javascript
复制
[
SNew(STextBlock)
.Text(LOCTEXT("SMainSlate_TextBlock1", "Mesh合并界面"))
.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 53))
.ColorAndOpacity(FSlateColor(FLinearColor(1, 0, 0.361307, 1)))
]
5.3.3.6> S类的事件绑定
5.3.3.6.1> 举例SButton的Event绑定

该部分需要了解UE4代理->代理详细介绍链接 分别对OnClicked/OnPressed/OnReleased/OnHovered/OnUnhovered都做了绑定 .h中

代码语言:javascript
复制
protected:
//SButton的事件绑定
FReply OnFirstSButton_OnClicked();
void OnFirstSButton_OnPressed();
void OnFirstSButton_OnReleased();
void OnFirstSButton_OnHovered();
void OnFirstSButton_OnUnhovered();

cpp中

代码语言:javascript
复制
SNew(SButton)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.ContentPadding(0.f)
.OnClicked(this, &SMainSlate::OnFirstSButton_OnClicked)	//事件绑定的技巧, 转到定义, 看那边的代理是怎么定义的, 把参数和返回值拿过来定义一个函数即可
.OnPressed(this, &SMainSlate::OnFirstSButton_OnPressed)
.OnReleased(this, &SMainSlate::OnFirstSButton_OnReleased)
.OnHovered(this, &SMainSlate::OnFirstSButton_OnHovered)
.OnUnhovered(this, &SMainSlate::OnFirstSButton_OnUnhovered)
[
SNew(STextBlock)
.Text(LOCTEXT("SMainSlate_Button_Text1", "---->"))
.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
]
代码语言:javascript
复制
FReply SMainSlate::OnFirstSButton_OnClicked()
{ 

GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnClicked"));
++nDynamicAddNum;
FString s2 = TEXT("new add") + FString::FromInt(nDynamicAddNum);
MyScrollBoxLeft->AddSlot()
[
SNew(STextBlock)
.Text(FText::FromString(s2))
.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
.ColorAndOpacity(FSlateColor(FLinearColor(1, 0, 1, 1)))
];
MyScrollBoxRight->AddSlot()
[
SNew(STextBlock)
.Text(FText::FromString(s2))
.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
.ColorAndOpacity(FSlateColor(FLinearColor(1, 1, 0, 1)))
];
return FReply::Handled();
}
void SMainSlate::OnFirstSButton_OnPressed()
{ 

//UE_LOG(LogTemp, Warning, TEXT("OnFirstSButton_OnPressed"));
GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnPressed"));
}
void SMainSlate::OnFirstSButton_OnReleased()
{ 

GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnReleased"));
}
void SMainSlate::OnFirstSButton_OnHovered()
{ 

GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnHovered"));
}
void SMainSlate::OnFirstSButton_OnUnhovered()
{ 

GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnUnhovered"));
}
5.3.3.6.2> 举例SCheckBox的Event绑定

我一共创建了三个CheckBox,并通过SAssignNew在链式编程里面将返回值放到了我们TArray数组中, 这个就是要用SAssignNew的情况。

代码语言:javascript
复制
//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的<CheckBox_1>
+ SConstraintCanvas::Slot()
.Anchors(0.f)
.Offset(FMargin(1260.0, 172.0, 600.0, 40.0))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
.AutoSize(false)			//对应AutoSize
.ZOrder(0)
[
SAssignNew(CheckBoxArray[0], SCheckBox)
.IsChecked(false)
.IsEnabled(true)
.OnCheckStateChanged(this, &SMainSlate::MyOnCheckStateChanged<0>)
[
SNew(STextBlock)
.Text(LOCTEXT("SMainSlate_checkbox_Text1", "按材质合并成SingleMesh"))
.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
]
]

我希望在三个checkbox之中同时只有一个被选中,所以有了下面的逻辑。 .h中

代码语言:javascript
复制
//SChechBox的事件绑定
template<int32 T>
void MyOnCheckStateChanged(ECheckBoxState emState)
{ 

if (CheckBoxArray.IsValidIndex(T))
{ 

for (int32 i = 0; i < CheckBoxArray.Num(); i++)
{ 

if (i==T)
{ 

if (CheckBoxArray[i].IsValid() || CheckBoxArray[i].Get())
{ 

CheckBoxArray[i]->SetIsChecked(emState);
}
}
else
{ 

if (CheckBoxArray[i].IsValid() || CheckBoxArray[i].Get())
{ 

CheckBoxArray[i]->SetIsChecked(ECheckBoxState::Unchecked);
}					
}
}
}
}

点击观看上一篇《UE4 Slate二 用UMG思想去理解Slate+Slate编码》 点击观看下一篇《UE4 Slate四 SlateUI如何做UI动画》

谢谢,创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 <( ̄︶ ̄)>

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/186068.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月4日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 虚幻引擎 SlateUI介绍
  • 1>前言
  • 2>该继承自哪个基类来写呢?SCompoundWidget/SPanel/SLeafWidget
    • 2.1>基类:SUserWidget或者是SCompoundWidget
      • 2.2>基类:SPanel
        • 2.3>基类:SLeafWidget
        • 3>该如何新建一个S类组件呢?SNew和SAssignNew
          • 3.1>SNew和SAssignNew
            • 3.2>链式编程优缺点
            • 4>代码入口,如何在这个插件里面将SMainSlate显示到我们的插件面板内
            • 5>Slate编程写法,以继承自SCompoundWidget的类举例
              • 5.1>空类中必须要有如下的代码
                • 5.2>宏讲解 SLATE_BEGIN_ARGS(){} SLATE_END_ARGS()
                  • 5.3>真正去构件我们的SlateUI,对照着UMG来写
                    • 5.3.1>首先我们要在Construct()函数里面作为入口开写
                    • 5.3.2>在构造里面敲出ChildSlot[],在[]内进行S类的控件创建
                    • 5.3.3> 对应我们的参考UMG,剖析代码
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档