【UE4 C++】Slate 初探: Editor UI 与 Game UI
概述
名词区分
- Slate
- Slate 是完全自定义、与平台无关的UI框架
- 应用
- 可用于编辑器UI,编辑器的大部分界面都是使用 Slate 构建的
- 可做为游戏UI
- 可作为独立应用开发
- 只能 C++ 开发
- 可以调用 UMG,使用TakeWidget()
- HUD
- HUD通常只显示,不互动
- 可绘制文本、线条等
- GameMode 设置
- 可创建 UMG、Slate
- UMG (Unreal Motion Graphics)
- UMG是基于原先的Slate封装开发的GUI
- 可在编辑设计,支持蓝图、C++访问
- 支持访问 Slate
Slate 框架
逻辑层部分 Slate、SlateCore
渲染部分 SlateRHIRenderer
基类为 SWidget
Slot 为槽,代表可以放置 子 Widget

Slate 的使用
声明性语法——宏
SLATE_BEGIN_ARGS( SSubMenuButton )
: _ShouldAppearHovered( false )
{}
/** 将显示在按钮上的标签 */
SLATE_ATTRIBUTE( FString, Label )
/** 单击按钮时调用 */
SLATE_EVENT( FOnClicked, OnClicked )
/** 将放置在按钮上的内容 */
SLATE_NAMED_SLOT( FArguments, FSimpleSlot, Content )
/** 在悬停状态下是否应显示按钮 */
SLATE_ATTRIBUTE( bool, ShouldAppearHovered )
SLATE_END_ARGS()
SNew
- SNew( SlateWidget 类名 ),返回TSharedRef
- SNew(SWeakWidget).PossiblyNullContent()
SAssignNew
- SAssignNew( SlateWidget 智能指针,SlateWidget 类名),返回TSharedPtr.
- SAssignNew(SWidget, SWeakWidget).PossiblyNullContent()
创建 Editor Slate
从三类插件了解
创建插件

点击事件代码对比

控件展示案例,更改插件 MyEditorMode 代码
\Engine\Source\Runtime\AppFramework\Private\Framework\Testing路径下的文件,拷贝至 插件Plugins\MyEditorMode\Source\MyEditorMode\Private- SUserWidgetTest.h
- SUserWidgetTest.cpp
- SWidgetGallery.h
- SWidgetGallery.cpp
- TestStyle.h
- TestStyle.cpp
- vs 添加文件,或者右键工程 Generate Visual Stuido project files
- 编译不通过
头文件问题,将头文件改成当前目录下的头文件
变量重名问题,注释掉相应的变量声明
LNK2019: 无法解析的外部符号 GetTestRenderTransform(void) 和 GetTestRenderTransformPivot(void),SWidgetGallery.cpp 中 MakeWidgetGallery 函数注释掉相关语句,如下所示。
TSharedRef<SWidget> MakeWidgetGallery()
{
//extern TOptional<FSlateRenderTransform> GetTestRenderTransform();
//extern FVector2D GetTestRenderTransformPivot();
return
SNew(SWidgetGallery);
//.RenderTransform_Static(&GetTestRenderTransform)
//.RenderTransformPivot_Static(&GetTestRenderTransformPivot);
}
修改 MyEditorMode .cpp 种的 OnSpawnPluginTab 函数
TSharedRef<SDockTab> FMyEditorModeModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{
FTestStyle::ResetToDefault();
TSharedPtr<SWidget> ToolkitWidget; return SNew(SDockTab)
.TabRole(ETabRole::NomadTab)
[
// Put your tab content here!
SAssignNew(ToolkitWidget, SBorder)
[
MakeWidgetGallery()
]
];
}

创建 Runtime Slate
.build.cs 添加依赖模块(如果自带,可以取消注释)
PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
创建类
图示

创建 HUD派生类:AMyHUD
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "MyHUD.generated.h"
UCLASS()
class DESIGNPATTERNS_API AMyHUD : public AHUD
{
GENERATED_BODY()
public:
virtual void BeginPlay() override; void ShowMySlate();
void RemoveMySlate(); // 没有 include "MyCompoundWidget",而使用 class ,避免头文件相互引用而编译错误
TSharedPtr<class SMyCompoundWidget> MyCompoundWidget; // 添加视口方法三
TSharedPtr<SWidget> WidgetContainer;
};
#pragma once
#include "MyHUD.h"
#include "Kismet/GameplayStatics.h"
#include "SMyCompoundWidget.h"
#include "Widgets/SWeakWidget.h" void AMyHUD::BeginPlay()
{
Super::BeginPlay();
ShowMySlate();
} void AMyHUD::ShowMySlate()
{
if (GEngine && GEngine->GameViewport)
{
// 第二个参数为 ZOrder,默认为 0
//GEngine->GameViewport->AddViewportWidgetContent(SNew(SMyCompoundWidget), 0);
//GEngine->GameViewport->AddViewportWidgetContent(SAssignNew(MyCompoundWidget, SMyCompoundWidget)); //
MyCompoundWidget = SNew(SMyCompoundWidget).OwnerHUDArg(this);
//SAssignNew(MyCompoundWidget, SMyCompoundWidget); // 添加视口方法一,可被移除
//GEngine->GameViewport->AddViewportWidgetContent(MyCompoundWidget.ToSharedRef()); // 添加视口方法二,此处无法移除,因为 weak widget
//GEngine->GameViewport->AddViewportWidgetContent(
//SNew(SWeakWidget).PossiblyNullContent(MyCompoundWidget.ToSharedRef()), 0); // 添加视口方法三,可被移除
GEngine->GameViewport->AddViewportWidgetContent(
SAssignNew(WidgetContainer,SWeakWidget).PossiblyNullContent(MyCompoundWidget.ToSharedRef()), 0); // 显示鼠标及设置输入模式
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
if (PC)
{
PC->bShowMouseCursor = true;
PC->SetInputMode(FInputModeUIOnly());
}
}
} void AMyHUD::RemoveMySlate()
{
if (GEngine && GEngine->GameViewport && WidgetContainer.IsValid())
{
// 移除添加视口方法一
GEngine->GameViewport->RemoveViewportWidgetContent(MyCompoundWidget.ToSharedRef()); // 移除添加视口方法三
GEngine->GameViewport->RemoveViewportWidgetContent(WidgetContainer.ToSharedRef()); // 移除所有
//GEngine->GameViewport->RemoveAllViewportWidgets(); // 显示鼠标及设置输入模式
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
if (PC)
{
PC->bShowMouseCursor = false;
PC->SetInputMode(FInputModeGameOnly());
}
}
}
创建SCompoundWidget 派生类:SMyCompoundWidget
#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
#include "MyHUD.h" /**
*
*/
class DESIGNPATTERNS_API SMyCompoundWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SMyCompoundWidget)
{}
// 添加参数
SLATE_ARGUMENT(TWeakObjectPtr<AMyHUD>, OwnerHUDArg);
SLATE_END_ARGS() /** Constructs this widget with InArgs */
void Construct(const FArguments& InArgs); FReply OnPlayClicked() const;
FReply OnQuitClicked() const; private:
TWeakObjectPtr<AMyHUD> OwnerHUD;
};
#include "SMyCompoundWidget.h"
#include "SlateOptMacros.h"
#include "Widgets/Images/SImage.h"
#include "MyHUD.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "Widgets/Layout/SBackgroundBlur.h"
#define LOCTEXT_NAMESPACE "MyNamespace" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SMyCompoundWidget::Construct(const FArguments& InArgs)
{
// 注意此处带下划线
OwnerHUD = InArgs._OwnerHUDArg;
// 文本和按钮间距设置
const FMargin ContentPadding = FMargin(500.0f, 300.0f);
const FMargin ButtonPadding = FMargin(10.f);
// 按钮和标题文本
const FText TitleText = LOCTEXT("SlateTest", "Just a Slate Test");
const FText PlayText = LOCTEXT("PlayGame", "Play");
const FText QuitText = LOCTEXT("QuitGame", "Quit Game");
//按钮字体及大小设置
FSlateFontInfo ButtonTextStyle = FCoreStyle::Get().GetFontStyle("EmbossedText");
ButtonTextStyle.Size = 40.f;
//标题字体及大小设置
FSlateFontInfo TitleTextStyle = ButtonTextStyle;
TitleTextStyle.Size = 60.f; //所有UI控件都写在这里
ChildSlot
[
SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Fill).VAlign(VAlign_Fill)
[
SNew(SImage) // 背景(半透明黑)
.ColorAndOpacity(FColor(0,0,0,127))
] + SOverlay::Slot()
.HAlign(HAlign_Fill).VAlign(VAlign_Fill)
[
SNew(SBackgroundBlur) // 高斯模糊
.BlurStrength(10.0f)
] + SOverlay::Slot()
.HAlign(HAlign_Fill).VAlign(VAlign_Fill)
.Padding(ContentPadding)
[
SNew(SVerticalBox) // Title Text
+ SVerticalBox::Slot()
[
SNew(STextBlock)
.Font(TitleTextStyle)
.Text(TitleText)
.Justification(ETextJustify::Center)
] // Play Button
+ SVerticalBox::Slot()
.Padding(ButtonPadding)
[
SNew(SButton)
.OnClicked(this, &SMyCompoundWidget::OnPlayClicked)
[
SNew(STextBlock)
.Font(ButtonTextStyle)
.Text(PlayText)
.Justification(ETextJustify::Center)
]
] // Quit Button
+ SVerticalBox::Slot()
.Padding(ButtonPadding)
[
SNew(SButton)
.OnClicked(this, &SMyCompoundWidget::OnQuitClicked)
[
SNew(STextBlock)
.Font(ButtonTextStyle)
.Text(QuitText)
.Justification(ETextJustify::Center)
]
]
]
]; } FReply SMyCompoundWidget::OnPlayClicked() const
{
if (OwnerHUD.IsValid())
{
OwnerHUD->RemoveMySlate();
}
return FReply::Handled();
} FReply SMyCompoundWidget::OnQuitClicked() const
{
if (OwnerHUD.IsValid())
{
OwnerHUD->PlayerOwner->ConsoleCommand("quit");
}
return FReply::Handled();
} END_SLATE_FUNCTION_BUILD_OPTIMIZATION #undef LOCTEXT_NAMESPACE
创建 GameModeBase派生类:AMyPlayerController ,PlayerController派生类:AMyPlayerController
- 设定 PlayerControllerClass 为 AMyPlayerController
- 设定 HUDClass 为AMyHUD
- 关卡 World Setting->GameMode Override 设置为 MyGameMode
UCLASS()
class DESIGNPATTERNS_API AMyPlayerController : public APlayerController
{
GENERATED_BODY()
}; UCLASS()
class DESIGNPATTERNS_API AMyGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
AMyGameMode() {
PlayerControllerClass = AMyPlayerController::StaticClass();
HUDClass = AMyHUD::StaticClass();
}
};

查看工具
实际写 Slate 的时候,可以多参考下源码 Engine\Source\Runtime\Slate\Public\Widgets\
显示扩展点

Widget Reflector

参考
【UE4 C++】Slate 初探: Editor UI 与 Game UI的更多相关文章
- UE4/Unity3d 根据元数据自动生成与更新UI
大家可能发现一些大佬讲UE4,首先都会讲类型系统,知道UE4会根据宏标记生成一些特定的内容,UE4几乎所有高级功能都离不开这些内容,一般来说,我们不会直接去使用它. 今天这个Demo内容希望能加深大家 ...
- UE4之Slate: SImage
概述 距离上次记录<UE4之Slate:纯C++工程配置>后已经好长时间了: 这个随笔来记录并分享一下SImage控件的使用,以在屏幕上显示一张图片: 目标 通过SImage控件的展示,学 ...
- iPhone/iPad/Android UI尺寸规范 UI尺寸规范,UI图标尺寸,UI界面尺寸,iPhone6尺寸,iPhone6 Plus尺寸,安卓尺寸,iOS尺寸
iPhone/iPad/Android UI尺寸规范 UI尺寸规范,UI图标尺寸,UI界面尺寸,iPhone6尺寸,iPhone6 Plus尺寸,安卓尺寸,iOS尺寸 iPhone界面尺寸 设备 分辨 ...
- 学习通过Thread+Handler实现非UI线程更新UI组件
[Android线程机制] 出于性能考虑,Android的UI操作并不是线程安全的,这就意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则 ...
- HTML5 UI框架Kendo UI Web中如何创建自定义组件(二)
在前面的文章<HTML5 UI框架Kendo UI Web自定义组件(一)>中,对在Kendo UI Web中如何创建自定义组件作出了一些基础讲解,下面将继续前面的内容. 使用一个数据源 ...
- iframeWin For Easy UI. 为 Easy UI 扩展的支持IFrame插件
iframeWin For Easy UI. 为 Easy UI 扩展的支持IFrame插件 在一个项目中用了Easy UI,但是发现里面的 Dialog .Window.Messager 弹窗都不支 ...
- Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面
Android应用的开发过程中需要把繁重的任务(IO,网络连接等)放到其他线程中异步执行,达到不阻塞UI的效果. 下面将由浅入深介绍Android进行异步处理的实现方法和系统底层的实现原理. 本文介绍 ...
- 2D UI和3D UI的工作原理
2D UI的工作原理 UI控件的位置在UI Root 的红框(视窗)上,也就是UI控件的z轴,相机的z轴,UI Root的z轴都是0,因为2D UI都是纯粹的2D图片按层次显示,不会不出现三维立体效果 ...
- 创新高性能移动 UI 框架-Canvas UI 框架
WebView 里无法获得的能力虽然是「体验增强」与「端基本能力」,但现都基本上有成熟解决方法.但后期的 UI 和 Layout 的性能反而是目前 Web 技术欠缺的.所以,无论是 Titanium ...
随机推荐
- MySQL主主互备不同步的解决方法
MySQL主主互备不同步 首先在服务器上执行show slave satus;可以看到很多同步的参数: Master_Log_File: SLAVE中的I/O线程当前正在读取的主服务器二进制日志文件的 ...
- 从环境搭建到打包使用TypeScript
目录 1.TypeScript是什么 2.TypeScript增加了什么 3.TypeScript环境的搭建 4.TypeScript的基本类型 5.TypeScrip编译选项 6.TypeScrip ...
- ElasticSearch集群的安装(windows)
首先尽量保持你的磁盘空间足够大,比如你下载的软件的放在D盘,D盘尽量保持10G以上,还有C盘也差不多10G以上比较保险 一.下载 1)目前我下载的版本是elasticsearch-7.12.0-win ...
- python库--pandas--部分实例
>>> pd.pivot( index=np.array(['one', 'one', 'one', 'two', 'two', 'two']), columns=np.array( ...
- mac下使用vscode技巧
1.查找 command + F 2.查找并替换 选中要替换的文字 再 command + options + F 3.竖状区域部分替换 鼠标点中替换字段的开头或者结尾,按options + s ...
- vue-cli 项目中使用 v-chart 及导出 chart 图片
安装: npm i v-charts echarts -S 组件中使用: 1 <template> 2 <div class="app-chart"> 3 ...
- 数据结构逆向分析-List
数据结构逆向分析-List 首先STL中的List就是一个链表,但是肯定C++用了很多封装,所以这里我们来一探究竟. 开始 首先先写一些简单的分析的源代码: #include<iostream& ...
- leetcode8 字符串转换为整数
最笨的办法实现 一步步判断 /** * @param {string} s * @return {number} */ var myAtoi = function(s) { s = s.trim() ...
- Jenkins操作手册 - 巨详细,一篇足矣!
一.继续集成相关概念 1.1.什么是持续集成? 随着软件开发复杂度的不断提高,团队开发成员间如何更好的协同工作以确保软件开发的质量已经成为开发过程中不可回避的问题.尤其是近年来敏捷开发在软件领域越来越 ...
- english note(6.3 to 6.8)
6.3 http://www.51voa.com/VOA_Special_English/pakistan-town-struggles-with-rise-in-hiv-infections-821 ...