编辑器扩展:自定义预览试图

前言

image-20210819094640216

最近因为项目需要打算做一个简易的插槽编辑器, 因为传统的插槽编辑都需要打开每个静态模型单独编辑, 操作相对比较麻烦, 那么首先需要创建一个自定义的预览试图, 本文先从这个开始

准备工作

首选创建一个插件, 类型随意, 如果先效率高一点简易选择 Editor Toolbar Button, 这样就直接可以通过一个工具栏按钮启动插件

关于这个按钮, 我个人习惯还是把他放到工具栏最前面,也就是要重写默认模块文件RegisterMenus()方法里的内容, 如下

1
2
3
4
5
6
7
8
9
10
11
//工具栏
TSharedPtr<FExtender> ToolBarExtender = MakeShareable(new FExtender());
ToolBarExtender->AddToolBarExtension("File",
EExtensionHook::Before, PluginCommands, FToolBarExtensionDelegate::CreateLambda(
[this](FToolBarBuilder& Builder)
{
Builder.AddToolBarButton(FSimpleSocketEditorCommands::Get().PluginAction);
}
));

LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolBarExtender);

这样就把按钮放到了File类型的前面, 即最靠前的地方

image-20210819095058833

编辑器创建

这样默认点击插件按钮出来的就是一个消息窗口, 我们需要参考Standeralone模式的插件来创建一个窗口

1
2
3
4
5
6
7
8
void FSimpleSocketEditorModule::MapActions()
{
PluginCommands = MakeShareable(new FUICommandList);
PluginCommands->MapAction(
FSimpleSocketEditorCommands::Get().PluginAction,
FExecuteAction::CreateRaw(this, &FSimpleSocketEditorModule::PluginButtonClicked),
FCanExecuteAction());
}
1
2
3
4
void FSimpleSocketEditorModule::PluginButtonClicked()
{
FGlobalTabmanager::Get()->TryInvokeTab(SimpleSocketEditorTabName);
}
1
2
3
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(SimpleSocketEditorTabName, FOnSpawnTab::CreateRaw(this, &FSimpleSocketEditorModule::OnSpawnPluginTab))
.SetDisplayName(LOCTEXT("SocketEditorTitil", "Socket Editor"))
.SetMenuType(ETabSpawnerMenuType::Hidden);
1
2
3
4
5
6
7
8
TSharedRef<SDockTab> FSimpleSocketEditorModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{
return SNew(SDockTab)
.TabRole(ETabRole::NomadTab)
[
SNew(SSHud_SimpleSocket)
];
}

层层相关, 最后打开一个我们自定义的Slate控件

其实一般情况会创建一个类似资源编辑器的类来作为编辑器视图类, 比如FStaticMeshEditor, 或者如我之前文章创建自定义编辑器写的那种资源编辑器, 但是这里我们就任性一次, 用Slate的方式硬怼出来一个编辑器

视图类

然后就是视图相关的类了, 这里有好几个类可以扩展, 虽然都不是必须的, 我们先从最主要的类开始扩展,这个继承自SEditorViewport的类, 他就是我们视图界面的Slate

1
2
class SEditorViewport_SimpleSocket : public SEditorViewport, public FGCObject, public ICommonEditorViewportToolbarInfoProvider
{//.....}

我们又额外继承了FGCObject(用于GC管理), ICommonEditorViewportToolbarInfoProvider(界面扩展)

接下来重写几个重要函数

1
2
3
virtual void OnFocusViewportToSelection()override;//按键F的实现, 就是聚焦的逻辑
virtual TSharedRef<FEditorViewportClient> MakeEditorViewportClient() override;//界面视图UI的
virtual TSharedPtr<SWidget> MakeViewportToolbar()override;//工具栏UI

如果想简单的看到视图效果, 我们可以大致像下面一样实现

1
2
3
4
5
6
7

TSharedRef<FEditorViewportClient> SEditorViewport_SimpleSocket::MakeEditorViewportClient()
{
FPreviewScene PreviewScene = MakeShareable(new FPreviewScene());
EditorViewportClient = MakeShareable(new FEditorViewportClient(nullptr, PreviewScene));
return EditorViewportClient.ToSharedRef();
}

里面有两个类我们使用了默认类型, 分别是FPreviewSceneFEditorViewportClient

同样的,工具栏也使用默认类型实现即可

1
2
3
4
TSharedPtr<SWidget> SEditorViewport_SimpleSocket::MakeViewportToolbar()
{
return SNew(SCommonEditorViewportToolbarBase, SharedThis(this));
}

但是这样场景中是黑漆漆一片, 没有任何物体

所以我们需要对PreviewScene对象添加物体

1
2
3
UStaticMesh* mesh =  LoadObject<UStaticMesh>(NULL, TEXT("StaticMesh'/Engine/EngineMeshes/Cube.Cube'"), NULL, LOAD_None, NULL);;
FTransform Transform = FTransform::Identity;
PreviewScene->AddComponent(PreviewMeshComp, Transform);

这样PreviewScene内就有了一个默认的Cube物体了

FAdvancedPreviewScene

至此场景里虽然能对物体进行光照, 但是还是黑漆漆的, 引擎给了我们一个高级的场景类, 多数的预览试图也是用的该类, 即FAdvancedPreviewScene

该对象内有很多参数可以设置, 比如我们可以这样使用

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
//.h
TSharedPtr<class FAdvancedPreviewScene> AdvancedScene;

//.cpp
FAdvancedPreviewScene::ConstructionValues ConstructValues; //预览场景参数
ConstructValues.SetCreatePhysicsScene(false); //关闭物理场景
ConstructValues.ShouldSimulatePhysics(false); //关闭物理模拟
ConstructValues.LightBrightness = 3; //设置光照强度
ConstructValues.SkyBrightness = 1; //设置天空光强度
ConstructValues.bEditor = true;

AdvancedScene = MakeShareable(new FAdvancedPreviewScene(ConstructValues));

//添加天空光组件
USkyLightComponent* Skylight = NewObject<USkyLightComponent>();
AdvancedScene->AddComponent(Skylight, FTransform::Identity);

//设置地面
AdvancedScene->SetFloorVisibility(false); //显示地面,默认就是显示
AdvancedScene->SetFloorOffset(100.f); //设置地面高度偏移,正数是往下偏移
//设置方向光
AdvancedScene->DirectionalLight->SetMobility(EComponentMobility::Movable);
AdvancedScene->DirectionalLight->CastShadows = true; //启用阴影
AdvancedScene->DirectionalLight->CastStaticShadows = true;
AdvancedScene->DirectionalLight->CastDynamicShadows = true;
AdvancedScene->DirectionalLight->SetIntensity(3);

这样就能完成指定图片那种光照效果了