|
UE的UI系统——无论是为了gameplay常用的UMG,还是更常用的Slate,都提供了很多预设的控件来支持开发者。然而,有的时候,你可以能仍然需要自定义自己的控件。有的时候是为了更上一级的封装,有的时候是为了填充一些原生不含有的功能控件,或者为了性能着想。本篇我们将介绍如果自定义一个Slate控件,无论Gameplay还是Editor,它都是可用的
一. 创建自定义Slate控件
新建一个CustomWidget.h和CustomWidget.cpp,创建一个Slate模板最好的方法是使用Rider,Rider的预设模板含有很多Unreal中常用的模板,包括接下来我们会聊到的UE模块
注意前缀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);
};
这里比较值得讨论的是SLATE_BEGIN_ARGS与SLATE_END_ARGS这两个宏,它用于实现我们的自定义Slate控件参数,以及实现参数默认值。
在Cpp文件中
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SCustomWidget::Construct(const FArguments& InArgs)
{
/*
ChildSlot
[
// Populate the widget
];
*/
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
也有两个宏,BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION和END_SLATE_FUNCTION_BUILD_OPTIMIZATION。顾名思义,这两个宏是协助优化使用的,我们在这个Slate控件中的函数均会放到这两个宏之间。在文末我给出的参考资料中,作者认为这两个宏主要围绕Construct函数使用,但UE的官方代码中使用的更随意一些。
二.传递参数
在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);
};
有趣的是SLATE_ARGUMENT这个宏,宏签名为
SLATE_ARGUMENT( ArgType, ArgName )
这个宏的作用是,向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);
};
此时我们使用的_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
这样,我们就可以在构造Slate自定义控件时使用这些自定义值。
HorizontalBox.AddSlot()
[
SNew(SCustomWidget).FloatValue(9.0f).IntValue(1)
];
这些就是我们在上一篇聊到的那些看起来很像链式调用一样的实质。实际上,在风格上,这更像是一种构造而非函数调用。
三. 绘制Slate自定义控件
绘制Slate自定义控件实际上是一个非常进阶的技巧,实现自定义的Slate绘制,通过这么一个函数:
virtual int32 OnPaint
(
const FPaintArgs & Args,
const FGeometry & AllottedGeometry,
const FSlateRect & MyCullingRect,
FSlateWindowElementList & OutDrawElements,
int32 LayerId,
const FWidgetStyle & InWidgetStyle,
bool bParentEnabled
) const
我的评价是受不了。像这种再看一眼就会爆炸的函数签名在UE几乎无处不在。
首先,我们聊一聊他的返回值,它返回一个int32类型的值,这个值代表着这个控件以及他的子控件能够达到的最大层ID。这里的层ID是用于渲染的。
再看几个常用的重要参数
- AllottedGeometry 确定Widget的位置和尺寸
- OutDrawElements 接受用来绘制这个Widget所需要的绘制元素
- InWidgetStyle 确定颜色啊 透明度啊之类的样式信息
一旦你使用了自己的OnPaint函数实现,那就意味着你不想用组件的组合来创建你的控件,而是需要从无到有的创建你的组件绘制方法。
为了达到此目标,你需要用到一个工具类:FSlateDrawElement,它是我们渲染Slate控件的地方,它提供了一些最基础的 绘制线 绘制Box等工具APi。我们需要填充一个叫做FSlateVertex结构,用来表明顶点,,还有一个SlateIndex的数组,用来表示三角形的绘制。
写到这里,我发现我举得这个例子没什么好绘制的,因此,我把我学习的时候使用的参考资料贴在文章最后,里面包含了一个完整的OnPaint函数使用的案例。
我个人的经验是,如果使用现有组件的组合可以绘制的组件,就不需要使用OnPaint函数覆写的方法来实现,而Gameplay中使用,应该使用UI Shader来实现,其性能更好也更简单,OnPaint函数仅仅在Editor组件中可用,价值并不是特别大。
<hr/>参考资料: |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|