※この記事で使用しているUnrealのVersionは04.27.2及び05.0.0EarlyAccessです。
(UE4とUE5で違う点なども記載してるので、サンプルプロジェクトは2つ用意してあります。
記事のキャプチャは基本的にUE4.27の方で説明いたします)

※この記事のサンプルプロジェクトは以下URLにアップされています。

UE4:サンプルプロジェクト
UE5:サンプルプロジェクト

※この記事はUnreal Engine (UE) Advent Calendar 2021の17日目の記事になります。
昨日はナナさんの「BlenderのAutoRigを使ってUE4に持っていって色々するアニメーション周りの話」でした!Blender×Unrealはいろんな事ができてすごい!!

前置き

みなさんお久しぶりです!
まだしばらくは海底に潜る予定のアジです!

今回は、以前にご紹介した↓の「【UE4】Blueprintだけで、Editor上にメニューを追加する【★★★」について、以降のバージョンアップで追加された機能紹介や説明しなかった機能の紹介をしていきたいと思います。

これであなたのプロジェクトにも、自分だけのエディター拡張を手軽に行えちゃいます!
少々難しい部分もあるかと思いますが、是非前半だけでも読んで快適なUnrealEditorLifeを構築してください!(最後におまけもあるよ!)

※Slateやらのお話は一切しませんので、ご了承ください(一切知りません)
もしFMenuBuilderやらでのメニュー追加方法やSDockTabやらでの拡張方法などについて知りたい場合は、
ヒストリア様の[UE4] プラグインによるエディタ拡張(2) エディタのメニューに項目を追加するとか
Naotsun様のUnrealC++でレベルエディタに独自メニューを追加する独自のタブをエディタのメニューから呼び出す方法(2021年13日目のアドカレ)等がとても参考になるかと思います(楽しく読ませていただきました)

※また読むにあたって、Blueprintをある程度自分で書けるような知識が必要になります。わからなければTwitter等で質問していただいて構いませんので、@kinnaji_blogまでご連絡ください。
※このブログは基本的にEditorの言語を英語にしてキャプチャー等を撮っています(海外の方でもわかるようにだったり、アジは英語版を基本的に使用しているので)。予めご了承ください。

※尚、UE5の方はEarlyAccess ということで正式版リリース時に仕様が変更になる場合もございますので予めご了承ください。

久々に書いたのでおかしいところもあるかもなのと、なるべく細かな部分まで説明をしたせいか、かなり長い記事になってしまいましたが最後までお付き合いいただければと思います!

その1:Asset,Actorの右クリックメニュー レベル【★★】

以前の記事には書いてなかったのですが、割と前からあるクラスで、
ActorActionUtility (UActorActionUtility)」と「AssetActionUtility(UAssetActionUtility)」というクラスがあります。

この2つのクラスはそれぞれアクター・アセットを右クリック時に表示されるメニューに、Blueprintで書いた独自の処理のボタンを自由に追加することができちゃうとっても便利なクラスとなります。

公式ドキュメント:スクリプト アクション

早速試してみましょう!!

~まずは作ってみる~

まず、ContentBrowser左上にある緑色の「Add/Import」をクリックし、出てきたメニューから「Editor Utilities」→「Editor Utility Blueprint」を選択します。

すると、クラスを選択するウィンドウが表示されるかと思いますので、まずは「ActorActionUtility」を選択して右下緑色の「Select」をクリックしましょう。

すると、新しくEditorUtilityBlueprintとして「ActorActionUtility」のブループリントが作成されます。

適当に名前をつけたら、ダブルクリックをしてGraphEditorを開きましょう。
すると、以下のようなウィンドウが開くかと思います。

さて、ここまでできたらあとは簡単!
新しく関数を作成するだけで、自動的に右クリックメニューに登録されます!

「MyBlueprint」タブ内の「Function」カテゴリにある「+Function」というボタンをクリックしてみましょう。
すると、「NewFunction_0」という新しい関数が作成されるかと思います。

あとは、左上の方にあるオレンジの?マークで「Compile」という風になっているボタンをクリックして、緑色のチェックマークの「Compile」というボタンに変化すれば準備完了です!

一旦Blueprintを閉じて、Viewport内の適当なアクターを右クリックしてみましょう。
すると、「Scripted Actions」という項目が新たに作成されているかと思います。

そこにカーソルを合わせると、右側に先ほど作成した「NewFunction_0」というメニューが作られているかと思います。これで独自のメニュー完成です!早い!

このボタンを押すことで、先程作成した関数が実行されます。

試しに先程の関数を以下の用に編集してみます。

↓実行結果(動画です)

このような感じでサクッと作れちゃいました!
同じ要領で、「AssetActionUtility」を継承してBlueprintを作成し関数を作成すれば、アセットの右クリックメニューにも同じ用に独自メニューを追加できちゃいます。

~機能解説~

さて、もう少し詳しい説明をしていきます。
アクター・アセットを右クリックしたときに、それぞれのActionUtilityBPクラスで定義した関数一覧を取得して表示しています。
その際、仮想関数の「GetSupportedClass」で右クリックしたアクター・アセットが特定のクラスかどうかを判別して、メニューに表示するかしないかを制御することができます。

試しに、「StaticMeshActor」アクタークラスのみ右クリック時にメニューを表示するようにしてみます。
BlueprintEditor上の「MyBlueprint」タブ内「Function」カテゴリの「+」にカーソルを合わせると「Override」という項目が出てくるので、それをクリックします。
その後「GetSupportedClass」を選択します。

すると、以下のような関数が作成されます。

真ん中の「Parent:GetSupportedClass」ノードを削除して、ReturnValueとなっている紫色のピンのプルダウンを選択し、「StaticMeshActor」を選択します。

そして、先程と同じように「Compile」ボタンを押して、BlueprintEditorを閉じましょう。
うまく行っていれば、以下の動画の用にStaticMeshActorを右クリックしたときにメニューが表示されるようになるかと思います。

ちなみに、「AssetActionUtility」の方には、「IsActionForBlueprints」という仮想関数があります。
これはTrueを返すことで、右クリックしたアセット”の親”のクラスが「GetSupportedClass」で選択したクラスかその子クラスである場合にメニューを表示するようになります。

これは、ContentBrowser上ではBlueprintは作成したクラスとして扱われず、「Blueprint」「WidgetBlueprint」などのアセットのクラスを参照してしまうため、その回避策としてこのような補助関数が用意されています。
(が、親クラスを指定なので、直接クラスを指定できないのが若干使いづらいです…)

ので、少し裏技的ですが以下みたいにすると正確に指定したBPClassかどうかを判定できたりします。
(まああまりいいやりかたじゃないかもですね)

あと、クラス内で作った関数を明示的にメニューに表示したくない場合とかあると思います。

メニューに追加される関数は、必ず「CallInEditor」という項目にチェックが付いています。
なので、この「CallInEditor」のチェックを外してあげることで、メニューに登録する候補から外すことができます。
(デフォでこのチェックがついてます)

ちなみに、関数がメニューとして表示されると紹介しましたが、「CustomEvent」もメニューに表示できます。

CustomEventの詳細で「CallInEditor」にチェックをつけるだけです。

あともう一つ、ActionUtility系のメニューにはとても便利な機能があります。

それは、メニュー用に作成した関数(CustomEventでも)に引数を追加することで、メニューから実行した際に、その引数を拡張ウィンドウとして出現させ、値を直接編集することができるというものです。

試しにActorActionUtilityに以下のような関数を追加します。

これを実行してみます。

拡張ウィンドウ

引数付き関数を実行すると、拡張ウィンドウが表示され、引数に対応したプロパティを入力できる項目が表示され、そこに値を入力してOKを押すことで、入力した値を引数に渡した状態で関数を実行してくれます。

尚、拡張ウィンドウが出ている間は裏にあるUnrealEditorは使えないので、値を入力し忘れたりすることなくメニュー機能を実行できるので、拡張メニューを作る際はかなり便利です。
是非ご活用してみてください!

ちなみに、表示されているメニューを「Shift」キーを押しながらクリックをすると、処理が書いてあるBlueprintをBlueprintEditorで表示してくれたりもします。
↓Shift押しながらクリックの動画

ちなみに、UE5では関数やCustomEventに設定した「Category」ごとに階層分けしてくれるため、メニューが多くなっても見やすくなるように改善されています(UE4使ってて丁度ほしいと思っていたところです!)

~覚えておくと便利な関数~

さて、この「ActorActionUtility」と「AssetActionUtility」を使う上で覚えておいてほしい関数がいくつかあります。

ActorActionUtilityでよく使うのが、「Get Selected Actors」という関数です。
AssetActionUtilityでよく使うのが、「Get Selected Assets」「Get Selected Asset Data」「Get Selected Blueprint Classes」という関数です。

上記関数は、どれも選択しているアクター・アセットをBlueprint上で取得するための関数で、これを用いることで選択しているものに対して様々な処理を行うことができます。

ついでに、「Editor Scripting Utilities」プラグインを有効にしていると、Blueprint側からアクター・アセットの選択状態を編集することもできます!

以下の「Set Actor Selection State」「Set Selected Level Actors」でアクターを、「Sync Browser To Objects」でアセットを選択状態にできます。

是非覚えてくださいね!

ActionUtility系の紹介は以上になります。
軽く紹介するつもりが、既に結構なボリュームになってしまいました。個人的にはここからが本番です…。

その2:もっと拡張された独自メニューを レベル【★★★】

~ActionUtility系のメニューではできないこと~

さて、初級編はアクター・アセットの右クリックメニューを独自作成する方法でしたが、初級編だけでも割と有用だとは思います。

しかし、それ以外にもメニューを追加したい場所ってありますよね!
まあでもメニュー追加ってC++必要なんでしょ…と思うでしょうが、そんな事はありません!

【UE4】Blueprintだけで、Editor上にメニューを追加する【★★★」でも紹介しましたが、最近のUnrealではBlueprintでメニューを追加する方法を提供してくれています。

基礎となるメニュー追加方法については上記記事を参照してみてください。

ここでは最近新たに追加されたBlueprint用機能と、上記記事で説明しなかった場所へのメニューの登録をやってみようかと思います。

UE4.26にて、EditorUtility系のクラスとして新たに「EditorUtilityToolMenuEntry(UEditorUtilityToolMenuEntry)」と「EditorUtilityToolMenuSection(UEditorUtilityToolMenuSection)」という2つのクラスが追加されています。

これら2つのクラスは、ToolMenuクラスにてメニューを使っていく上でとても有効的なクラスです。

というのも、その1で説明した「ActorActionUtility」と「AssetActionUtility」では、アクター・アセットの右クリックメニューにしか追加できないということ以外にも、できないことがあるのです。

それは、「インスタンスを保持し続ける事ができない」という部分です。

どういうことかと言うと、ActionUtility系のメニューは、実行される度に新しいObjectが作成され、そのObjectで処理が実行されます。

↓「GetPathName」を用いて実行されている関数が所属するオブジェクトのパスを確認する動画

ご覧いただければわかりますが、実行されるたびに自身のPathNameをPrintしていますが、毎回パスが変わっているのがわかります。

これは、メニューが実行されるタイミングで毎回インスタンスが生成されているからです。

Q.実行するたびに新しいインスタンスが作られると何ができないの?

と思うかもしれないですが、毎回実行環境が新しくなってしまうということは、それまでに実行した結果等を保持しておく場所がないので、なにか記憶して置かなければならない場合はどこかにその値を保持しておく必要があるのです。
↓例

プレイ中であれば「GameInstance」というクラスが、常に1つ存在しレベルを移動しようがその中に保持している値は初期化されることなく保持し続けてくれます。

しかし、エディター上のメニューなので、保持する場所はエディター上のどこかでなければなりません。

もしレベル内のアクターに保持したりした場合は、別のレベルをロードした際にアクターの情報は破棄されてしまいます。
また、何かしらのEventDispatcherをBindしたりする場合なども、インスタンスが常に存在してくれていたほうが何かと都合がいいです。
(まあC++でEditorSubsystemを定義すれば、独自にEditor上に常駐させられるクラスを作成できるのですがね…)

そこで登場するのが「EditorUtilityToolMenuEntry」と「EditorUtilityToolMenuSection」です。

この2つのクラスは、メニューを管理する「ToolMenu(UToolMenu)」というクラス内で保持させておくことができ、一度もたせると、Editorを閉じるまで常駐してくれるインスタンスとなってくれます。

早速使って見たいと思いますが、その前に軽くUnrealEditor上のメニューの設計について説明します。

~EditorMenuの大雑把な設計説明~

Unreal上の大抵のメニュー(細かく言うと、アクセスできるようにUObjectを使って管理しているメニュー)は、以下のような構造になっています。
※Slate側の説明は省きます。また、厳密な説明ではなくわかりやすさ重視にしています。

基本的に上記のような階層になっています。
「ToolMenus」「ToolMenu」はオブジェクト(UObject)で管理目的のオブジェクトのようなので、比較的簡単にアクセスが可能です。
「Section」や「Menu」はそれぞれ構造体になっており、設定項目も多くなっています。
そして、一番下の「Menu」部分がメニュー表示するための構造体で、この部分の情報を登録することで独自のMenuとして表示・使用する事ができます。
以前書いた【UE4】Blueprintだけで、Editor上にメニューを追加する【★★★】では、登録時に構造体を直接渡す「Add Menu Entry」を紹介しました。

今回は、BlueprintからMenuを登録できるもう一つの方法として「Add Menu Entry Object」という関数を使って見たいと思います。

~Toolbarにメニューを追加する~

テストするにあたって、今回は「Toolbar」タブの中にメニューを作ってみたいと思います。

まずは「EditorUtilityToolMenuEntry」を継承したEditorUtilityBlueprintを作成していきます。

ContentBrowser左上緑のボタンの「Add/Import」から「Editor Utilities」→「Editor Utility Blueprint」を選択します。

出てきたウィンドウから、「EditorUtilityToolMenuEntry」を選択して「Select」を押します。

作成したらBlueprintを開き、まずはClassDefaultを選択してプロパティを見ていきます。

基本的にはこのプロパティを編集することで、任意の場所にメニューを追加することができます。
プロパティのそれぞれの説明は以下になります。

ひとまずざっと解説していますが、いきなりだと何が何だか分からないかと思います。
なので、ひとまず重要な「Menu」「Label」「EntryType」の3箇所を変更していただければ大丈夫です。

Menu:LevelEditor.LevelEditorToolBar
Label:アジ用のボタン(好きな文字でOK)
EntryType:ToolBarButton

※メニューを追加する部分の名前(Menu)を調べる方法や紹介は少し下の方で軽くは説明していますが、詳しくは【UE4】Blueprintだけで、Editor上にメニューを追加する【★★★にありますので御覧ください。

ひとまずここまで準備できたら一旦このBlueprintはおいておき、今度はメニューを追加するためのEditorUtilityBlueprintを作成します。
特に目立った機能は必要ないので、「EditorUtilityObject」を継承して作成します。

作成できたらBlueprintを書いていきます。

ひとまず処理がかけたらBlueprintを閉じて、このEditorUtilityWidgetを実行してみます。
↓実行結果

簡単に追加できちゃいましたね!

ちなみに、UE5ではToolBar周りの設計が少し変更され、「LevelEditor.LevelEditorToolBar」という名前のToolMenu自体にメニューを登録するのではなく、「LevelEditor.LevelEditorToolBar.AssetsToolBar」等のように、LevelEditor.LevelEditorToolBar以下にそれぞれの区画のMenuToolオブジェクトがあり、そこに対して追加するようにしないと表示されません。

このあたりのどのメニューがどんな名前なのかは、残念ながらソースを読まないとわからない…ことはありません!無理やりですが、メニュー名一覧を表示することもできます。

※このあたりは【UE4】Blueprintだけで、Editor上にメニューを追加する【★★★】で説明しているので詳しくはそちらを見るか、サンプルプロジェクト(UE4UE5)をダウンロードして「EUW_EditorMenuChecker」ご確認していただければと思います、やり方としてはToolMenusのプロパティを強制表示するEditorUtilityWidgetを作成します。
↓作成したEditorUtilityWidget

(どの名前がどこと連携しているかは地道に調査するしかないですけどね…;;)

次に、ボタンが押された際の処理を書いていきます。
先程作成した「EditorUtilityToolMenuEntry」を継承したBPを再び開きましょう。

MyBlueprintタブ内のFunctionsカテゴリの+にカーソルを合わせて「Override」ボタンを押します。
すると、いくつかOverrideできる関数が表示されるかと思います。
その中で、「Execute」という項目をクリックしましょう。

すると、Executeイベントノードが作成されるので、そこからPrintStringを作成して処理をつなげます。

そしてCompileが終わったら、再びメニュー登録用のEditorUtilityを実行してみます。

ただ、この調子で実行するたびにメニューが増えていくと見切れてしまいアクセスしづらかったりするので、メニュー登録時に、これから登録するメニューと同じEntryを持つメニューを削除してから追加するように少し改造してみます。

これでもう一度実行すれば、重複することなく常に新しいメニューが登録されていきます。
で、試しに新しく登録したメニューを実行してみましょう。

メニューボタンが押されるたびにPrintStringが処理されていますね!

この方法で登録したEditorUtilityToolMenuEntryオブジェクトは、明示的にRemoveを行うか、Editorが終了するまでずっと残り続けるため、内部で何かしらの変数を保持したり特定のイベントをバインドして待機させたりといったことができるようになってます。

↓押した回数をカウントする処理

連続で押した結果↓

逆に初期化をさせる場合は明示的に初期化を行わないと不具合を生みますが、この機能を使うことでできることがかなり広がりますよね!

ちなみに、他のOverrideできる関数についても説明しておきます。

CanExecute

ReturnValueにFalseを返すことで、メニューに表示はされているがグレーアウトされた見た目になり、クリックすることができなくなります。

ConstructMenuEntry


メニューが構築されるたびに処理が走ります。
(例えば右クリックメニューであれば、右クリックでメニューが表示されるたびに呼ばれる)
注意!
このイベントをBlueprint上においておくだけで、Menuを登録する側の処理の際にメニューへエントリーさせる部分の処理が飛ばされてしまいます。不具合ではなく明示的にそう処理しているようなので、何かしら意図があってやっていると思われます。
(気になる方はToolMenus.cppのUToolMenus::AssembleMenuSectionあたりを参照してください)

GetCheckState

UserInterfaceActionTypeが「Check」になっている場合に、表示上の状態を設定できる関数です。

GetIcon,GetLabel,GetToolTip

それぞれ設定したアイコン(の各種名前)、ラベル、Tipsの値を更新できます。

IsVisible

メニュー自体を表示するかどうかを制御できます。

※大体のOverride関数に「Context」という構造体がありますが、これは所属しているSlate側の情報を持っているオブジェクトみたいなもので、一応Object側まではアクセスできますが、BPではほぼ使いようがないので説明を省きます。

~Section用のObjectを追加する~

EditorUtilityToolMenuEntryは、1つのメニューに対して1インスタンスを生成して、メニューの処理を管理してくれます。
そして先程も紹介しましたが、もう一つ「EditorUtilityToolMenuSection」というクラスも似たような機能なので、説明します。

EditorUtilityToolMenuSectionは、名前の通りメニューに対してではなく、メニューが所属するSectionにインスタンスをぶら下げることができます。

まずは作成してみます。

ContentBrowser左上緑のボタンの「Add/Import」から「Editor Utilities」→「Editor Utility Blueprint」を選択します。

出てきたウィンドウから「EditorUtilityToolMenuSection」を選択します。

作成したらBlueprintを開いてみます。

EditorUtilityToolMenuEntryと比べると、機能自体はかなり小さいものになります。
使える機能としては、「Construct Sections」というImplementableEventをOverrideできるぐらいです。

このイベントは、セクションが構築されるたびに処理がはしるようになっています。

上記のようなBPを作成したら、先程のメニュー登録用BPにこのSectionを登録するための処理を追記してみます。

わかりやすく処理が動いているかを確認するために、登録する場所をツールバーではなく、Folderの右クリックメニューにしてみます。

↓実行結果

こんな感じで、メニューが作られるたびに処理が走ってくれます。

EditorUtilityToolMenuSectionに関しては、直接メニューに影響を与えるような設定はありませんが、メニューを開くたびに何かしらのリストからメニューを生成するなどの処理を書いたりすることで、セクションごとのマネージャー的な使い方ができたりします。

ちなみにツールバーなどで表示されるアイコンですが、特に設定しない場合は歯車のアイコンが表示されます。

このアイコンは、新しいアイコンを使う場合はC++から画像データの登録が必要です
(FSlateStyleSetを継承し定義する)
が、既に登録されているアイコンであれば、設定することが可能です。

試しにRecompileのアイコンを設定してみます。

EditorUtilityToolMenuEntryの「Icon」部分の項目を以下のように編集。

StyleSetName:EditorStyle
StyleName:LevelEditorRecompile
SmallStyleName:LevelEditor.Recompile.Small

↓実行結果

どの名前を設定すればどれになるかは、ソースに直接定義されているので、ソースを読んでくださいっ!(え……)
Editorで使われているアイコンは「SlateEditorStyle.cpp」をみるといっぱい見つかります。

それと、ActionUtility系のメニューでは、メニュー用関数に引数をつけることでEditorの処理に同期した拡張ウィンドウを使用することができました。
とても便利でしたが、こちらではそのメニューを使用することができな…っとUE5EA版を見ると、「Show Object Dialog」「Show Objects Dialog」という便利な関数が追加されているではありませんか!

ActionUtility系で表示されていたウィンドウが、任意のObjectを指定し、そのプロパティを表示してくれるようです!

早速使ってみる

適当にEditorUtilityObjectクラスを継承してEUBを作成。

適当にString変数を作成

先程のMenuEntryクラスの「Event Execute」から処理を↓のように記載

そして実行

めちゃめちゃ便利そうですね!!

ちなみに、毎回テスト動画では、EditorUtilityBlueprintをRunして実行していますが、 普通メニューへの登録はEditor起動直後から実行してほしいかと思います。【UE4】Blueprintだけで、Editor上にメニューを追加する【★★★】でも記載してありますが、Editor起動時にEditorUtilityBlueprintを実行してくれる機能を使うことで、Editorを開いた瞬間から独自メニューの登録を行うことができます!

Explorer上でプロジェクトがあるフォルダを開き、「Config」フォルダ以下にある「DefaultEditorPerProjectUserSettings.ini」というファイルを開きます(なければテキスト等を作成し、拡張子ごと名前を上記にしてみてください)


その後メモ帳などで開いて、以下の文を追加します。

[/Script/Blutility.EditorUtilitySubsystem]
+StartupObjects=/Game/EditorMenu/EUB_RegisterToolbarMenu.EUB_RegisterToolbarMenu
#=後のパスは、Menuを登録するためのContentBrowser上でのPath + "." + ContentBrowser上での名前を記載してください

これで、Editorを再度開くことで起動時にMenu登録用EUBが実行されて起動直後からメニューが表示されます。
↓実行結果

詳しくは以前の記事をご覧になるか、公式ドキュメント:エディタの起動時にブループリントを実行するを御覧ください。

さて、まだ説明しきれていない部分もありますが、そろそろアジの気力が限界なのでこれにて沖へ帰ります!

終わりに

いかがだったでしょうか!?
EditorUtilityBlueprintやEditorUtilityWidgetは基本的に直接パッケージには影響しないので、インゲームの処理負荷等をあまり気にせずに作っていけるのもメリットかなと思います。
※みんながみんな大量にメニュー追加していくと、EditorがごちゃごちゃになったりEditor自体が重くなりやすいので、ご利用は計画的にねっ!
久々なので少し詰め込み過ぎた感はありますが、皆さんのプロジェクトにも是非気軽にメニューを追加して開発を効率化してみてください!

明日はチベスナ(ほり)さんの「初心者ですが何か書きます!」です!
Unreal歴はまだ3ヶ月ほどらしいですが、どんな記事を書くのか楽しみですね!

かなりボリュームのある記事になりましたが、最後まで読んでいただき感謝感激です!
おまけですが、久々のキンアジちゃんの落書きをどうぞ!今年もあと2週間ほどですが、お体にお気をつけて!良いお年を!

Thank you for reading !!

※この記事で使用しているUnrealのVersionは04.27.2及び05.0.0EarlyAccessです。

※この記事のサンプルプロジェクトは以下URLにアップされています。

UE4:サンプルプロジェクト
UE5:サンプルプロジェクト

コメントを残す

メールアドレスが公開されることはありません。