【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 ...
随机推荐
- k8s 存活探针(健康检查)
重启策略 (RestartPolicy ) Always:当容器终止退出后,总是重启容器,默认策略. OnFailure:当容器异常退出(退出状态码非0)时,才重启容器. Never:当容器终止退出, ...
- 20210819 Emotional Flutter,Medium Counting,Huge Counting,字符消除2
考场 T1 一下想到了这题,将白块缩短 \(s\) 后维护类似的区间即可. T2 T3 俩计数,直接跳了. T4 的可行 \(t\) 集合相同相当与从 \(n\) 往前跳 kmp 数组,途径点相同,从 ...
- 最详尽的 JS 原型与原型链终极详解(1)(2)(3)===转载
转载===方便以后复习 原文网址:https://www.jianshu.com/p/dee9f8b14771 一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为 ...
- 了解mysql concat()函数
concat(arg1,arg2,....):将形参对应字段的值组合成一个字符串 假设:现在有一张学生表(test_user) 将这三个字段组合成一个字符串作为第四个字段 select test_us ...
- JS020. Array map()函数查到需要的元素时跳出遍历循环,不再执行到数组边界
Array.prototype.map() map( ) 方法创建一个 新数组 *,其结果是该数组中的每个元素是调用一次提供的 函数后的返回值 *.[ MDN / RUNOOB ] * map 添加 ...
- 面试官:MySQL的幻读是怎么被解决的?
大家好,我是小林. 我之前写过一篇数据库事务的文章「 事务.事务隔离级别和MVCC」,这篇我说过什么是幻读. 在这里插入图片描述 然后前几天有位读者跟我说,我这个幻读例子不是已经被「可重复读」隔离级别 ...
- CodeForces - 764B Timofey and cubes(模拟)
Young Timofey has a birthday today! He got kit of n cubes as a birthday present from his parents. Ev ...
- 浏览器缓存旧的js文件或css文件导致没出现预期效果
最好在加载的js或css文件后加上 ?v=1.0.0 版本号,更新js后就更改一下版本号即可
- html阴影 box-shadow
右下阴影 div { box-shadow: 10px 10px 5px #888888; }四周阴影 div { box-shadow: 0 0 5px #888888; } div {box-sh ...
- Linux系列(34) - yum源文件(1)
yum源文件各参数含义 在[/etc/yum.repos.d/]目录中,默认有4个yum源文件,其中[CentOS-Linux-BaseOS.repo]是基本yum源文件,如果我们能上网,那它是默认生 ...