UE编辑器-Slate快速入门【开篇】
本节将对Slate插件工程进行一个详解,还是本着不放过每个细节、不模棱两可的态度,对插件工程的方方面面进行洞悉,期待和大家一起交流。
前言
Slate是UE4自带的一套升级版IMGUI框架,既能用于Runtime中的UI,也能用于Edit状态下的操作界面创建,其强大的功能能满足你各种复杂的需求。
UE4本身的编辑器界面是由Slate框架进行创建的,包括我们用于UMG的widget也是基于Slate进行封装,因此我们也同样可以借助这套工具来帮助策划定制一套集成于UE4的编辑插件。对这样一套神奇的框架,我们已经迫不及待对其进行学习和运用,那就开始吧!
附1:官方Slate介绍:https://docs.unrealengine.com/4.26/zh-CN/ProgrammingAndScripting/Slate/Architecture/
一、基础工程介绍/回顾
UE内为我们准备好了包含Slate框架的基础插件程序,我们可以直接创建来看看究竟。
1.1 工程创建
在【开篇】我们已经介绍过,这里简单过一遍。
确保游戏项目为C++项目,如果为蓝图项目,需要创建C++工程
点击面板中Settings
下的Plugins
进行创建(菜单栏的Edit
下也有同样目录)
在插件界面右下绿色按钮选择创建的插件模板,使用Slate需要创建带Editor字样的的插件,这里我们选择Editor Standalone Window类型。填好名字(SlateTest),作者,就可以用打开项目代码了。
1.2 工程概览
打开创建好的程序,可见其目录结构如下:
编辑器插件目录结构介绍:
- Resources(文件夹)
- .png :该图片作为Slate插件在引擎中图标按钮
- Souce(文件夹)
- SlateTest(模块名称)
- Private
- SlateTest.cpp:插件模块主程序
- SlateTestCommands.cpp:用于按键的映射设置(可为按钮绑定
键盘快捷键
) - StateTestStyle.cpp:Slate控件自定义风格样式设置(Brush)
- Public
- 同上
- .Build.cs文件:每个模块对应一个“ModuleName.Build.cs”,主要是用于配置第三方库,让UE程序能够正确调用库(因为UE4使用自己的UBT来编译,利用C#来处理程序依赖)
附2:理解UE4静态库和动态库的使用:https://www.cnblogs.com/sevenyuan/p/7161516.html - uplugin文件:插件配置(描述)文件,用于设置插件包含的模块和运行方式
附3:插件官方介绍:https://docs.unrealengine.com/4.26/zh-CN/ProductionPipelines/Plugins/
Tips:不知道伙伴们有没有发现,程序目录中有两个命名和你创建插件同名的目录,例如上面,一个SlateTest
目录下包含了另一个SlateTest
目录,其实第一个SlateTest是插件的目录,第二个是SlateTest是模块的目录,因为一个插件至少含有一个模块的原则,所以UE其实是为我们创建了一个同名
的模块。
二、主程序介绍
Slate插件主程序其结构如下
SlateTest模块的代码
SlateTest继承于IModuleInterface基类,因此它是一个模块类,可以见到插件模块里面主要有五个函数,他们实现了slate模块的基本结构
+StartupMoudule()
:模块开始执行的方法
+ShutdownModule()
:模块注销执行的方法
+PluginButtonClicked()
:插件入口的按钮触发事件(启动模块函数中绑定的按键事件)
+RegisterMenus()
:将模块注册到UE4编辑器菜单栏和工具栏中(让插件可在主菜单显示)
+OnSpawnPluginTab():
Slate插件的可视化程序部分,也是写Slate插件最主要编写的部分
2.1 StartupModule函数
void FSlateTestModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
/* 初始化SlateStyle样式,加载图标等 */
FSlateTestStyle::Initialize();
FSlateTestStyle::ReloadTextures();
/* 注册Commmand管理器,映射绑定输入指令和操作功能 */
FSlateTestCommands::Register();
PluginCommands = MakeShareable(new FUICommandList);
PluginCommands->MapAction(
FSlateTestCommands::Get().OpenPluginWindow,
FExecuteAction::CreateRaw(this, &FSlateTestModule::PluginButtonClicked),
FCanExecuteAction());
/* 为模块注册在菜单栏和工具栏上的入口 */
UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FSlateTestModule::RegisterMenus));
/* 为该模块注册一个Tab显示窗口 */
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(SlateTestTabName, FOnSpawnTab::CreateRaw(this, &FSlateTestModule::OnSpawnPluginTab))
.SetDisplayName(LOCTEXT("FSlateTestTabTitle", "SlateTest"))
.SetMenuType(ETabSpawnerMenuType::Hidden);
}
void FSlateTestModule::PluginButtonClicked()
{
/* 触发注册在TabManager中的Tab窗口 */
FGlobalTabmanager::Get()->TryInvokeTab(SlateTestTabName);
}
2.2 OnSpawnPluginTab()函数
该函数用于生成显示插件模块的Tab主界面,采用链式编程。
/* 该函数为Slate的Tab主界面的生成函数 */
TSharedRef<SDockTab> FSlateDemoOneModule::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("FSlateDemoOneModule::OnSpawnPluginTab")),
FText::FromString(TEXT("SlateDemoOne.cpp"))
);
/* 链式编程构建界面 */
return SNew(SDockTab)
.TabRole(ETabRole::NomadTab)
[
// Put your tab content here!
SNew(SBox)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(WidgetText)
]
];
}
2.3 RegisterMenus()函数
该函数用于创建该模块在UE编辑器上的菜单入口(包括上菜单栏和中间工具栏入口)
Tips:在UE5和UE4的工具栏的可拓展插槽位置略有不同,UE5【LevelEditor.LevelEditorToolBar.PlayToolBar】, UE4 【LevelEditor.LevelEditorToolBar】
/* 在编辑器菜单栏和工具栏拓展按钮 */
void FSlateDemoOneModule::RegisterMenus()
{
// Owner will be used for cleanup in call to UToolMenus::UnregisterOwner
FToolMenuOwnerScoped OwnerScoped(this);
{
/* 主菜单栏中插入触发按钮 */
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window");
{
//在WindowLayout位置片区添加触发按钮
FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout");
Section.AddMenuEntryWithCommandList(FSlateDemoOneCommands::Get().OpenPluginWindow, PluginCommands);
}
}
{
/* 主工具栏中插入触发按钮 */
UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar");
{
// 在工具栏的Settings片区中添加触发按钮
FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings");
{
FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FSlateDemoOneCommands::Get().OpenPluginWindow));
Entry.SetCommandList(PluginCommands);
}
}
}
}
2.4 模块的注册
这一节看后续文章详细讲解
2.5 总结
通过上述代码的分析,我们可以知道一个slate plugin创建和打开的流程
三、Commands.cpp文件介绍
Commands.cpp涉及到编辑器的命令管理,整个插件主要涉及到三个类FUICommandInfo、TCommands、FUICommandList
【1】FUICommandInfo是创建和管理编辑器命令的类,编辑器命令是指在编辑器上执行的操作:打开一个窗口、保存一个文件等。我们可以将每个操作抽象为一个该对象,实现界面与具体操作的解耦,也可以实现多个操作对应一个命令(例如Ui按钮和快捷键对应同一个命令操作) 【2】TCommands可以理解为FUICommnadInfo
的容器,方便插件统一定义和初始化相关FUICommnadInfo
【您原来就是一个config文件】,这也是创建commands.cpp的原因。
【3】FUICommandList可以理解为命令的管理器,用于创建、注册和管理命令。通过FUICommandList,我们可以将FUICommandInfo与UI控件触发事件绑定在一起。
4.1 流程图介绍
插件中命令的具体流程如下(从上往下看)
具体代码流程总结就是三步:定义命令(初始化)、绑定命令与执行事件(执行)、绑定命令与具体控件UI(输入)
4.2 代码流程
结合上述概览的command流程,我们来看看具体代码吧!
步骤一、定义UI命令(Commands文件中)
Commands文件主要用于配置UICommandInfo,从而设置UICommandInfo相应的信息、样式、快捷键功能等。
// Commands.h文件
class FSlateTestCommands : public TCommands<FSlateTestCommands>
{
/* ..................................... */
public:
TSharedPtr< FUICommandInfo > OpenPluginWindow;
};
// Commands.cpp 文件
void FSlateTestCommands::RegisterCommands()
{
UI_COMMAND(OpenPluginWindow, "SlateTest", "Bring up SlateTest window", EUserInterfaceActionType::ToggleButton, FInputChord());
}
小问环节:既然TCommands可以理解为一个配置文件,那么是不是我们可以不在TCommands文件中定义呢?
答:当然,但是TCommands提供了方便创建FUICommandInfo的宏,如UI_COMMAND()
,除此之外,还提供了其对应的生命周期的统一管理。如果自己创建,那么需要我们自己正确管理其创建和生命周期,因此不建议脱离TCommands来创建FUICommandInfo。
步骤二、绑定相关执行事件
绑定的时候用到FUICommandList的MapAction函数来绑定命令和具体执行函数,FUICommandList中重载了多种MapAction的实现,主要就是几个常见的参数
TSharedPtr< const FUICommandInfo > InUICommandInfo
:传入UICommandInfo对象FExecuteAction
:传入一个FExecuteAction对象,绑定了具体执行操作。CanExecuteAction
:传入一个FCanExecuteAction对象,用于检查当前操作是否能够执行,可以理解为一个条件检测函数,默认返回True
//.h
TSharedPtr<class FUICommandList> PluginCommands;
//.cpp
PluginCommands = MakeShareable(new FUICommandList); //创建管理类
PluginCommands->MapAction( FTestMenuCommands::Get().PluginAction, TestFExecuteAction::CreateRaw(this, &FSlateTestModule::PluginButtonClicked), FCanExecuteAction()); // 创建绑定
步骤三、传入命令用于扩展编辑器
最后的步骤便是将命令与编辑器界面的某个UI进行结合,这里需要我们分别传入FUICommandInfo和FUICommandList。还需要传入FUICommandList,原来就是为了获取FUICommandInfo对应的UIAction。
UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar");
{
FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings");
{
// 传入命令FUICommandInfo
FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FSlateTestCommands::Get().OpenPluginWindow));
// 传入命令管理器FUICommandList(用于获取命令对应的Action)
Entry.SetCommandList(PluginCommands);
}
}
4.3 总结
UE编辑器利用命令FUICommandInfo解耦了UIAction和UI控件,使得整个界面的操作更加灵活和自定义。这种输入控制解耦的理念后面在UE5中被发扬光大到GamePlay中,也即UE5中的增强输入,增强输入系统更加复杂,且不局限于界面与命令的解耦,还包括与硬件之前的解耦输入。
四、Style.cpp文件介绍
4.1 认识Style
- Style文件主要负责Slate的样式,Style样式在UE中常被用于UI的外貌打造,样式一般涉及按钮颜色、图片、字体、边框等具体的控件参数。
在插件模块中Style文件主要提供一些静态方法,来帮助插件模块创建、注册和注销FSlateStyleSet类型的单例。
- FSlateStyleSet是用于定义和管理SlateUI样式的类,我们可以在其中定义Slate插件方方面面的样式(按钮、文本框、滑块、复选框等),每种样式属性都可以以名称+属性值存储在FSlateStyleSet中,并在需要的时候调用。具体可以参考下面插件中的Style程序
TSharedRef< FSlateStyleSet > FSlateTestToolBarStyle::Create()
{
/* 创建SlateStyleSet */
TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("SlateTestStyle"));
/* 设置样式集资产读取路径 */
Style->SetContentRoot(IPluginManager::Get().FindPlugin("SlateTest")->GetBaseDir() / TEXT("Resources"));
/* 存储名为 SlateTest.PluginAction 的BrushStyle到StyleSet中 */
Style->Set("SlateTest.PluginAction", new IMAGE_BRUSH_SVG(TEXT("PlaceholderButtonIcon"), Icon20x20));
return Style;
}
Slate插件中的Style程序中只定义了一个用于设置SlateTestToolBar.PluginAction命令对应的button的样式,这里用的一个划线的图片资产,编辑器中展示效果如图所示,这便是我们插件的工具栏入口按钮。
4.2 学以致用
问:我们可以脱离Style文件直接创建Style样式吗?
答:我们当然可以脱离插件的Style文件来使用FSlateStyleSet,但必须正确管理其生命周期,因此一般情况下还是不建议自己管理。
/* --------------注册并使用---------------*/
// 创建一个新的样式集
FSlateStyleSet* StyleSet = new FSlateStyleSet("MyStyleSet");
// 定义一个样式属性并存储到StyleSet
FSlateBrush ButtonBrush = FSlateBrush();
ButtonBrush.TintColor = FSlateColor(FLinearColor::Red);
StyleSet->Set("MyButtonStyle", ButtonBrush);
// 注册样式集
FSlateStyleRegistry::RegisterSlateStyle(*StyleSet);
// 在创建按钮时,使用这个样式
SNew(SButton)
.ButtonStyle(StyleSet, "MyButtonStyle")
.Text(FText::FromString("My Button"));
/* --------------注销---------------*/
// 使用完后,找合适的时机注销StyleSet
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet);
StyleSet.Reset();
五、配置文件介绍
5.1 插件描述(.uplugin)文件介绍
上面目录中介绍到uplugin
是插件的配置(描述)文件,主要用于配置插件的各种基本信息(版本号、作者等等),当然还用于来配置插件使用到的模块,该文件使用json
的文件格式。该文件主要在游戏引擎启动期间去读取。
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "SlateTest",
"Description": "",
"Category": "Other", //作者
"CreatedBy": "CloudBoy",
"CreatedByURL": "",
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": false,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false,
"Modules": [
{
"Name": "SlateTest", //插件模块命名
"Type": "Editor", //模块的类型,决定能加载模块的应用程序类型 EHostType::Type
"LoadingPhase": "Default" //模块加载策略 ELoadingPhase::Type
}
]
}
这里我们主要来看下哪些参数会实质影响到项目的一些功能加载:
1)普通参数
- CanContainContent
是否包含资产目录,当为true的时候,UE会为我们默认创建一个Content目录
- IsBetaVersion
顾名思义该插件是否为测试版本,仅仅是一个提示作用,会在插件栏显示一个beta提示,并不影响我们对项目的打包。
- IsExperimentalVersion
是否为实验版本,打开这个小开关有什么后果呢?当然也只是一个提示,UE为了规范,拥有这个提示的插件是不建议被进行发布,望周知。
- Installed
这个参数官方的解释是Signifies that the plugin was installed on top of the engine
,表示的是插件是否基于引擎安装,true则作为引擎的一部分安装,false则表示需要作为独立插件安装,而不是表示是否安装插件,当然这也只是一个标记。
2)Modules配置
Modules配置比较关键,有两个重要枚举参数设置,一个是模块类型Type
,一个加载类型LodaingPhase
,这两个配置会实实在在影响到插件的运行状态和加载策略,因此需要谨慎配置。
- Type类型
先认识几个关键字:
【程序/Program】:UE可以创建独立的应用程序,不依赖编辑器启动,像UHT和PakViewer就是一个独立应用程序,在源码引擎Programs目录可以看到很多应用程序
【命令模式/Commandlet】:UE可以创建命令程序,和独立程序一样,不依赖编辑器即可独立启动,通常用于批处理UE中的资源,并且方便部署到流水线进行自动化批处理。常见的例子有:重定向器、打包Cook等,这些功能都是拉起一个命令程序来单独执行的。
namespace EHostType
{
enum Type
{
Runtime, //表示模块可以加载到所有目标中,除了【程序】
RuntimeNoCommandlet, //表示模块可以加载到所有目标中,除了【程序】和运行【命令模式】的编辑器。
RuntimeAndProgram, //表示模块可以加载到所有目标中,包括支持的【程序】
CookedOnly, //表示模块只能在已经打包的游戏中加载
UncookedOnly, //表示模块只能在未打包的游戏中加载
Developer, //UE5中因存在歧义被废弃,不建议使用:表示模块只能在编辑器和【程序】中加载,可以在任何编辑器模式下加载(例如 -game、-server)
Editor, //表示模块只能在编辑器启动时加载
EditorNoCommandlet, //表示模块只能在编辑器启动时加载,但不能在【命令模式】下加载。
EditorAndProgram, //表示模块只能在编辑器和【程序】中加载
Program, //只有运行独立【程序】时的插件
ServerOnly, //表示模块可以加载到除了专用客户端之外的所有目标中
ClientOnly, //表示模块可以加载到除了专用服务器之外的所有目标中
ClientOnlyNoCommandlet, //在客户端和编辑器下加载,但不能在 【命令模式】下
};
}
- LodaingPhase类型
namespace ELoadingPhase
{
enum Type
{
PostConfigInit, //引擎完全加载前,配置文件加载后。适用于较底层的模块。
PreEarlyLoadingScreen, //在UObject加载前,用于补丁系统
PreLoadingScreen, //在引擎模块完全加载和加载页面之前
PreDefault, //默认模块加载之前阶段
Default, //默认加载阶段,在引擎初始化时,游戏模块加载之后
PostDefault, //默认加载阶段之后加载
PostEngineInit, //引擎初始化后
None, //不自动加载模块
};
}
- 可参考文章 附4:.uplugin参考文章:https://zhuanlan.zhihu.com/p/114649056
5.2 Build.cs文件介绍
当然,为代码添加注释可以帮助读者更好地理解Slate插件的创建过程。以下是完整代码及其注释:
// 在 #include 中添加您需要使用的模块
#include "Slate/Public/SlateBasics.h"
#include "Slate/Public/Widgets/Text/STextBlock.h"
#include "Slate/Public/Widgets/Input/SButton.h"
#include "Slate/Public/Widgets/Layout/SBox.h"
// 创建一个新的UE4插件项目
class FSlatePlugin : public IModuleInterface
{
public:
/** IModuleInterface implementation */
void StartupModule();
void ShutdownModule();
};
void FSlatePlugin::StartupModule()
{
// 在此处添加您的模块启动代码
}
void FSlatePlugin::ShutdownModule()
{
// 在此处添加您的模块关闭代码
}
// 实现UMG插件
IMPLEMENT_MODULE(FSlatePlugin, SlatePlugin)
// 创建一个继承自 SCompoundWidget 的 Slate Widget
class SLATEPLUGIN_API SSlatePluginWidget : public SCompoundWidget
{
public:
// 在 begin_args 和 end_args 中定义您的构造函数所需的任何成员变量
SLATE_BEGIN_ARGS(SSlatePluginWidget) {}
SLATE_END_ARGS()
// 构造函数
void Construct(const FArguments& InArgs)
{
// 定义子窗口
ChildSlot
[
SNew(SBox)
[
SNew(STextBlock)
.Text(FText::FromString("Hello, World!"))
]
];
}
};
// 添加一个按钮到 Slate Widget
void Construct(const FArguments& InArgs)
{
// 定义子窗口
ChildSlot
[
SNew(SBox)
[
SNew(SButton)
// 设置按钮文本
.Text(FText::FromString("Click me!"))
// 设置按钮点击事件
.OnClicked(this, &SSlatePluginWidget::HandleButtonClick)
]
];
}
// 处理按钮点击事件
FReply HandleButtonClick()
{
// 在此处添加您需要实现的代码
return FReply::Handled();
}
// 将插件添加到项目中
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Slate", "SlateCore" });
六、创建Slate基础控件
6.1 认识链式编程
链式编程的形式如下面所示,通过SNew一个控件类型,然后通过.xxx来配置参数或者是绑定相关事件,然后再通过中括号包含其内容。
小提示:最外层的括号结尾记得加分号!
其实这个链式编程流程和UMG的层级插槽是一致的,可以参考下面这张图
6.2 创建Button实例
下面我们快速自己创建一个按钮来实践下这个流程,先把原有内容Sbox删除
在链式编程中创建控件实例其实我们有SNew和SAssignNew两种方法,都可以通过其获得控件实例指针,但是SNew不强制赋值,SAssignNew必须提供类型指针
好了,我们开始创建Button,实现点击Button打印字符串,代码如下,关键代码在红色框选部分
在上图中,我们既配置了按钮上显示的字符,也配置了按钮点击时绑定的事件,我们编译后在UE4工具栏查看我们的插件,可以看到插件页面就有了一个大大的按钮。可以进行点击,然后会打印日志,说明我们的插件生效了
对于其他类型的基础控件
,我们也可以举一反三,其实就和使用UMG时的方法是一样的,毕竟UMG就是基于Slate来进行创建的。
6.3 如何创建自定义控件
1 控件基类
UE4 Slate框架中最基础的类是SWidget
,基于SWidget
的子类主要由三种,分别是SCompoudWidget
、SLeafWidget
、SPanel
\
他们三个最主要的区别在于能附加子控件的数目。
SCompoudWidget
其子类只能拥有一个子控件,常见的子类有SButton
,SBorder
等,他们的特点都是只能附加一个子控件
SLeafWidget
其子类已经是叶子节点,不能再拥有子控件,常见的子类有SImage
、STextBlock
,这类控件都是没有子控件插槽
的SPanel
其子类的特点是可以无限添加子控件,没有数量限制,常见的子类的有SHorizontal(水平框)
、SPanel
等等
详细介绍可见链接
:https://zhuanlan.zhihu.com/p/262232941
基于上面三种基类,UE4创建了许多基础样式为我们所用,我们也可以同样可以基于上述三种基类
来创建我们自己的样式。
2 创建自定义控件
这里我们以创建一个继承于SCompoudWidget
的子类为例
// NewWdiget.h
#pragma once
class NewWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(NewWidget) {}
SLATE_END_ARGS()
void Construct(const FArguments& InArgs);
};
// NewWdiget.cpp
#include "NewWidget.h"
void NewWidget::Construct(const FArguments& InArgs)
{
ChildSlot[
SNew(SButton)
];
}
上面的代码就完成了一个非常简单的自定义控件的创建,我们只为其插槽加了一个按钮。(这其实是一个非常好的模板,按照这个模板来创建你的其他自定义控件)
不同于其他类,基于SCompoundWidget
的子类,有一段SLATE_BEGIN_ARGS(NewWidget) {} SLATE_END_ARGS()
的宏,这里的宏是来让我们自定义控件中的参数、事件、插槽等等;而且还有一个属于Swidget专属的构造函数void Construct(const FArguments& InArgs);
1 )声明自定义参数
在上述宏中我们可以用SLATE_ATTRIBUTE(属性)、SLATE_EVENT(事件)、SLATE_ARGUMENT(参数)、SLATE_NAMED_SLOT(插槽) 和 SLATE_DEFAULT_SLOT来声明我们的需要的参数。这里详细可以参考链接:https://zhuanlan.zhihu.com/p/115267691中的内容。
这里我们以常用的SLATE_ARGUMENT(参数)
为例
#pragma once
class NewWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS( NewWidget )
: _IsFocusable( false )
{}
SLATE_ARGUMENT( bool, IsFocusable )
SLATE_END_ARGS()
void Construct(const FArguments& InArgs);
private:
bool IsFocusable;
};
例如上面我们定义了IsFocusable
的参数,并对他进行了初始化_IsFocusable( false )
,通过宏定义的参数其实会变为_
+参数名
的形式,并存放在由宏定义的结构体 Arguments
中。
Arguments
这个结构体主要是为了在控件构造创建时方便
将自定义参数的值传递
给当前我们定义的类中的同名变量
,因此我们还需要在类中创建一个同名成员变量bool isRight;
来存放值,并在构造函数中为其赋值。
ChildSlot
是SCompoundWidget
子类所拥有的唯一一个插槽,我们可以在其中放置我们所需要的控件类型。
#include "NewWidget.h"
void NewWidget::Construct(const FArguments& InArgs)
{
IsFocusable = InArgs._IsFocusable ;
//插槽中创建一个按钮
ChildSlot[
SNew(SButton)
];
}
2 )使用自定义控件
使用自定义控件和使用基础控件的方法其实是一样的,就是使用SNew
和SAssignNew
来进行创建
我们在开始创建的CloudBoy
插件中的OnSpawnPluginTab
函数中创建上述我们自定义的控件,并初始化其自定义参数
#include "NewWidget.h" //首先引入头文件
TSharedRef<SDockTab> FCloudBoyModule::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("FCloudBoyModule::OnSpawnPluginTab")),
FText::FromString(TEXT("CloudBoy.cpp"))
);
TSharedPtr<NewWidget> testWdiget; //定义指针
return SNew(SDockTab)
.TabRole(ETabRole::NomadTab)
[
// Put your tab content here!
SAssignNew(testWdiget, NewWidget) //创建自定义控件
.IsFocusable(true) //初始化自定义参数
];
}
因为自定义控件中放的是一个按钮,因此结果插件显示的效果也是一个大的白色按钮