前言

  关于菜单搭建,我之前就写过一篇文章关于如何使用 Python 搭建 Unreal 主界面的菜单。 链接
  但是倘若要实现更为复杂的效果,比如在 Sequencer 的右键菜单上扩展,或者添加工具栏。
  要实现这样的功能 Python 就无能为力了。

  既然要用 C++ 实现,就必须要推荐一下官方提供的扩展教程。 B站链接 Youtube链接 github链接
  教程稍微有点老了,是 2015 年的,不过我把 Github 的代码拉下来,稍微修复一些编译的小问题,基本上是可用的。
  作为引擎扩展是绝佳的参考,另外最近逛论坛的时候还发现了一个不错 Python Editor 扩展插件。
  在 Unreal 里面实现 Maya 工具栏的效果,也是引擎扩展非常好的参考。 论坛地址 github地址

  不过 Python Editor 在 4.25 引擎下编译会出现大量错误,头文件的索引很多都不可用,还有一些代码的小问题。
  所以我把这些杂七杂八的错误全部修复了,参见我 Fork 的仓库。 github地址
  时间不足,有空要好好研究一下这个插件,做得非常好。


  这个需求和我上次研究的 Unreal 自定义快捷键拓展中诸多相似的地方,可以结合到一起看 链接

Sequencer 菜单扩展

  虚幻是开源的,所以即使再不济,也可以在源码上进行修改。
  但是改动引擎是非常不好的操作,说明架构设计上扩展性不足,堂堂虚幻引擎怎么会沦落到这种地步。
  所以在官方论坛上搜索一下,可以看到官方工作人员给出了一些使用提示 链接

  官方的回答有提到大部分 Slate 都预留了 FExtender 来进行菜单的扩展,不需要修改源代码。
  可以通过 C++ 插件来实现,并且插件实现可以迁移到不同项目的引擎,相比之下更友好。


  到这一步可以去查 Unreal 的 C++ API 文档,看看 Sequencer 的 API 有没有提供相关的方法。
  通常优先查 I 开头的类,因为这种类属于接口,将内部函数转换到外部使用的。

  所以可以在 ISequencerModule 里面找到目标。

alt

  这个函数获取到 FExtensibilityManager 基本上就和上次扩展快捷键一样。
  需要定制一个 UI_COMMAND 然后通过 AddExtender 将 UI_COMMAND 定义的命令添加进去。
  同时 FExtender 构建参数里面 FMenuExtensionDelegate 中定义菜单嵌入的生成代码。

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
35
36
37
38
39
40
41

// NOTE 获取 SequencerModule 以及 FExtensibilityManager
ISequencerModule &SequencerModule = FModuleManager::Get().LoadModuleChecked<ISequencerModule>("Sequencer");
TSharedPtr<FExtensibilityManager> Manager = SequencerModule.GetObjectBindingContextMenuExtensibilityManager();

struct Callback
{
static void ExportCurve()
{
// NOTE 点击触发执行的函数,这里我通常是定义调用特定路径的 Python 脚本
FString Script = "py \" xxx \"";
GEngine->Exec(NULL, Script.GetCharArray().GetData());
}

static void ExtendMenu(FMenuBuilder &MenuBuilder)
{
// NOTE 设置 Section
MenuBuilder.BeginSection("FXCurve", LOCTEXT("FXCurve", "FXCurve"));
{
// NOTE 设置点击菜单栏
MenuBuilder.AddMenuEntry(
LOCTEXT("ExportCurve", "Export Curve"),
LOCTEXT("ExportCurveTooltip", "Export Curve to FX Blueprint"),
FSlateIcon(),
// NOTE 设置点击触发的函数
FExecuteAction::CreateStatic(&ExportCurve)
);
}
MenuBuilder.EndSection();
}
};

// NOTE 获取 SequencerModule 以及 FExtensibilityManager
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);
MenuExtender->AddMenuExtension(
TEXT("Spawnable"),
EExtensionHook::Before,
TSharedPtr<FUICommandList>(),
FMenuExtensionDelegate::CreateStatic(&Callback::ExtendMenu));
Manager->AddExtender(MenuExtender);

  上面的代码可以在 Sequencer 的 binding 菜单上嵌入 Entry 。

alt

  嵌入的 Spawnable 位置是怎么填写的呢?
  其实看了官方的引擎扩展教程就可以知道怎么操作 ~
  需要在引擎的配置面板上开启显示。

alt

  需要在引擎的配置面板上勾选显示。
  然后重开引擎。(不重开 动态菜单也可以更新,不过 Toolbar 之类的就不行)

alt

  这样就可以看到菜单背后的代码标记了。
  方便定位菜单的嵌入。

工具栏嵌入

  其实有了菜单功能的嵌入,工具栏嵌入也是大同小异而已。
  不逼逼,直接上代码。

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
35
36
37
38
39
40
41
42
43
ISequencerModule &SequencerModule = FModuleManager::Get().LoadModuleChecked<ISequencerModule>("Sequencer");
TSharedPtr<FExtensibilityManager> Manager = SequencerModule.GetToolBarExtensibilityManager();

struct Callback
{
static void ExportCurve()
{
FString Script = "py \" xxx \"";
GEngine->Exec(NULL, Script.GetCharArray().GetData());
}

static void ExtendToolBar(FToolBarBuilder &ToolBarBuilder)
{

ToolBarBuilder.BeginSection("Keys");
{
ToolBarBuilder.AddToolBarButton(
FPyCommandList::Get().ExportSelectedKeys,
NAME_None,
TAttribute<FText>(),
TAttribute<FText>(),
FSlateIcon(FEditorStyle::GetStyleSetName(), "Matinee.ToggleCurveEditor")
);
}
ToolBarBuilder.EndSection();
ToolBarBuilder.AddSeparator();
}
};

TSharedRef<FUICommandList> CommandList = MakeShareable(new FUICommandList);
CommandList->MapAction(
FPyCommandList::Get().ExportSelectedKeys,
FExecuteAction::CreateStatic(&Callback::ExportCurve)
);

TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);
MenuExtender->AddToolBarExtension(
TEXT("Curve Editor"),
EExtensionHook::After,
CommandList,
FToolBarExtensionDelegate::CreateStatic(&Callback::ExtendToolBar));
Manager->AddExtender(MenuExtender);

alt

  Toolbar 多了一个操作,需要创建一个 UI_COMMAND
  然后通过 CommandList 来映射执行的回调函数。
  这里我直接用了 Unreal 内置的图标,具体的名称可以参考引擎源码 SlateEditorStyle.cpp 里面的定义。

总结

  可惜目前 Python 还没有暴露出相关的 UObject 类型来调用。
  所以没法用 Python 进行扩展,不过也可以参考 Sequencer Scripting 插件的做法,将 FClass 转成 UClass 进行调用。
  后续有空再进一步研究吧,c++的开发效率实在是太低了,而且我的基础极其不扎实,需要一些基础教程巩固一下(:з」∠)