编辑器扩展及Slate杂谈

前言

编辑器扩展和Slate相关内容大杂烩, 不定期记录

自定义属性界面

SObjectPropertyEntryBox

用于选择资源的界面控件

image-20210106105801136

直接上代码

  • .h
1
2
3
4
5


TSharedPtr<FAssetThumbnailPool> AssetThumbnailPool;
UObject* SelectObject;
TSharedPtr<SObjectPropertyEntryBox> EditWidgetBox;
  • cpp
1
2
3
4
void SOVR_Hud::Construct(const FArguments& InArgs)
{
AssetThumbnailPool = MakeShareable(new FAssetThumbnailPool(24));
................................
1
2
3
4
5
6
SAssignNew(EditWidgetBox,SObjectPropertyEntryBox)
.AllowedClass(UWidgetBlueprint::StaticClass())
.OnObjectChanged(this, &SOVR_Hud::OnSetObject)
.ObjectPath(this,&SOVR_Hud::GetPathName)
.OnShouldFilterAsset(this,&SOVR_Hud::ShouldFilterAsset)
.ThumbnailPool(AssetThumbnailPool)
1
2
3
4
void SOVR_Hud::OnSetObject(const FAssetData& AssetData)
{
SelectObject = AssetData.GetAsset();
}
1
2
3
4
FString SOVR_Hud::GetPathName()const
{
return SelectObject ? SelectObject->GetPathName() : TEXT("");
}
  • AllowedClass : Class类型, 这个是资源的类型
  • OnObjectChanged : 选择改变以后的代理
  • ObjectPath : 提供资源的路径名称
  • OnShouldFilterAsset : 过滤选项, 非必须
  • ThumbnailPool: Icon的对象池, 如果不设置就没有Icon

注意:

GetPathName()必须要返回真实的PathName,否则会导致界面显示还是None,如果没有正确的资源那么需要返回空或者None,否则会导致启动插件一次性卡顿并报警告

SClassPropertyEntryBox

类似的, 用于选择Class的界面控件

image-20210106110849937

1
2
3
4
5
6
7
SAssignNew(ClassBox, SClassPropertyEntryBox)
.MetaClass(AActor::StaticClass())
//.RequiredInterface(UStaticMesh::StaticClass())
.IsBlueprintBaseOnly(false)
.ShowTreeView(true)
.SelectedClass(this, &SOVR_Hud::OnGetClass)
.OnSetClass(this, &SOVR_Hud::OnSetClass)
1
2
3
4
5
6
7
8
const UClass* SOVR_Hud::OnGetClass() const
{
return SelectedClass;
}
void SOVR_Hud::OnSetClass(const UClass* SetClass)
{
SelectedClass = SetClass;
}

cpp运行EditorUtitlityWidget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//SelectObject = 任意编辑器UMG资源

UWidgetBlueprint* Blueprint = Cast<UWidgetBlueprint>(SelectObject);
if (Blueprint && Blueprint->GeneratedClass->IsChildOf(UEditorUtilityWidget::StaticClass())) {
const UEditorUtilityWidget* CDO = Blueprint->GeneratedClass->GetDefaultObject<UEditorUtilityWidget>();
if (CDO->ShouldAutoRunDefaultAction()) {
UEditorUtilityWidget* Instance = NewObject<UEditorUtilityWidget>(GetTransientPackage(), Blueprint->GeneratedClass);
Instance->ExecuteDefaultAction();
}
else {
FName RegistrationName = FName(*(Blueprint->GetPathName() + TEXT("_ActiveTab")));
FText DisplayName = FText::FromString(Blueprint->GetName());
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));
TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
TSharedRef<SDockTab> NewDockTab = LevelEditorTabManager->InvokeTab(RegistrationName);
}

}

这个方案有缺陷, 就是第一次还是需要在编辑器下右键运行一次才能用cpp来运行, 因为右键运行会调用到 UEditorUtilitySubsystem::SpawnAndRegisterTab(), 在这个函数中会对UMG进行初始化, 而用上述方法是没法初始化的

恶心的问题是, 此模块基本上都没有导出符号(即没法在自己的cpp中使用任何有关UEditorUtilityWidget的类), 没法自己初始化, 解决方法二选一

  1. 手动右键先运行一次(不推荐)
  2. 使用AutoRunDefaultAction, 然后在蓝图中自己运行自己的资源(这个资源还没法cast, 否则还能写个通用方法)

image-20210106162756815

消息窗口

先看两个枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace EAppMsgType
{
/**
* Enumerates supported message dialog button types.
*/
enum Type
{
Ok,
YesNo,
OkCancel,
YesNoCancel,
CancelRetryContinue,
YesNoYesAllNoAll,
YesNoYesAllNoAllCancel,
YesNoYesAll,
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace EAppReturnType
{
/**
* Enumerates message dialog return types.
*/
enum Type
{
No,
Yes,
YesAll,
NoAll,
Cancel,
Ok,
Retry,
Continue,
};

前者为输入,后者会返回

可以通过返回类型的不同来做不同的逻辑

1
EAppReturnType::Type ReturnType = FMessageDialog::Open(EAppMsgType::OkCancel, DialogText);
  • 强行封装蓝图库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
UENUM(BlueprintType)
enum class EMsgType : uint8
{
Ok,
YesNo,
OkCancel,
YesNoCancel,
CancelRetryContinue,
YesNoYesAllNoAll,
YesNoYesAllNoAllCancel,
YesNoYesAll,

};
UENUM(BlueprintType)
enum class EMsgRetType : uint8
{
No,
Yes,
YesAll,
NoAll,
Cancel,
Ok,
Retry,
Continue,

};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
EMsgRetType UFlib_IO::OpenMessageWindow(EMsgType Type, FText Msg)
{
EAppMsgType::Type tp;
switch (Type)
{
case EMsgType::Ok:
tp = EAppMsgType::Ok;
break;
case EMsgType::YesNo:
tp = EAppMsgType::YesNo;
break;
case EMsgType::OkCancel:
tp = EAppMsgType::OkCancel;
break;
case EMsgType::YesNoCancel:
tp = EAppMsgType::YesNoCancel;
break;
case EMsgType::CancelRetryContinue:
tp = EAppMsgType::CancelRetryContinue;
break;
case EMsgType::YesNoYesAllNoAll:
tp = EAppMsgType::YesNoYesAllNoAll;
break;
case EMsgType::YesNoYesAllNoAllCancel:
tp = EAppMsgType::YesNoYesAllNoAllCancel;
break;
case EMsgType::YesNoYesAll:
tp = EAppMsgType::YesNoYesAll;
break;
}
EAppReturnType::Type retType = FMessageDialog::Open(tp, Msg);
EMsgRetType returnType = EMsgRetType::Ok;
switch (retType)
{
case EAppReturnType::No:
returnType = EMsgRetType::No;
break;
case EAppReturnType::Yes:
returnType = EMsgRetType::Yes;
break;
case EAppReturnType::YesAll:
returnType = EMsgRetType::YesAll;
break;
case EAppReturnType::NoAll:
returnType = EMsgRetType::NoAll;
break;
case EAppReturnType::Cancel:
returnType = EMsgRetType::Cancel;
break;
case EAppReturnType::Ok:
returnType = EMsgRetType::Ok;
break;
case EAppReturnType::Retry:
returnType = EMsgRetType::Retry;
break;
case EAppReturnType::Continue:
returnType = EMsgRetType::Continue;
break;
}
return returnType;
}

image-20210108110520787