【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 ...
随机推荐
- 从synchronize到CSA和
目录 导论 悲观锁和乐观锁 公平锁和非公平锁 可重入锁和不可重入锁 Synchronized 关键字 实现原理 Java 对象头 Monitor JVM 对 synchronized 的处理 JVM ...
- uni-app中websocket的使用 断开重连、心跳机制
前言 最近关于H5和APP的开发中使用到了webSocket,由于web/app有时候会出现网络不稳定或者服务端主动断开,这时候导致消息推送不了的情况,需要客户端进行重连.查阅资料后发现了一个心跳机制 ...
- java版gRPC实战之一:用proto生成代码
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- ajax获取图片
<img id="contents2_img" alt="" src="images/hope.png" style="wi ...
- AspectJ基于xml和基于注解
一.基于xml 执行的切入点中具体方法有返回值,则方法结束会立即执行后置通知,然后再执行环绕通知的放行之后的代码: 2.连接点即所有可能的方法,切入点是正真被切的方法,连接点方法名: 其中,只有环绕通 ...
- Spring基于XML方式加载Bean定义信息(又名:Spring IOC源码时序图)-图解
- Git--生成公钥和私钥并添加gitlab访问权限
Git配置 打开git bash 执行以下命令 git config --global user.name 用户名 git config --global user.email 邮箱 ssh-keyg ...
- Linux的基础命令(一)
目录: 一.Linux系统基础 1.shell 2. Linux命令的分类 二.Linux命令行 1.Linux命令行提示符 2.Linux通用命令行使用格式 3.Lin ...
- MySQL-技术专题-MySQL主从架构以及[半同步机制]模式大全
MySQL的主从复制 一般在大规模的项目上,都是使用MySQL的复制功能来创建MySQL的主从集群的. 主要是可以通过为数据库服务器配置一个或多个备库的方式来进行数据同步. 复制的功能不仅有利于构建高 ...
- vmware workstation16许可证密钥
ZF3R0-FHED2-M80TY-8QYGC-NPKYFYF390-0HF8P-M81RQ-2DXQE-M2UT6ZF71R-DMX85-08DQY-8YMNC-PPHV8FA1M0-89YE3-0 ...