【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 ...
随机推荐
- nRF52832蓝牙iBeacon广播
开发环境 SDK版本:nRF5_SDK_15.0.0 芯片:nRF52832-QFAA 蓝牙iBeacon实现 iBeacon的核心就是广播,不需要进行连接,通过在广播包中插入信息然后广播出去. 广播 ...
- 性能测试必备命令(1)- free
性能测试必备的 Linux 命令系列,可以看下面链接的文章哦 https://www.cnblogs.com/poloyy/category/1819490.html 介绍 显示系统的内存使用情况 语 ...
- 博主有偿带徒 《编程语言设计和实现》《MUD游戏开发》《软件破解和加密》《游戏辅助外挂》《JAVA开发》
<考研专题>操作系统原理 理论解答:8K 实战 1.5W CPU设计 理论解答:1W 实战 2.5W <编程语言设计和实现>初窥门径<5K>:编译原理.编译设计小试 ...
- 本地jvisualvm通过jstatd远程监控GC
1.查找jdk路径 [root@xxx ~]# which java /data/soft/jdk1.8.0_221/bin/java 2.进入jdk的bin目录下添加指定安全策略文件,注意jdk路径 ...
- MySQL高级语句(一)
一.MySQL高级进阶SQL 语句 1.SELECT 2.DISTINCT 3.WHERE 4.AND.OR 5.IN 6.BETWEEN 7.通配符.LIKE 8.ORDER BY 9.| | 连 ...
- C# 动态构建表达式树(一)—— 构建 Where 的 Lambda 表达式
C# 动态构建表达式树(一)-- 构建 Where 的 Lambda 表达式 前言 记得之前同事在做筛选功能的时候提出过一个问题:如果用户传入的条件数量不确定,条件的内容也不确定(大于.小于和等于), ...
- java web利用jsp完成简单的学生管理系统
index.jsp <%@ page language="java" import="java.sql.*" pageEncoding="utf ...
- open failed: EACCES (Permission denied)
出现背景:调用系统相册进行图片展示,但是没有成功,是空白的,且检查权限无问题 解决方法
- 在PHP中操作临时文件
关于文件相关的操作,想必大家已经非常了解了,在将来我们刷到手册中相关的文件操作函数时也会进行详细的讲解.今天,我们先来了解一下在 PHP 中关于临时文件相关的一些内容. 获取 PHP 的默认临时创建文 ...
- PHP的引用计数是什么意思?
什么是引用计数 在PHP的数据结构中,引用计数就是指每一个变量,除了保存了它们的类型和值之外,还额外保存了两个内容,一个是当前这个变量是否被引用,另一个是引用的次数.为什么要多保存这样两个内容呢?当然 ...