UE4 将 Slate控件封装成 UWidget

UE4 将Slate控件封装成UWidget



前言

UE4 本身提供了许多封装好的 UMG 控件,当然我们可以将自己的 Slate 控件封装到 UMG 中,方便我们在可视化编辑器中搭建我们的界面。

在本文中我会以上一篇文章(UE4 Slate UI框架入门)中的 SMyComponentWidget (一个简单的,只包含一个默认 Slot 的自定义控件)为例,将其封装到 UMG 中。


一、基类选择

首先,我们要搞清楚我们需要继承什么类型去实现我们的 UMG。

一般我们会从 UWidget、UContentWidget 或 UPanelWidget 去派生出我们的 UMG 控件

  1. UWidget 是最简单的类型,如果不需要通过编辑器去添加子控件,可以直接继承该类型,比如 UImage 就是直接从 UWidget 派生的;
  2. UContentWidget 是拥有固定数量插槽的控件类型,比如 USizeBox、UButton、UBorder 等都是继承的该类型;
  3. UPanelWidget 是拥有不固定数量插槽的控件类型,比如 UCanvasPanel、UOverlay、UHorizontalBox 等是继承的该类型;

基类的选择通常会与 Slate 控件的功能相关,但并不是绝对的,主要还是取决于你希望在编辑器中如何去使用它。

因为我的 SMyComponentWidget 控件只有一个插槽,并希望能在编辑器中向控件中添加一个其他子控件,所以在这里我选择继承 UContentWidget 来实现。

二、创建自定义UMG控件

1. 重要方法

无论选择哪种基类,我们都需要重载这么几个函数:

  1. RebuildWidget() 在这里我们会生成我们的 Slate 控件。
TSharedRef<SWidget> UMyCompoundWidget::RebuildWidget()
{
	TSharedPtr<SWidget> SlateContent;

	// 如果在编辑器中添加了子控件,我们可以通过 GetChildrenCount 获取到对应的 Slot
	if (GetChildrenCount() > 0)
	{
		UWidget* UMGContent = GetContentSlot()->Content;
		SlateContent= UMGContent ? UMGContent->TakeWidget() : SNullWidget::NullWidget;
	}
	else
	{
		SlateContent = SNew(STextBlock)
			.Text(LOCTEXT("NoChildWidget", "没有子控件"));
	}
	
	// 创建自定义 Slate 控件
	MyWidget = SNew(SMyCompoundWidget, 1.0f)
		[
			SlateContent.ToSharedRef()
		];

	return MyWidget.ToSharedRef();
}
  1. SynchronizeProperties() 编译时,会调用该函数,主要用于将属性同步到 Slate 控件的显示上
void UMyCompoundWidget::SynchronizeProperties()
{
	Super::SynchronizeProperties();

	// 如果希望将 UWidget 中的数据同步到 Slate 显示中,可以在这里添加代码
	// MyWidget->SetSomething();
}
  1. ReleaseSlateResources(bool bReleaseChildren) 如果有指向 Slate 控件的智能指针,需要在这里释放,否则可能会导致控件被释放时出现意外的错误
void UMyCompoundWidget::ReleaseSlateResources(bool bReleaseChildren)
{
	Super::ReleaseSlateResources(bReleaseChildren);

	MyWidget.Reset();
}

2. 其他可选方法

到目前为止,我们的 Slate 控件已经能够在 UMG 编辑器中正常使用了,当然,还有一些可选的函数供我们实现更多内容,我举几个例子,就不一一详细阐述了:

  1. GetPaletteCategory() 如果你希望能在 UMG 控件列表中指定分类中找到你的控件,可以重写这个函数,但需要注意的是,这是一个仅在编辑器下使用的函数,意味着你需要添加 WITH_EDITOR 宏的控制;
  2. GetSlotClass() 提供了重写自定义 Slot 类型的方法,默认使用的是 UPanelSlot,没有什么可以在编辑面板上修改的内容。

三、示例截图和代码

1. 示例截图

在这里插入图片描述
没有添加子控件时
添加一个Button控件后

2. 示例代码

MyCompoundWidget.h

/**
 * 自定义CompoundWidget
 */
UCLASS()
class UMyCompoundWidget : public UContentWidget
{
	GENERATED_BODY()

public:

	/** 构建 Slate 控件 */
	virtual TSharedRef<SWidget> RebuildWidget() override;
	
	/** 同步属性到显示 */
	virtual void SynchronizeProperties() override;

	/** 释放 Slate 资源 */
	virtual void ReleaseSlateResources(bool bReleaseChildren) override;

#if WITH_EDITOR
	/** 指定在 UMG 控件列表中的分类 */
	virtual const FText GetPaletteCategory() override;
#endif

protected:

	/** 底层 Slate 控件的缓存指针 */
	TSharedPtr<SMyCompoundWidget> MyWidget;

};

MyCompoundWidget.cpp

#define LOCTEXT_NAMESPACE "UMG"

TSharedRef<SWidget> UMyCompoundWidget::RebuildWidget()
{
	TSharedPtr<SWidget> Content;

	// 如果在编辑器中添加了子控件,我们可以通过 GetChildrenCount 获取到对应的 Slot
	if (GetChildrenCount() > 0)
	{
		Content = GetContentSlot()->Content ? GetContentSlot()->Content->TakeWidget() : SNullWidget::NullWidget;
	}
	else
	{
		Content = SNew(STextBlock)
			.Text(LOCTEXT("NoChildWidget", "没有子控件"));
	}
	
	// 创建自定义 Slate 控件
	MyWidget = SNew(SMyCompoundWidget, 1.0f)
		[
			Content.ToSharedRef()
		];

	return MyWidget.ToSharedRef();
}

void UMyCompoundWidget::SynchronizeProperties()
{
	Super::SynchronizeProperties();
}

void UMyCompoundWidget::ReleaseSlateResources(bool bReleaseChildren)
{
	Super::ReleaseSlateResources(bReleaseChildren);

	MyWidget.Reset();
}

#if WITH_EDITOR
const FText UMyCompoundWidget::GetPaletteCategory()
{
	return LOCTEXT("Custom", "Custom"); 
}
#endif

#undef LOCTEXT_NAMESPACE

总结

总的来说,封装方法还是比较简单的,不过这只是一个几乎没有什么功能的示例。
如果想要做好一个控件,还需要在许多细节地方多下功夫,UE4 源码是一个非常好的学习对象,本文只是提供了一个基础思路。

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值