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

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

UE4シーケンサーのメニューに項目を追加したい(2)

目標


UE4シーケンサーのエディター拡張を行い、メニューの項目を追加してシーケンサーに対して独自の処理を行うための入り口を作りたい。
※エンジン改造はしません。
今回はシーケンサーのトラックメニューに追加する方法になります。
具体的には以下の画像の箇所

f:id:naoxgames:20210508013755p:plain
トラックメニュー

前回の続きで対応するため、以下もご一読ください。
naoxgames.hatenablog.jp

環境

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

やってみる

作戦としては前回同様UI拡張点からコードを検索して、似た処理を真似てみます。
前回のプラグインに処理を追加して対応します。

UIの拡張点の表示

前回同様UIの拡張点の表示を行いましょう。
UE4エディターの[編集]→[エディタの環境設定]→[UIの拡張点の表示]にチェックを入れます。
該当箇所を確認すると

f:id:naoxgames:20210508015105p:plain
拡張点
「ObjectBindings」ということがわかりました。

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

「ObjectBindings」でエンジンのコードを検索すると、SSequencer.cppSSequencer::GetContextMenuContentで使われていることがわかります。
FSequencer::BuildAddObjectBindingsMenuを見ると、ObjectBindings[i]->BuildSequencerAddMenuでメニューの登録がされています。
トラックメニューに表示されるにはこのObjectBindingsに登録されている必要があるようです。

ObjectBindingsに登録する処理を探すとFSequencer::InitSequencerObjectBindings.Addされているのが確認できます。
EditorObjectBindingDelegatesの要素数分登録されているので、FSequencer::InitSequencerの引数のEditorObjectBindingDelegatesがどこでどう設定されているか確認します。

FSequencer::InitSequencerを検索するとFSequencerModuleのCreateSequencerで呼び出しています。
EditorObjectBindingDelegatesに登録する処理はFSequencerModuleのRegisterEditorObjectBindingで行っています。


っということで、必要な情報は揃いました。

モジュール設定

SequenceExtensionEd.Build.csに以下を追加します。

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


シーケンサーモジュールに拡張メニュー追加

トラックメニューに追加する処理を作成します。

ObjectBindingExtension.h

#include "CoreMinimal.h"

#include "ISequencerEditorObjectBinding.h"
/**
 * 
 */
class ISequencer;
class AActor;

class FObjectBindingExtension : public ISequencerEditorObjectBinding
{
public:

    FObjectBindingExtension(TSharedRef<ISequencer> InSequencer);

    static TSharedRef<ISequencerEditorObjectBinding> OnCreateActorBinding(TSharedRef<ISequencer> inSequencer);

    // ISequencerEditorObjectBinding interface
    virtual void BuildSequencerAddMenu(FMenuBuilder& MenuBuilder) override;
    virtual bool SupportsSequence(UMovieSceneSequence* InSequence) const override;

private:

    void AddObjectBindingMenuExtension();

private:
    TWeakPtr<ISequencer> Sequencer;
};



ObjectBindingExtension.cpp

#include "ObjectBindingExtension.h"

#include "Misc/Guid.h"
#include "EditorStyleSet.h"
#include "LevelSequence.h"

#define LOCTEXT_NAMESPACE "FObjectBindingExtension"

FObjectBindingExtension::FObjectBindingExtension(TSharedRef<ISequencer> InSequencer)
    : Sequencer(InSequencer)
{ }

TSharedRef<ISequencerEditorObjectBinding> FObjectBindingExtension::OnCreateActorBinding(TSharedRef<ISequencer> inSequencer)
{
    return MakeShareable(new FObjectBindingExtension(inSequencer));
}

void FObjectBindingExtension::BuildSequencerAddMenu(FMenuBuilder& MenuBuilder)
{
    MenuBuilder.AddMenuEntry(
        LOCTEXT("ObjectBindingEx", "ObjectBindingEx"),
        LOCTEXT("ObjectBindingToolTip", "ObjectBindingToolTip"),
        FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.ColumnViewAssetIcon"),
        FUIAction(FExecuteAction::CreateRaw(this, &FObjectBindingExtension::AddObjectBindingMenuExtension)));
}

bool FObjectBindingExtension::SupportsSequence(UMovieSceneSequence* InSequence) const
{
    return InSequence->GetClass() == ULevelSequence::StaticClass();
}

void FObjectBindingExtension::AddObjectBindingMenuExtension()
{
    FText DialogText = FText::FromString("ObjectBindingMenuExtension");
    FMessageDialog::Open(EAppMsgType::Ok, DialogText);
}

#undef LOCTEXT_NAMESPACE



SequencerExtensionEd.h

~
private:
~
    FDelegateHandle ObjectBindingDelegateHandle;
~



SequencerExtensionEd.cpp

~
#include "ObjectBindingExtension.h"
~
void FSequencerExtensionEdModule::StartupModule()
{
~
    ObjectBindingDelegateHandle = SequencerModule.RegisterEditorObjectBinding(FOnCreateEditorObjectBinding::CreateStatic(&FObjectBindingExtension::OnCreateActorBinding));
~
}
~
void FSequencerExtensionEdModule::ShutdownModule()
{
~
        SequencerModule.UnRegisterEditorObjectBinding(ObjectBindingDelegateHandle);
~
}



実行!!!!

f:id:naoxgames:20210508031343p:plain
トラックメニュー
f:id:naoxgames:20210508031502p:plain
メニュー押したとき

シーケンサーのトラックメニューに項目が指定した名前で追加され、ダイアログが表示されました。


思ったこと

前回とは違った追加の仕方で、メニューごとに違うとやりにくいですね。
以前に書いた記事で、ISequencerを取得する対応を紹介しています。
naoxgames.hatenablog.jp これと組み合わせることでメニューからできることの幅が広がると思います。


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

この記事の続き naoxgames.hatenablog.jp

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