UE4 Slate二 用UMG思想去理解Slate+Slate编码

原创文章,转载请注明出处。

点击观看上一篇《UE4 Slate一 开篇》
点击观看下一篇《UE4 Slate三 SlateUI代码讲解》

虚幻引擎 SlateUI介绍

  • 1>前言
  • 2>为什么要用UMG去理解我们的Slate?
  • 3>Slate的书写格式(链式编程)
  • 4>编码前第一步, 创建我们的Editor Standalone Window插件
  • 5>编码前第二步, 创建我们的参考UMG
  • 6>编码,直接贴出代码,下一篇文章中再对代码详细介绍
    • 6.1>SlateUI代码,纯UI代码,没有往插件面板上加
      • 6.1.1>头文件SlateMain.h
      • 6.1.2>cpp文件SlateMain.cpp
    • 6.2>将上面创建的SlateUI添加到插件面板上
  • 7>SlateUI效果

1>前言

我们基于编辑器插件EditorStandaloneWindow来作为载体制作我们的SlateUI。
Engine Version:4.25.4

文章内的插件代码下载链接
解压后放到Plugins目录下
UE4 Slate二 用UMG思想去理解Slate+Slate编码_第1张图片

2>为什么要用UMG去理解我们的Slate?

做之前我是先根据我想完成的样子做了一个UMG,然后再用Slate实现成我这个UMG的样子。
1>因为Slate并不可视化,需要我们在代码里面手动一行行的敲(链式编程)
2>写Slate的时候可以对照UMG的参数,将参数给到我们的Slate上
3>对照着写,方便我们记录写到了什么位置了。(链式编程)代码实在是有点变态(不友好),一长串往下的都是。

3>Slate的书写格式(链式编程)

优点:
1>效率比UMG要高,因为UMG封装的就是Slate
缺点:
1>不能断点调试,断点无法命中链式内部
2>编写界面制作麻烦且不易维护

4>编码前第一步, 创建我们的Editor Standalone Window插件

Plugins文件夹下的test5_EditorStandlonWindow就是我们新建的插件, 注意我们这个插件是Editor模式下才会被加载的,当然我们可以把Slate的代码移植到我们的GamePlay里面,新弄个模块,这样就能参与我们的GamePlay部分的逻辑了。
UE4 Slate二 用UMG思想去理解Slate+Slate编码_第2张图片
UE4 Slate二 用UMG思想去理解Slate+Slate编码_第3张图片
创建完后启动编辑器会多出如下图的一个图标
UE4 Slate二 用UMG思想去理解Slate+Slate编码_第4张图片

5>编码前第二步, 创建我们的参考UMG

UE4 Slate二 用UMG思想去理解Slate+Slate编码_第5张图片
UE4 Slate二 用UMG思想去理解Slate+Slate编码_第6张图片
下面我们将用Slate来实现出上面的UMG样子来

6>编码,直接贴出代码,下一篇文章中再对代码详细介绍

6.1>SlateUI代码,纯UI代码,没有往插件面板上加

UE4 Slate二 用UMG思想去理解Slate+Slate编码_第7张图片

6.1.1>头文件SlateMain.h

SlateMain.h这个文件是创建的UI,但是还没有被加到插件的视口上

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "SlateCore.h"
#include "Widgets/SCompoundWidget.h"
#include "Components/SlateWrapperTypes.h"
#include "Animation/CurveSequence.h"
#include "SlateMainWidgetStyle.h"


//Slot概念很重要, 插槽就是说我们在他下面可以添加多少个子控件,下面有三个我们写Slate会经常继承的类
//SCompoundWidget有一个插槽, 固定一个, SButton, SBox
//SPanel: 多个插槽 比如SVerticalBox, SHorizontalBox
//SLeafWidget:没有插槽, 比如STextBlock

class SMyAnimationSlate;

//这个类的作用就是去用Slate代码实现一次这个UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')
class SMainSlate : public SCompoundWidget /*public SUserWidget*/
{
public:
	//SLATE_BEGIN_ARGS+SLATE_END_ARGS 其实是一个结构体, 内部写的东西都相当于写在了一个结构体里面
	SLATE_BEGIN_ARGS(SMainSlate)
	{

	}
	SLATE_END_ARGS()

	//外部执行SNew或者SAssignNew时候会调用Construct()
	void Construct(const FArguments& InArgs);

protected:
	//SButton的事件绑定
	FReply OnFirstSButton_OnClicked();
	void OnFirstSButton_OnPressed();
	void OnFirstSButton_OnReleased();
	void OnFirstSButton_OnHovered();
	void OnFirstSButton_OnUnhovered();


	//SChechBox的事件绑定
	template<int32 T>
	void MyOnCheckStateChanged(ECheckBoxState emState)
	{
		if (CheckBoxArray.IsValidIndex(T))
		{
			for (int32 i = 0; i < CheckBoxArray.Num(); i++)
			{
				if (i == T)
				{
					if (CheckBoxArray[i].IsValid() || CheckBoxArray[i].Get())
					{
						CheckBoxArray[i]->SetIsChecked(emState);
					}

				}
				else
				{
					if (CheckBoxArray[i].IsValid() || CheckBoxArray[i].Get())
					{
						CheckBoxArray[i]->SetIsChecked(ECheckBoxState::Unchecked);
					}
				}
			}
		}
	}
private:
	//最外层的层, 就是我们默认创建UMG带的层
	TSharedPtr<class SConstraintCanvas> CanvasPanel_0;

	//存放SCheckBox的数组, 选中后可维护唯一选中
	TArray<TSharedPtr<class SCheckBox>> CheckBoxArray;

	//左侧层
	TSharedPtr<class SScrollBox> MyScrollBoxLeft;

	//右侧层
	TSharedPtr<class SScrollBox> MyScrollBoxRight;

	//自定义的样式
	const struct FMainWidgetStyle *MyCustomWidgetStyle;
};


//Slate动画演示类
class SMyAnimationSlate : public SCompoundWidget /*public SUserWidget*/
{
public:
	//SLATE_BEGIN_ARGS+SLATE_END_ARGS 其实是一个结构体, 内部写的东西都相当于写在了一个结构体里面
	SLATE_BEGIN_ARGS(SMyAnimationSlate)
	{
		_Visibility = EVisibility::SelfHitTestInvisible;
	}

	//外部Slot里面的都是作为这个Slate里面的ChildSlot出现
	SLATE_DEFAULT_SLOT(FArguments, OutSlots)

	SLATE_END_ARGS()

	//外部执行SNew或者SAssignNew时候会调用Construct()
	void Construct(const FArguments& InArgs);

	//鼠标进入
	virtual void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;

	//鼠标离开
	virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override;

	//颜色变化
	FLinearColor SlateColorChanged();
private:
	//Slate动画对象
	FCurveSequence MySlateCurveSequence;
};

6.1.2>cpp文件SlateMain.cpp

SlateMain.cpp这个文件是创建的UI,但是还没有被加到插件的视口上

#include "SlateMain.h"
#include "Widgets/Layout/SConstraintCanvas.h"
#include "Widgets/Layout/SScrollBox.h"
#include 

#define LOCTEXT_NAMESPACE "SlateMain"

static int32 nDynamicAddNum = 0;

void SMainSlate::Construct(const FArguments& InArgs)
{
	//计数器
	nDynamicAddNum = 0;
	//初始化checkbox数组, 默认给3个值
	CheckBoxArray.SetNum(3);

	//获取自定义样式
	MyCustomWidgetStyle = &Ftest5_EditorStandlonWindowStyle::Get().GetWidgetStyle<FMainWidgetStyle>(TEXT("MyCustomWidgetStyleBP"));

	//链式编程 维护很麻烦...
	ChildSlot
	[
		SAssignNew(CanvasPanel_0, SConstraintCanvas)

		+ SConstraintCanvas::Slot()
		.Anchors(0.f)	//对应UMG这个文本控件上的Anchors属性,拷贝过来即可
		.Offset(FMargin(600, 0.f, 100, 100))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SNew(SImage)
			.Image(&MyCustomWidgetStyle->TemplateImage)
			.ColorAndOpacity(FSlateColor(MyCustomWidgetStyle->TemplateColor))
		]

		//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的文本
		+ SConstraintCanvas::Slot()
		.Anchors(0.f)	//对应UMG这个文本控件上的Anchors属性,拷贝过来即可
		.Offset(FMargin(20.0, 12.f, 600, 72))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SNew(STextBlock)
			.Text(LOCTEXT("SMainSlate_TextBlock1", "Mesh合并界面"))
			.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 53))
			.ColorAndOpacity(FSlateColor(FLinearColor(1, 0, 0.361307, 1)))
		]

		//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的文本
		+SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(132, 128, 350.0, 40))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SNew(STextBlock)
			.Text(LOCTEXT("SMainSlate_TextBlock2", "Level中的模型列表"))
			.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			.ColorAndOpacity(FSlateColor(FLinearColor(1, 1, 1, 1)))
		]

		//对应左侧的ScrollBox
		+ SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(132, 180.0, 369, 625))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SAssignNew(MyScrollBoxLeft, SScrollBox)
			.Orientation(Orient_Vertical)
			.ScrollBarAlwaysVisible(true)

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)

			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox_TextBlock1", "场景中模型_1"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox_TextBlock2", "场景中模型_2"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox_TextBlock3", "场景中模型_3"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox_TextBlock4", "场景中模型_4"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox_TextBlock5", "场景中模型_5"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]
		]

		//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的文本
		+ SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(764.0, 128, 350.0, 40))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SNew(STextBlock)
			.Text(LOCTEXT("SMainSlate_TextBlock3", "预览->按材质合并之后的"))
			.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			.ColorAndOpacity(FSlateColor(FLinearColor(1, 1, 1, 1)))
		]

		//对应中间的ScrollBox
		+ SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(764, 180.0, 370.0, 625))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SAssignNew(MyScrollBoxRight, SScrollBox)
			.Orientation(Orient_Vertical)
			.ScrollBarAlwaysVisible(true)

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)

			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox2_TextBlock1", "预览->按材质合并之后的模型_1"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox2_TextBlock2", "预览->按材质合并之后的模型_2"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox2_TextBlock3", "预览->按材质合并之后的模型_3"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox2_TextBlock4", "预览->按材质合并之后的模型_4"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]

			+ SScrollBox::Slot()
			.Padding(0.f)
			.HAlign(HAlign_Fill)
			.VAlign(VAlign_Fill)
			[
				//链式编程里面不能写for, 所以我就new5个吧。。。
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_SVerticalBox2_TextBlock5", "预览->按材质合并之后的模型_5"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]
		]


		//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的
		+SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(584.0, 468.0, 100.0, 40))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SNew(SButton)
			.HAlign(HAlign_Center)
			.VAlign(VAlign_Center)
			.ContentPadding(0.f)
			.OnClicked(this, &SMainSlate::OnFirstSButton_OnClicked)	//事件绑定的技巧, 转到定义, 看那边的代理是怎么定义的, 把参数和返回值拿过来定义一个函数即可
			.OnPressed(this, &SMainSlate::OnFirstSButton_OnPressed)
			.OnReleased(this, &SMainSlate::OnFirstSButton_OnReleased)
			.OnHovered(this, &SMainSlate::OnFirstSButton_OnHovered)
			.OnUnhovered(this, &SMainSlate::OnFirstSButton_OnUnhovered)
			[
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_Button_Text1", "---->"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]
		]

		//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的
		+ SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(1520.0, 888.0, 225.0, 90))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SNew(SButton)
			.HAlign(HAlign_Center)
			.VAlign(VAlign_Center)
			.ContentPadding(0.f)
			.OnClicked(this, &SMainSlate::OnFirstSButton_OnClicked)	//事件绑定的技巧, 转到定义, 看那边的代理是怎么定义的, 把参数和返回值拿过来定义一个函数即可
			.OnPressed(this, &SMainSlate::OnFirstSButton_OnPressed)
			.OnReleased(this, &SMainSlate::OnFirstSButton_OnReleased)
			.OnHovered(this, &SMainSlate::OnFirstSButton_OnHovered)
			.OnUnhovered(this, &SMainSlate::OnFirstSButton_OnUnhovered)
			[
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_Button_Text2", "一键合并"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 30))
			]
		]

		//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的
		+ SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(1260.0, 172.0, 600.0, 40.0))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SAssignNew(CheckBoxArray[0], SCheckBox)
			.IsChecked(false)
			.IsEnabled(true)
			.OnCheckStateChanged(this, &SMainSlate::MyOnCheckStateChanged<0>)
			[
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_checkbox_Text1", "按材质合并成SingleMesh"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]
		]

		//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的
		+ SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(1260.0, 264.0, 600.0, 40.0))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SAssignNew(CheckBoxArray[1], SCheckBox)
			.IsChecked(false)
			.IsEnabled(true)
			.OnCheckStateChanged(this, &SMainSlate::MyOnCheckStateChanged<1>)
			[
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_checkbox_Text2", "按材质合并成InstanceMesh"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]
		]

		//对应UMG(WidgetBlueprint'/Game/Blogs_Slate/ReferUMGBP.ReferUMGBP')中的
		+ SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(1260.0, 360.0, 600.0, 40.0))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SAssignNew(CheckBoxArray[2], SCheckBox)
			.IsChecked(false)
			.IsEnabled(true)
			.OnCheckStateChanged(this, &SMainSlate::MyOnCheckStateChanged<2>)
			[
				SNew(STextBlock)
				.Text(LOCTEXT("SMainSlate_checkbox_Text2", "按材质合并成HeighInstanceMesh"))
				.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
			]
		]

		//测试动画的
		+ SConstraintCanvas::Slot()
		.Anchors(0.f)
		.Offset(FMargin(100, 600, 200, 200))	//这个可能会迷惑, 第一个参数在这是PositionX, 第二个参数在这是PositionY, 第三个参数在这是SizeX, 第四个参数在这是SizeY.找不到设置坐标的同学注意看这里
		.Alignment(FVector2D(0.f, 0.f))	//同样的, 对应Alignment是个FVector2D
		.AutoSize(false)			//对应AutoSize
		.ZOrder(0)
		[
			SNew(SMyAnimationSlate)
			[
				SNew(SButton)
				.HAlign(HAlign_Center)
				.VAlign(VAlign_Center)
				.ContentPadding(0.f)
				.OnClicked(this, &SMainSlate::OnFirstSButton_OnClicked)	//事件绑定的技巧, 转到定义, 看那边的代理是怎么定义的, 把参数和返回值拿过来定义一个函数即可
				.OnPressed(this, &SMainSlate::OnFirstSButton_OnPressed)
				.OnReleased(this, &SMainSlate::OnFirstSButton_OnReleased)
				.OnHovered(this, &SMainSlate::OnFirstSButton_OnHovered)
				.OnUnhovered(this, &SMainSlate::OnFirstSButton_OnUnhovered)
				[
					SNew(STextBlock)
					.Text(LOCTEXT("SMainSlate_ButtonSlateAnimation_Text3333", "测试Slate动画"))
					.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
				]
			]
		]
	];
}


FReply SMainSlate::OnFirstSButton_OnClicked()
{
	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnClicked"));

	++nDynamicAddNum;

	FString s2 = TEXT("new add") + FString::FromInt(nDynamicAddNum);
	MyScrollBoxLeft->AddSlot()
		[
			SNew(STextBlock)
			.Text(FText::FromString(s2))
		.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
		.ColorAndOpacity(FSlateColor(FLinearColor(1, 0, 1, 1)))
		];

	MyScrollBoxRight->AddSlot()
		[
			SNew(STextBlock)
			.Text(FText::FromString(s2))
		.Font(FCoreStyle::GetDefaultFontStyle("Roboto", 24))
		.ColorAndOpacity(FSlateColor(FLinearColor(1, 1, 0, 1)))
		];

	return FReply::Handled();
}

void SMainSlate::OnFirstSButton_OnPressed()
{
	//UE_LOG(LogTemp, Warning, TEXT("OnFirstSButton_OnPressed"));
	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnPressed"));
}

void SMainSlate::OnFirstSButton_OnReleased()
{
	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnReleased"));
}

void SMainSlate::OnFirstSButton_OnHovered()
{
	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnHovered"));
}

void SMainSlate::OnFirstSButton_OnUnhovered()
{
	GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Green, TEXT("OnFirstSButton_OnUnhovered"));
}


void SMyAnimationSlate::Construct(const FArguments& InArgs)
{
	MySlateCurveSequence = FCurveSequence(0.f, 1.f);
	ColorAndOpacity = TAttribute<FLinearColor>::Create(TAttribute<FLinearColor>::FGetter::CreateSP(this, &SMyAnimationSlate::SlateColorChanged));

	ChildSlot
		[
			InArgs._OutSlots.Widget
		];
}

void SMyAnimationSlate::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
	if (!MySlateCurveSequence.IsAtEnd())
	{
		if (MySlateCurveSequence.IsInReverse() && MySlateCurveSequence.IsPlaying())
		{
			MySlateCurveSequence.Reverse();
		}
		else if (!MySlateCurveSequence.IsPlaying())
		{
			MySlateCurveSequence.Play(this->AsShared());
		}
	}
}

void SMyAnimationSlate::OnMouseLeave(const FPointerEvent& MouseEvent)
{
	if (!MySlateCurveSequence.IsAtStart())
	{
		if (!MySlateCurveSequence.IsInReverse() && MySlateCurveSequence.IsPlaying())
		{
			MySlateCurveSequence.Reverse();
		}
		else if (!MySlateCurveSequence.IsPlaying())
		{
			MySlateCurveSequence.PlayReverse(this->AsShared());
		}
	}
}

FLinearColor SMyAnimationSlate::SlateColorChanged()
{
	return FLinearColor(1, 1, 1, 1 - MySlateCurveSequence.GetLerp());
}

#undef LOCTEXT_NAMESPACE



6.2>将上面创建的SlateUI添加到插件面板上

因为我们是基于UE4 Plugin创建了一个Editor Standlone Window,
在这个插件代码中(test5_EditorStandlonWindow.cpp)中的OnSpawnPluginTab()方法中,
就是创建Slate的部分,所以我们在这个位置写就好了。
下面我们在test5_EditorStandlonWindow.cpp 中的OnSpawnPluginTab()方法中SNew/SAssignNew我们在上面新建好的SMainSlate
然后打开我们编辑器就能看到效果了,下一篇对本篇写的Slate的代码做一些介绍

TSharedRef<SDockTab> Ftest5_EditorStandlonWindowModule::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("Ftest5_EditorStandlonWindowModule::OnSpawnPluginTab")),
		FText::FromString(TEXT("test5_EditorStandlonWindow.cpp"))
		);

	//第一种方式
	MyMainSlate = SNew(SMainSlate);
	return SNew(SDockTab)
		[
			MyMainSlate->AsShared()
		];

	//第二种方式
	/*SAssignNew(MyMainSlate, SMainSlate);
	return SNew(SDockTab)
		[
			MyMainSlate->AsShared()
		];*/

	//第三种方式
	/*return SNew(SDockTab)
		[
			SNew(SMainSlate)
		];*/

	//第四种方式
	/*return SNew(SDockTab)
		[
			SAssignNew(MyMainSlate, SMainSlate)
		];*/

	//原来的代码注释掉了
		//.TabRole(ETabRole::NomadTab)
		//[
		//	// Put your tab content here!
		//	SNew(SBox)
		//	.HAlign(HAlign_Center)
		//	.VAlign(VAlign_Center)
		//	[
		//		SNew(STextBlock)
		//		.Text(WidgetText)
		//	]
		//];
}

写了这么多效果是什么样子的呢?

7>SlateUI效果

点击观看上一篇《UE4 Slate一 开篇》
点击观看下一篇《UE4 Slate三 SlateUI代码讲解》

谢谢,创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 <( ̄︶ ̄)>

你可能感兴趣的:(UE5,UE4,UE4/UE5,Slate,UE4/UE5,SlateUI,SlateUI制作,编辑器扩展,UE4/UE5编辑器扩展)