エンジニアっぽくなりたい

UnityやUnrealEngine 4などでなにか役に立ちそうなことを発信していく。

UE4シーケンサーのメニューに項目を追加したい(4)~Level Sequence Asset の Context Menu~

シーケンサーとはちょっと離れるかもしれませんが、今回はLevel Sequence のAssetの右クリックメニューに項目を追加しようとおもいます。

目標


Level Sequence のAssetの右クリックメニューに項目を追加する。
具体的には
f:id:naoxgames:20211231214015p:plain
このあたりに追加する
※エンジン改造はしません。

前回の続きで対応するため、以下もご一読ください。
naoxgames.hatenablog.jp
naoxgames.hatenablog.jp
naoxgames.hatenablog.jp
一連の対応を上げているGitリポジトリです。
この記事のコードはすべてこのリポジトリに追加で進めています。
github.com

環境

・UE 4.26.1 (エンジンビルドはしていません)
・VisualStudio 2019 16.11.2

やってみる

いつも通りエディターの拡張点などからエンジンの類似処理を見つけて真似てみる作戦で行きます。

エンジンから類似処理を探す

LevelSequenceのアセットをコンテンツブラウザから右クリックしてみると以下のようになります。
f:id:naoxgames:20211231221040p:plain
「GetAssetActions」がなにかできそうなので、これを調べてみます。
その結果「FTakeRecorderModule::RegisterMenus()」と「FSequencerModule::RegisterMenus()」を見ると参考になりそうな処理が書いてありました。
これを元に対応していきます。

拡張メニュー追加

拡張メニュー追加の処理を実装していきます。
以下はアセットを右クリック時に「LevelSequenceContextMenuExtension」という項目を表示し、選択するとダイアログが出るようにしています。

SequencerExtensionEd.Build.cs

~
        PublicDependencyModuleNames.AddRange(
            new string[]
            {
                ~
                "ContentBrowser",
                ~
            }
            );
~


SequenceExtensionEd.cpp

~
//AssetContextMenu
#include "ContentBrowserMenuContexts.h"
~
void FSequencerExtensionEdModule::RegisterMenus()
{
~
    {
        //右クリックメニュー
        FToolMenuOwnerScoped ContextMenuOwner("LevelSequenceContextMenuExtension");
        UToolMenus* ToolMenus = UToolMenus::Get();
        UToolMenu* AssetContextMenu = ToolMenus->ExtendMenu("ContentBrowser.AssetContextMenu.LevelSequence");
        if (!AssetContextMenu)
        {
            return;
        }

        FToolMenuSection& Section = AssetContextMenu->FindOrAddSection("GetAssetActions");
        Section.AddDynamicEntry("LevelSequenceContextMenuExtension", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection)
            {
                UContentBrowserAssetContextMenuContext* Context = InSection.FindContext<UContentBrowserAssetContextMenuContext>();
                if (!Context)
                {
                    return;
                }
                //一つのアセットを選択中のみ有効
                ULevelSequence* LevelSequence = Context->SelectedObjects.Num() == 1 ? Cast<ULevelSequence>(Context->SelectedObjects[0]) : nullptr;
                if (LevelSequence)
                {
                    InSection.AddMenuEntry(
                        "LevelSequenceContextMenuExtension",
                        FText::FromString(TEXT("LevelSequenceContextMenuExtension")),
                        FText::FromString(TEXT("LevelSequenceContextMenuExtension")),
                        FSlateIcon(),
                        FExecuteAction::CreateLambda(
                            [this, LevelSequence]
                            {
                                FText DialogText = FText::FromString("LevelSequenceContextMenuExtension");
                                FMessageDialog::Open(EAppMsgType::Ok, DialogText);
                            }));
                }
            }));
    }
~
}
~


これで準備は完了です。

実行!!!!

f:id:naoxgames:20211231222810p:plain
f:id:naoxgames:20211231222826p:plain
という感じでLevelSequenceのアセットの右クリックメニューに項目を追加することができました。

FAssetTypeActions_Baseを使った実装について

アセットのメニューについて調べてみると、どうやら「FAssetTypeActions_Base」というクラスを継承して、「GetSupportedClass」で指定したクラスのアセットのメニューが設定できるようです。
ですが今回はこの方法を使いませんでした。
理由としては、過去に「FAssetTypeActions_Base」を使った実装を「Skeleton」のアセットに対して行ったことがあるのですが、その際「Skeleton」に対するAssetTypeActionsクラスがすでにエンジン側に存在しており、こちらが作ったものが優先されてしまう状況になりました。
つまり元の機能が使えなくなってしまったのです。
何とかする方法があるのかもしれませんが、結局解決できておりません。

その時に今回の方法を使った場合、問題なく対応ができます。
※Skeletonの場合は「ExtendMenu("ContentBrowser.AssetContextMenu.Skeleton")」と指定する。
そのため、今回は「FAssetTypeActions_Base」を使わない実装を行いました。


思ったこと

この機能を使えば、「LevelSequence」に対する特定の処理を選択したアセットにのみ行うことができて、多分便利!
基本的になにか処理を加えたいときはすべての「LevelSequence」一括で行う気がするので微妙かもしれませんが。。
シーケンサーを何かの形式でエクスポートする処理とか、逆にインポートするときとか、そんなことをするときにかつて仕事で使ったことがあります。


ここまでの内容は以下のリポジトリに上げてあります。 github.com

正しい対応方法やより良い方法があればご指摘お願いいたします!!!