在上一篇我们简单介绍了UE4下Plugin的扩展步骤, 这次我们再研究下UE4中实现Custom Editor窗口的过程。
UE4中我们最常用的编辑窗口就是默认的Level Editor 的窗口, 它提供我们对场景内任意物件进行编辑预览的功能。UE中默认的例如:动作、粒子、角色等,都有一个独立的Editor预览窗口。如果我们想对自定义的Asset文件做一些操作和显示上的自定义,那么我们就需要实现Custom Editor。
一, 常规Editor窗口的组成
一个完整的Editor窗口的组成:
1, 顶部菜单栏 : 快捷菜单的扩展;
2, 工具栏 : 工具栏按钮的自定义;
3, 细节面板 : 显示细节参数;
4, 视图窗口 : 预览对象的窗口, 所见即所得;
二, 窗口的布局(简单介绍Slate)
UE4的Editor下使用Slate来对窗口个的多个子窗口进行布局, 其中SCompoundWidget是窗口组件的主要基类。看下如下代码, 简单熟悉下Slate的语法。
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
| TabLayout = FTabManager::NewLayout("Ability_AbilityTimelineEditMode_Layout_v1") ->AddArea( FTabManager::NewPrimaryArea() ->SetOrientation(Orient_Vertical) //垂直布局 ->Split ( //Toolbar FTabManager::NewStack() ->SetSizeCoefficient(0.1f) //尺寸 ->SetHideTabWell(true) //是否隐藏标签页 ->AddTab(AceEditor->GetToolbarTabId(), ETabState::OpenedTab) ) ->Split ( //此处是个视图窗口 FTabManager::NewSplitter() //新的窗口分隔 ->SetOrientation(Orient_Horizontal) //水平布局 ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(0.7f) ->SetHideTabWell(false) ->AddTab(FAceEditorViewportSummoner::ID, ETabState::OpenedTab) ) ->Split ( //此处是属性详细页 FTabManager::NewStack() ->SetSizeCoefficient(0.3f) ->SetHideTabWell(false) ->AddTab(FAcePropertiesSummoner::ID, ETabState::OpenedTab) ) ) );
|
三,构建窗口的过程
在上一篇中, 我们简单介绍了如果自定义Asset并实现菜单中右键创建的过程。其中在FAssetTypeActions_Blueprint的OpenAssetEditor函数中, 我们将之前在UFactory创建的Asset对象, 传入新创建的Editor的初始化函数中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor) { EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone;
for (UObject* Object : InObjects) { if (UBlueprint* Blueprint = Cast<UBlueprint>(Object)) { bool isFirstCreate = Blueprint->bIsNewlyCreated; TSharedRef<FPathFollowerEditor> Editor(new FPathFollowerEditor()); TArray<UBlueprint*> Blueprints; Blueprints.Add(Blueprint);
//这一步很重要 Editor->InitEditor(Mode, EditWithinLevelEditor, Blueprints, ShouldUseDataOnlyEditor(Blueprint)); } } }
|
在来到我们自定义的FBlueprintEditor中, 看下InitEditor的函数实现:
1 2 3 4 5 6 7 8 9
| void InitEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, const TArray<UBlueprint*>& InBlueprints, bool ShouldUseDataOnlyEditor) { TArray<UObject*> ObjectsBeingEditted; for (UBlueprint* BP : InBlueprints) ObjectsBeingEditted.Add(BP);
//调用默认的蓝图编辑窗口, 如果我们需要自定义就修改这里. InitBlueprintEditor(Mode, IniInitEditortToolkitHost, InBlueprints, false); }
|
InitBlueprintEditor是UE4提供的默认的蓝图类编辑接口, 它会根据我们在工厂类中对我们自定义的蓝图Asset文件的类型来调用对应的蓝图编辑器, 例如是Actor, Animation或者Particle。但是如果我们想实现自定义的蓝图类型, 并且自定义对应的操作,那我们需要在InitEditor函数中进行扩展。
在自定义的InitBlueprintEditor中, 我们通过自定义实现FApplicationMode的方式, 来切换当前运行的模式, 进入如上图所示开始初始化的整个流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void InitBlueprintEditor(const EToolkitMode::Type Mode, const TSharedPtr<IToolkitHost>& InitToolkitHost, const TArray<UBlueprint*>& InAbilityBlueprints, bool ShouldUseDataOnlyEditor) { TSharedPtr<FAceEditor> Editor(SharedThis(this));
if (!Toolbar.IsValid()) Toolbar = MakeShareable(new FBlueprintEditorToolbar(SharedThis(this)));
TArray<UObject*> ObjectsBeingEditted; for(UBlueprint* BP : InAbilityBlueprints) ObjectsBeingEditted.Add(BP);
//Create mode. TSharedRef<FApplicationMode> ViewMode = MakeShareable(new FAceEditorViewMode(Editor)); AddApplicationMode(ViewMode->GetModeName(), ViewMode);
const TSharedRef<FTabManager::FLayout> DummyLayout = FTabManager::NewLayout("NullLayout")->AddArea(FTabManager::NewPrimaryArea()); const bool bCreateDefaultStandaloneMenu = true; const bool bCreateDefaultToolbar = true;
FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, "AceEditorApp", DummyLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectsBeingEditted);
SetCurrentMode(ViewMode->GetModeName()); }
|
简单介绍下这几个类的作用及他们的关系:
- SCompoundWidget: 大部分窗口部件的基类;
- FWorkflowTabFactory: FTab标签页自定义工厂;
- FPreviewScene: 封装用于预览或缩略图呈现的场景, 个人理解是主要负责预览场景的渲染;
- FEditorViewportClient: 视图窗口, 对摄像机移动,渲染调试, 鼠标点击等一些操作的高级封装;
- SSingleObjectDetailsPanel: 细节面板的基类, 对一些属性进行可视化编辑
四,延伸扩展
默认的Custom Editor中的初始化的对象, 只支持点击不支持鼠标移动等操作。如果需要类似Level Editor中的鼠标拖动等操作, 需要对这块的部分接口做扩展。还有一点需要注意的在Editor的下屏幕空间的拾取,决定了窗口里的一些鼠标点击等事件,所以所有需要参与点击类的Component, 必须有AActor作为对应的Owner,否则没法参与屏幕拾取~
在UE中, 需要渲染的可点击对象都会生成一个HitProxy数据,每一个HitProxy包含一个唯一的INT32的唯一标识码, HitProxyId。在渲染时, UE4会将HitProxyId转换成颜色, 写入一块宽高和屏幕瞪大的数组中, 这就是HitProxyMap。当每次鼠标移动的时候,根据对应屏幕的xy坐标索引得到对应的HitProxyId, 再根据这个Id得到对应的HitProxy数据。