跳转至

UE 编辑器插件 UE.EditorPlus 说明文档

介绍视频

插件源码

UE.EditorPlus

商城下载

EditorPlus

项目添加源码插件 EU.EditorPlus

参考文档:

插件说明

UE.EditorPlus 是一个 UE 编辑器插件,提供了一种方便的方式来扩展编辑器菜单,并支持高级方式来扩展,同时包含了一些实用的编辑器工具。本插件支持 UE5.3+。

扩展编辑器菜单

说明

支持多种方式扩展编辑器菜单:

  • 路径方式:RegisterPathAction("/<MenuBar>Bar/<SubMenu>SubMenu/<Command>Action")
  • 实例化方式:EP_NEW_MENU(FEditorPlusMenuBar)("Bar")
  • 混合方式:RegisterPath("/<MenuBar>Bar/<SubMenu>SubMenu/<Command>Action",EP_NEW_MENU(FEditorPlusCommand)("Action")

路径方式

可以通过这样的方式来注册一个编辑器菜单指令:

FEditorPlusPath::RegisterPathAction(
    "/<MenuBar>Bar/<SubMenu>SubMenu/<Command>Action",
    FExecuteAction::CreateLambda([]
        {
            // do action
        })
);

这样就可以在编辑器菜单栏 Help 后面增加一个菜单栏 Bar,Bar 里面增加一个子菜单 SubMenu, SubMenu 里面增加一个命令 Action。

完整的路径格式会是这样的:/<Hook>HookName/<Type1>Name1/<Type2>Name2,第一个路径必须是 <Hook>,目前支持的类型和限制:

  • <Hook>:表示需要在哪个 Hook 的位置上生成菜单,后续路径不能有 <Hook>
  • <MenuBar>:菜单栏,后面路径不能有 <Hook>, <MenuBar>, <ToolBar>
  • <ToolBar>: 工具栏,后面路径不能有 <Hook>, <MenuBar>, <ToolBar>
  • <Section>:菜单分节,后面路径不能有 <Hook>, <MenuBar>, <Section>
  • <Separator>:菜单分隔符,后面路径不能有 <Hook>, <MenuBar>
  • <SubMenu>:子菜单,后面路径不能有 <Hook>, <MenuBar>
  • <Command>:菜单命令,后面不能有任何路径
  • <Widget>:更多可扩展定制的 Slate UI 组件,后面不能有任何路径

更简易的路径形式:/BarName/SubMenuName1/SubMenuName2/CommandName,如果不指定类型,默认路径的第一个是 <MenuBar>,中间的是 <SubMenu>,最后的是 <Command>

如果没有指定 <Hook> 则自动最前面加上 <Hook>Help,表示在 Help 菜单后面添加菜单栏。

实例化方式

路径方式是自动把所有节点根据类型和默认参数实例化出来,我们也可以自己控制实例化,可以更细致控制扩展的内容。

EP_NEW_MENU(FEditorPlusMenuBar)("MyBar", "MyBar", LOCTEXT("MyBar", "MyBar"), LOCTEXT("MyBarTips", "MyBarTips"))
->RegisterPath()
->Content({
    EP_NEW_MENU(FEditorPlusSubMenu)("MySubMenu")
    ->Content({
        EP_NEW_MENU(FEditorPlusCommand)("MyAction")
        ->BindAction(FExecuteAction::CreateLambda([]
            {
                // do action
            })),
    })
});

实例化 MyBar 的时候可以传入 Hook 名字,本地化名字,本地化提示参数("MyBar", LOCTEXT("MyBar", "MyBar"), LOCTEXT("MyBarTips", "MyBarTips"))。上面代码就相当于路径方式 /<Hook>Help/<MenuBar>MyBar/<SubMenu>MySubMenu/<Command>MyAction

混合方式

当然还可以两种方式混合使用:

FEditorPlusPath::RegisterPath(
    "/<MenuBar>Bar/<SubMenu>SubMenu/<Command>Action",
    EP_NEW_MENU(FEditorPlusCommand)("Action")
    ->BindAction(FExecuteAction::CreateLambda([]
        {
            // do action
        })),
);

这种情况下,插件会自动实例化中间路径的节点,最后的路径使用用户自己实例化的节点。

更多用例

头文件:

#include <EditorPlusPath.h>

路径方式指定本地化语言,EP_FNAME_HOOK_AUTO 表示自动使用路径名字作为 Hook 名字:

FEditorPlusPath::RegisterPathAction(
        "/Bar/Action",
        FExecuteAction::CreateLambda([]
        {
            // do action
        }),
        EP_FNAME_HOOK_AUTO,
        LOCTEXT("Action", "Action"),
        LOCTEXT("ActionTips", "ActionTips"));

通过路径获取节点并设置本地化文本:

FEditorPlusPath::GetNodeByPath("/MenuTest")
    ->SetFriendlyName(LOCTEXT("MenuTest", "MenuTest"))
    ->SetFriendlyTips(LOCTEXT("MenuTestTips", "MenuTestTips"));

路径末端添加一个 Slate UI 控件

FEditorPlusPath::RegisterPath(
    "/<MenuBar>Bar/<SubMenu>SubMenu/<Widget>Widget",
    EP_NEW_MENU(FEditorPlusWidget)("Widget", LOCTEXT("Widget", "Widget"))
        ->BindWidget(SNew(SHorizontalBox)));
);

在 UE 自带的 Hook 里面添加新的节点

FEditorPlusPath::RegisterPath("<Hook>EpicGamesHelp/<Separator>ExtendSeparator")

多次声明相同的路径,都被识别成同一个路径,因此可以不断扩展相同的路径

FEditorPlusPath::RegisterPathAction("/MenuTest/SubMenu1/SubMenu1/Path1", Action, EP_FNAME_HOOK_AUTO, LOCTEXT("Path1", "Path1"), LOCTEXT("Path1Tips", "Path1Tips"));
FEditorPlusPath::RegisterPathAction("/MenuTest/SubMenu1/SubMenu1/Path2", Action, EP_FNAME_HOOK_AUTO, LOCTEXT("Path2", "Path2"), LOCTEXT("Path2Tips", "Path2Tips"));

为一个节点继续扩展路径

auto node = FEditorPlusPath::GetNodeByPath("/MenuTest");
FEditorPlusPath::RegisterChildPath(node, "<SubMenu>Sub/<Separator>Sep");

删除一个路径

FEditorPlusPath::UnregisterPath("/MenuTest/SubMenu1/SubMenu1/Path1");

扩展工具栏

FEditorPlusPath::RegisterPath("/<Hook>ProjectSettings/<ToolBar>MenuTestToolBar")
->Content({
    EP_NEW_MENU(FEditorPlusCommand)("ToolBarCommand1")
    ->BindAction(...)
});

接口说明

class EDITORPLUS_API FEditorPlusPath
{
public:

    static TSharedPtr<FEditorPlusMenuBase> RegisterPath(const FString& Path, const TSharedPtr<FEditorPlusMenuBase>& Menu=nullptr);
    static TSharedPtr<FEditorPlusMenuBase> RegisterPath(const FString& Path, const FText& FriendlyName, const FText& FriendlyTips);
    static TSharedPtr<FEditorPlusMenuBase> RegisterPathAction(
        const FString& Path, const FExecuteAction& ExecuteAction, const FName& Hook=EP_FNAME_HOOK_AUTO,
        const FText& FriendlyName=FText::GetEmpty(), const FText& FriendlyTips=FText::GetEmpty());

    static TSharedPtr<FEditorPlusMenuBase> RegisterChildPath(
        const TSharedRef<FEditorPlusMenuBase>& InParent, const FString& Path, const TSharedPtr<FEditorPlusMenuBase>& Menu=nullptr);
    static TSharedPtr<FEditorPlusMenuBase> RegisterChildPath(
        const TSharedRef<FEditorPlusMenuBase>& InParent, const FString& Path, const FText& FriendlyName, const FText& FriendlyTips);
    static TSharedPtr<FEditorPlusMenuBase> RegisterChildPathAction(
        const TSharedRef<FEditorPlusMenuBase>& InParent, const FString& Path, const FExecuteAction& ExecuteAction,
        const FName& Hook=EP_FNAME_HOOK_AUTO, const FText& FriendlyName=FText::GetEmpty(), const FText& FriendlyTips=FText::GetEmpty());

    static bool UnregisterPath(
        const FString& Path, const TSharedPtr<FEditorPlusMenuBase>& Leaf=nullptr);

    static TSharedPtr<FEditorPlusMenuBase> GetNodeByPath(const FString& Path);
};
  • RegisterPath:生成路径菜单
  • RegisterPathAction:生成路径菜单,并自动为末端 <Command> 节点绑定操作
  • RegisterChildPath:为指定节点继续生成子路径
  • RegisterChildPathAction:为指定节点继续生成子路径,并自动绑定操作
  • UnregisterPath:删除路径,Leaf 在有多个同名的末端节点可以指定严格匹配。删除的过程中,会回溯中间节点,一旦中间节点没有任何子节点也会被删除
  • GetNodeByPath:根据路径获取节点

节点类型

// base class of all node
class EDITORPLUS_API FEditorPlusMenuBase: public TSharedFromThis<FEditorPlusMenuBase> {}

class EDITORPLUS_API FEditorPlusHook: public TEditorPlusMenuBaseRoot {}

class EDITORPLUS_API FEditorPlusMenuBar: public TEditorPlusMenuBaseNode {}

class EDITORPLUS_API FEditorPlusToolBar: public TEditorPlusMenuBaseNode {}

class EDITORPLUS_API FEditorPlusSection: public TEditorPlusMenuBaseNode {}

class EDITORPLUS_API FEditorPlusSeparator: public TEditorPlusMenuBaseNode{}

class EDITORPLUS_API FEditorPlusSubMenu: public TEditorPlusMenuBaseNode {}

class EDITORPLUS_API FEditorPlusCommand: public TEditorPlusMenuBaseLeaf {}

class EDITORPLUS_API FEditorPlusWidget: public TEditorPlusMenuBaseLeaf {}

更多样例和接口说明请参考源码 UE.EditorPlus,测试用例 MenuTest.cpp

模块化管理

UE.EditorPlus 还提供了一个模块化管理扩展菜单的框架,支持插件加载和卸载的时候,自动加载和卸载扩展的菜单

让菜单类继承 IEditorPlusToolInterface,并覆写 OnStartupOnShutdown 函数。OnStartup 负责创建菜单,OnShutdown 负责调用节点的 Destroy 函数清理菜单。单节点的引用数归0,则会执行自动清理。

class FMenuTest: public IEditorPlusToolInterface
{
public:
    virtual void OnStartup() override;
    virtual void OnShutdown() override;
}

void FMenuTest::OnStartup()
{
    BuildPathMenu();
    BuildCustomMenu();
    BuildMixMenu();
    BuildExtendMenu();
}

void FMenuTest::OnShutdown()
{
    for(auto Menu: Menus)
    {
        if(Menu.IsValid()) Menu->Destroy();
    }
    Menus.Empty();
}

菜单管理类继承 IEditorPlusToolManagerInterface,并覆写 AddTools 函数,在 AddTools 里面添加菜单类

class FEditorPlusToolsImpl: public IEditorPlusToolManagerInterface
{
public:
    virtual void AddTools() override;
}

void FEditorPlusToolsImpl::AddTools()
{
    if (!Tools.Num())
    {
        Tools.Emplace(MakeShared<FMenuTest>());
    }

}

插件加载和卸载的时候分别调用管理类的 StartupToolsShutdownTools 函数

void FEditorPlusToolsModule::StartupModule()
{
    Impl = FEditorPlusToolsImpl::Get();
    Impl->StartupTools();

}
void FEditorPlusToolsModule::ShutdownModule()
{
    Impl->ShutdownTools();
}

完成以上适配,则可以自动在加载和卸载插件的时候,自动加载和卸载扩展的菜单。

编辑器工具

UE.EditorPlus 还提供了一些实用的编辑器工具

创建编辑器窗口

使用 EditorPlus,可以很简单的创建一个新的编辑器窗口

// register spawn tab
Tab = MakeShared<FEditorPlusTab>(LOCTEXT("ClassBrowser", "ClassBrowser"), LOCTEXT("ClassBrowserTip", "Open the ClassBrowser"));
Tab->Register<SClassBrowserTab>();

// register menu action to spawn tab
FEditorPlusPath::RegisterPathAction(
    "/EditorPlusTools/ClassBrowser",
    FExecuteAction::CreateSP(Tab.ToSharedRef(), &FEditorPlusTab::TryInvokeTab),
);

SClassBrowserTab 是一个自定义的 UI 控件

class SClassBrowserTab final : public SCompoundWidget
{
    SLATE_BEGIN_ARGS(SClassBrowserTab)
    {}
    SLATE_END_ARGS()
    // ...
}

ClassBrowser

ClassBrowser 是一个 UE Class 查看器,通过菜单 EditorPlusTools -> ClassBrowser 来打开

基于 UE 的反射来实现,可以很方便查看 UE 各种类型的成员信息,说明提示等,支持模糊搜索,并能跳转打开父类的信息。

MenuCollections 是一个菜单命令快速查找和收藏工具,能够帮助你快速找到需要执行的菜单命令,并可以收藏常用命令,提升效率。

SlateResourceBrowser

SlateResourceBrowser 是一个可以快速查看 Slate UI 资源的工具,能够帮助你浏览和查找需要的编辑器资源,方便扩展编辑器。

原文地址:https://wiki.disenone.site

本篇文章受 CC BY-NC-SA 4.0 协议保护,转载请注明出处。