|
# 前言概述:
这是一篇关于Unreal引擎 C++插件开发中关于自定义AssetEditor的开发教程,
本篇文章将会提供:
1. 一种清晰的思路去管理和开发自定义资产编辑器,方便其他开发人员使用;
2. 支持较为复杂的功能定制,使开发人员能够根据需求进行更灵活的操作;
3. 可以帮助开发人员提高对UnrealC++插件开发的技能和知识。
阅读文章可能需要的前置准备有:
了解如何在Unreal中创建自定义资产
了解Slate
了解C++基础的语法
文章最后的代码执行测试的引擎版本为:
Unreal Engine 5.0
创建与部署自定义资产编辑器类
首先本文想先通过一张相关类的说明图片直观解释创建一个AssetEditor涉及到的类和重要的接口方法。资产编辑器开发可用的接口繁多,但是依然可以参照一个主线:编辑器主要围绕FWorkFlowCentricApplication(或FAssetEditorToolkit)类进行开发,上游需要接入FAssetTypeAction类进行入口的实现。下游则需要实现自定义的Slate类,并通过通过父类方法创建Slate控件实例并注册到编辑器布局中。
类实现首先需要通过
# 一.前置工作:
●创建了Unreal插件作为代码开发模块,支持EditorModule开发。
●通过UFactory创建了新的资产类型。
# 二. AssetEditor的创建类型
+ 引擎提供的资产编辑器类
在Unreal中定制一个自定义的编辑器界面,可以使用两个类:
FAssetEditorToolkit和它的子类FWorkflowCentricApplication,它们被用来构建工作流程和编辑器类型的应用程序。
FWorkflowCentricApplication 类除了继承了父类的接口与功能,还包含了一个用于存储多个FApplicationMode的Map图ApplicationModeList,并且能够进行Mode的切换。
+ 使用场景建议
FWorkflowCentricApplication 适用于创建游戏开发过程中用于不同阶段的工具和界面,需要支持多Mode时可以选用该模式(行为树、动画蓝图资产等都使用了这种方法),相较于AssetEditroToolkit类来说,实现并不复杂,同时支持更多的功能扩展。
FAssetEditorToolkit 适用于创建编辑器工具来编辑游戏中的资产
+ 本文的实现继承FWorkflowCentricApplication类,两种类的基础实现没有差异。
# 三. Editor类的实现
+ FCustomAssetEditor是本文案例中继承FWorkflowCentricApplication的子类,
类的基础实现中除了重载父类实现定义编辑器样式方法外,还需要实现几个关键方法:InitCustomAssetEditor方法、自定义Slate创建的方法SpawnTab_CustomWidget和RegistorTabSpawners/UnRegistorTabSpawner方法,前两个方法的命名可由开发者自行定义,RegistorTabSpawners则是重载父类方法。
## 3.1 InitCustomAssetEditor方法:
在此方法中,主要分为两个内容:
① 初始化编辑器布局,可以使用以下步骤:
1. 通过创建适当类的实例并设置其属性来定义自定义 FLayout、菜单和工具栏。
2. 自定义 Slate 窗口现在将绑定到 TabManager,并使用自定义 FLayout、菜单和工具栏显示资产编辑器。
② 调用父类方法InitAssetEditor将自定义 Slate 窗口绑定到 TabManager。
## 3.2 RegistorTabSpawners/UnRegistorTabSpawner方法:
+ 我们首先需要知道,Tab(SDockTab)是引擎定义的一个控件类,每一个Tab都是可以被关闭或打开的SWidget.Tab类型的窗口右上角包含一个关闭按钮。窗口内则可以包含自定义显示的各种Slate内容。
+ 我们打开编辑器时,就会根据Layout布局加载对应的Tab控件,并显示其中的内容,即Tab装入的子控件(Child Slate)。
+ 在此方法中,最重要的是调用TabManager对象,TabManager类主要用于管理编辑器中的布局,创建Tab对象。在类中我们需要定义一个FName类型的TabID变量,用于在该方法中指示一个Slate窗口。我们需要通过调用TabManager的方法RegisterTabSpawner绑定TabID和Slate,用于后续定义Slate控件的显示。
## 3.3 SpawnTab_CustomWidget方法
在此方法中,将会创建并返回一个SWidget实例。
而RegistorTabSpawner方法通过调用TabManager的接口,将该Slate控件实例注册,与TabID进行绑定,并能够在InitCustomAssetEditor方法中添加到布局Flayout中被显示出来。
我们不仅可以创建简单的Slate控件(如SButton、IDetailsViews),也可以创建较为复杂的窗口,如SEditorViewport(3D预览窗口)、SGraphEditor(可以连接复杂自定义的节点图表系统)等内容,并将其添加到我们的自定义编辑器中。
在本文实例中,我在该方法中创建了一个最简单的Slate类SButton。
```cpp
TSharedRef<SDockTab> FArtAssetEditor::SpawnTab_CustomTab(const FSpawnTabArgs& Args)
{
return
SNew(SDockTab)
[
//Add A New S Element
SNew(SButton)
.Text(FText::FromString(&#34;Custom Tab!&#34;))
];
}
```
## 3.4 类的基础实现
Editor类的实现主要包括如下几部分:
CustomAssetEditor.h
```cpp
/*CustomAssetEditor.h*/
#pragma once
//engine
#include &#34;WorkflowOrientedApp/WorkflowCentricApplication.h&#34;
#include &#34;IDetailsView.h&#34;
//project 项目文件路径和类需要自己定义实现
#include &#34;CustomAsset.h&#34;
#include &#34;SCustomWidget.h&#34;
#define LOCTEXT_NAMESPACE &#34;CustomAssetEditor&#34;
class FCustomAssetEditor : public FWorkflowCentricApplication
{
//Basic views information in Unreal Editor
public:
FCustomAssetEditor();
virtual ~FCustomAssetEditor();
// FAssetEditorToolkit
FName GetToolkitFName() const override { return &#34;toolkitFName_CustomAssetEditor&#34;; }
FText GetBaseToolkitName() const override { return INVTEXT(&#34;BaseToolkitName_CustomAssetEditor&#34;); }
FString GetWorldCentricTabPrefix() const override { return &#34;Prefix_CustomAsset &#34;; }
FLinearColor GetWorldCentricTabColorScale() const override { return {}; }
// End of FAssetEditorToolkit*/
//Register TabID - TabPtr
void RegisterTabSpawners(const TSharedRef<class FTabManager>& InTabManager) override;
void UnregisterTabSpawners(const TSharedRef<class FTabManager>& InTabManager) override;
//Init Interface
void InitCustomAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr<class IToolkitHost>& InitToolkitHost, UCAsset* InCustomAsset);
//Independence Functions
//Custom Slate Element
static const FName CustomTabID;
TSharedRef<SDockTab> SpawnTab_CustomTab(const FSpawnTabArgs& Args);
//Asset
public:
TWeakObjectPtr<UCustomAsset> CustomAsset;
UCustomAsset* GetCustomAsset(){return CustomAsset.Get();}
};
#undef LOCTEXT_NAMESPACE
```
CustomAssetEditor.cpp
```cpp
/*CustomAssetEditor.cpp*/
#include &#34;CustomAssetEditor.h&#34;
//project 项目文件路径和类需要自己定义实现
#include &#34;SCustomWidget.h&#34;
#define LOCTEXT_NAMESPACE &#34;CustomAssetEditor&#34;
//Set Name
const FName FCustomAssetEditor::CustomTabID(TEXT(&#34;CustomAsset_Custom&#34;));
FCustomAssetEditor::FCustomAssetEditor()
{
CustomAsset = nullptr;
}
FCustomAssetEditor::~FCustomAssetEditor()
{
}
void FCustomAssetEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
{
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
//group
WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT(&#34;WorkspaceMenu_CustomAssetEditor&#34;, &#34;CustomAsset Editor&#34;));
InTabManager->RegisterTabSpawner(CustomTabID, FOnSpawnTab::CreateSP(this, &FCustomAssetEditor::SpawnTab_CustomTab))
.SetDisplayName(LOCTEXT(&#34;Custom Tab&#34;, &#34;CustomAsset Custom Tab&#34;))
.SetGroup(WorkspaceMenuCategory.ToSharedRef());
}
void FCustomAssetEditor::UnregisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
{
FAssetEditorToolkit::UnregisterTabSpawners(InTabManager);
//Unregister tab ptr: Details View
InTabManager->UnregisterTabSpawner(CustomTabID);
}
void FCustomAssetEditor::InitCustomAssetEditor(const EToolkitMode::Type Mode,
const TSharedPtr<IToolkitHost>& InitToolkitHost, UCustomAsset* InCustomAsset)
{
CustomAsset = InCustomAsset;
//layout
const TSharedRef<FTabManager::FLayout> CustomAssetEditorLayout = FTabManager::NewLayout(&#34;Layout_CustomAsset&#34;)
->AddArea
(
FTabManager::NewPrimaryArea()
->SetOrientation(Orient_Vertical)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.1f)
->SetHideTabWell(true)
->AddTab(CustomTabID, ETabState::OpenedTab)
)
);
//Init
InitAssetEditor(Mode, InitToolkitHost, FName(&#34;CustomAssetEditorApp&#34;), CustomAssetEditorLayout, true, true, InCustomAsset);
}
TSharedRef<SDockTab> FCustomAssetEditor::SpawnTab_CustomTab(const FSpawnTabArgs& Args)
{
return
SNew(SDockTab)
[
//Add A New S Element
SNew(SButton)
.Text(FText::FromString(&#34;Custom Tab!&#34;))
];
}
#undef LOCTEXT_NAMESPACE
```
# 5. AssetEditor编辑器开启入口:
+ 实现Asset开启自定义编辑器,可通过继承AssetTypeAction类实现,并重载类中的OpenAssetEditor方法实现。
创建一个Asset类型后,如果没有自定义的编辑器,就会调用父类FAssetTypeAction的OpenAssetEditor方法,这个方法中创建了一个FSimpleAssetEditor类实例并调用其InitEditor方法,生成了一个带有DetailsView的简易编辑器窗口。
这里给出AssetTypeAction类的实现:
AssetTypeActions_CustomAsset.h
```cpp
#pragma once
#include &#34;CoreMinimal.h&#34;
#include &#34;AssetTypeActions_Base.h&#34;
/**
*
*/
/*AssetTypeActions class for the Motion Field so it can have a distinctive color and opens the Motion Field editor*/
class FAssetTypeActions_CustomAsset : public FAssetTypeActions_Base
{
//************* construct, override and interface
public:
FAssetTypeActions_CustomAsset();
// IAssetTypeActions interface
// 右键打开创建时的资产名称
virtual FText GetName() const override;
// 资产颜色
virtual FColor GetTypeColor() const override;
// 资产支持类
virtual UClass* GetSupportedClass() const override;
// 右键打开创建时的资产目录
virtual uint32 GetCategories() override;
// End of IAssetTypeActions interface
//************* end construct, override and interface
public:
// Entry Function while open editor if Asset Has Editor
virtual void OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor = TSharedPtr<IToolkitHost>()) override;
};
```
AssetTypeActions_CustomAsset.cpp
```cpp
#include &#34;CustomAssetTypeActions.h&#34;
//project
#include &#34;CustomEditor.h&#34;
#include &#34;CustomAssetEditor.h&#34;
#include &#34;Asset/CustomAsset.h&#34;
FAssetTypeActions_CustomAsset::FAssetTypeActions_CustomAsset()
{
}
FText FAssetTypeActions_CustomAsset::GetName() const
{
return INVTEXT(&#34;CustomAsset&#34;);
}
FColor FAssetTypeActions_CustomAsset::GetTypeColor() const
{
return FColor::Magenta;
}
UClass* FAssetTypeActions_CustomAsset::GetSupportedClass() const
{
return UCustomAsset::StaticClass();
}
uint32 FAssetTypeActions_CustomAsset::GetCategories()
{
//这里的Categories和注册type actions时调用的问题尚未解决
return EAssetTypeCategories::Misc;
}
// If Asset Has Editor
void FAssetTypeActions_CustomAsset::OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor)
{
const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone;
for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt)
{
if (UCustomAsset* CustomAsset = Cast<UCustomAsset>(*ObjIt))
{
TSharedRef<FCustomAssetEditor> NewCustomAssetEditor(new FCustomAssetEditor());
NewCustomAssetEditor->InitCustomAssetEditor(Mode, EditWithinLevelEditor, CustomAsset);
}
}
}
```
运行结果
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|