The UI framework of Unreal Engine, not matter UMG which is commonly used in gameplay or Slate, provided many preset widgets to support developers. However, sometimes you might still need to customizing your own widgets. Sometimes is for higher-level encapsulation, sometimes for filling some useful functional widgets which is not inculuded original, and sometimes for performance. In this post I will introduce to customizing a slate widget,  it is useful regardless of Gameplay or Editor.

UE的UI系统——无论是为了gameplay常用的UMG,还是更常用的Slate,都提供了很多预设的控件来支持开发者。然而,有的时候,你可以能仍然需要自定义自己的控件。有的时候是为了更上一级的封装,有的时候是为了填充一些原生不含有的功能控件,或者为了性能着想。本篇我们将介绍如果自定义一个Slate控件,无论Gameplay还是Editor,它都是可用的

1. Customizing a slate Widget

Create two files : CustomWidget.h and CustomWidget.cpp, the best way to create a slate template is using rider, rider privides a lot of commonly used Unreal templates, including the UE Modules we will talk about soon.

新建一个CustomWidget.h和CustomWidget.cpp,创建一个Slate模板最好的方法是使用Rider,Rider的预设模板含有很多Unreal中常用的模板,包括接下来我们会聊到的UE模块。

Note the prefix “S”, Slate widgets are prefixed with “S”. We choose SCompoundWidget as parent class, it has one slot. After creation, let’s take a look at the class signature.

注意前缀S,Slate控件以S为前缀。我们选择SCompoundWidget作为父类,它拥有一个插槽。创建后,我们首先来看类签名:

class SLATELEARNING_API SCustomWidget : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SCustomWidget)
	{
	}

	SLATE_END_ARGS()

	/** Constructs this widget with InArgs */
	void Construct(const FArguments& InArgs);
};

What worth discussing in this class signature are the two macro : SLATE_BEGIN_ARGS and SLATE_END_ARGS, they are used to implement the parameters of customizing slate widget and default parameters value.

这里比较值得讨论的是SLATE_BEGIN_ARGS与SLATE_END_ARGS这两个宏,它用于实现我们的自定义Slate控件参数,以及实现参数默认值。

In cpp file:

在Cpp文件中:

BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SCustomWidget::Construct(const FArguments& InArgs)
{
	/*
	ChildSlot
	[
		// Populate the widget
	];
	*/
}

END_SLATE_FUNCTION_BUILD_OPTIMIZATION

There are also two macros, BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION and END_SLATE_FUNCTION_BUILD_OPTIMIZATION. As the name suggests, these two macros are used for assist in optimization, and all our functions implemented in slate widget will be placed between these to macros.In the reference I gave at the end of this post, the author believes that these are used mostly for Construct(), however, they are used more casually in code of UE.

也有两个宏,BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION和END_SLATE_FUNCTION_BUILD_OPTIMIZATION。顾名思义,这两个宏是协助优化使用的,我们在这个Slate控件中的函数均会放到这两个宏之间。在文末我给出的参考资料中,作者认为这两个宏主要围绕Construct函数使用,但UE的官方代码中使用的更随意一些。

Passing parameters in slate widgets takes a bit tricky, let’s declare two variables for test.

在Slate控件中传递参数需要一些技巧,我们声明两个测试变量

class SLATELEARNING_API SCustomWidget : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SCustomWidget)
	{
	}

	SLATE_ARGUMENT(float, FloatValue)
	SLATE_ARGUMENT(int32, IntValue)

	SLATE_END_ARGS()
	
	float FloatValue;
	int32 IntValue;

	/** Constructs this widget with InArgs */
	void Construct(const FArguments& InArgs);
};

What’s interesting is the macro SLATE_ARGUMENT, which signature is:

有趣的是SLATE_ARGUMENT这个宏,宏签名为

SLATE_ARGUMENT( ArgType, ArgName )

The role of this marco is: Add variable that you just declared to FArguments, and named with _FloatValue and _IntValue in FArguments.

We can use some construct function-styled methods to give default values to the parameters in SLATE_BEGIN_ARGS.

这个宏的作用是,向FArguments中添加你声明的变量,在FArguments中,变量的名称为_FloatValue与_IntValue。

我们可以在SLATE_BEGIN_ARGS这个宏中用一些构造函数风格的方法来为这些变量赋于初值

class SLATELEARNING_API SCustomWidget : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SCustomWidget) : _FloatValue(4.0f), _IntValue(2)
	{
	}

	SLATE_ARGUMENT(float, FloatValue)
	SLATE_ARGUMENT(int32, IntValue)

	SLATE_END_ARGS()
	
	float FloatValue;
	int32 IntValue;

	/** Constructs this widget with InArgs */
	void Construct(const FArguments& InArgs);
};

Now, the _FloatValue and _IntValue are actually variables of struct FArguments, and you can get these variables in Construct() by this way:

此时我们使用的_FloatValue与_IntValue实际上都是FArguments中的值,可以Construct函数中获取这些值:

BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SCustomWidget::Construct(const FArguments& InArgs)
{
	float FValue = InArgs._FloatValue;
	int32 IValue = InArgs._IntValue;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION

By this, we can assign these variables when contruct slate widgets.

这样,我们就可以在构造Slate自定义控件时为这些自定义值赋值。

HorizontalBox.AddSlot()
[
	SNew(SCustomWidget).FloatValue(9.0f).IntValue(1)
];

These are the essences of the look-like chain calls we seen in the previous post. Actually, stylistically, this is more like a construct than chain function call.

这些就是我们在上一篇聊到的那些看起来很像链式调用一样的实质。实际上,在风格上,这更像是一种构造而非函数调用。

By JiahaoLi

Hypergryph - Game Programmer 2023 - Now Shandong University - Bachelor 2019-2023

Leave a Reply

Your email address will not be published. Required fields are marked *