Published on

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

前言

image-20210819094640216

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

准备工作

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

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

//工具栏
`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模式的插件来创建一个窗口

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

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

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

视图类

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

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

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

接下来重写几个重要函数

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

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


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

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

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

`TSharedPtr<SWidget>` SEditorViewport_SimpleSocket::MakeViewportToolbar()
{
	return SNew(SCommonEditorViewportToolbarBase, SharedThis(this));
}

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

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

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

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

//.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);

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