こんにちは、株式会社Leon Gameworksの遠藤です。

第17回では、エディタ拡張として独自メニューを追加する手法を解説しました。今回はその応用として、追加したメニューから直接Editor Utility Widget(EUW)を起動する方法を紹介します。

記事の目次

    0:動作環境

    本記事はUE5.7.4を基に執筆しており、画面のスクリーンショットはエディタの言語設定を「英語」として撮影しています。

    なお、本記事で作成するプロジェクト一式は、以下よりダウンロード可能です。

    今回のプロジェクトデータ

    1:実行するEUWを作成

    まず、メニューから起動するEditor Utility Widget(EUW)アセットを作成します。

    ▲EUW_MyTool

    今回はツールの起動までを解説するため、UIの見た目は何でも問題ありません。ここではボタンとテキストを配置しています。

    ▲ツールの見た目を作成

    2:エディタモジュールの作成

    メニューの追加はエディタ上でのみ実行される処理のため、エディタ専用のモジュールを作成します。

    {
    	"FileVersion": 3,
    	"EngineAssociation": "5.7",
    	"Category": "",
    	"Description": "",
    	"Modules": [
    		{
    			"Name": "EUTools21",
    			"Type": "Runtime",
    			"LoadingPhase": "Default",
    			"AdditionalDependencies": [
    				"Engine"
    			]
    		},
    		{
    			"Name": "EUTools21Editor",
    			"Type": "Editor",
    			"LoadingPhase": "PostEngineInit",
    			"AdditionalDependencies": [
    				"Engine",
    				"UnrealEd",
    				"Blutility",
    				"EditorSubsystem",
    				"CoreUObject"
    			]
    		}
    	],
    	"Plugins": [
    		{
    			"Name": "ModelingToolsEditorMode",
    			"Enabled": true,
    			"TargetAllowList": [
    				"Editor"
    			]
    		}
    	]
    }
    ▲モジュールの追加

    3:メニュー押下時の処理を作成

    今回は、モジュール起動時に呼び出される StartupModule 関数内で、関連する処理をまとめて実装します。

    StartupModule ShutdownModule に加え、メニュー登録用の OnWindowMenuBarExtension 関数を定義します。

    #pragma once
    
    #include "CoreMinimal.h"
    #include "Modules/ModuleManager.h"
    
    class FExtender;
    class FMenuBarBuilder;
    
    class FEUTools21EditorModule : public IModuleInterface
    {
    public:
    	//~ Begin IModuleInterface Interface.
    	virtual void StartupModule() override;
    	virtual void ShutdownModule() override;
    	//~ End IModuleInterface Interface.
    
    private:
    	/** メニューバーに拡張内容を登録 */
    	void OnWindowMenuBarExtension(FMenuBarBuilder& MenuBarBuilder);
    	
    private:
    	// メニューの拡張ポイント
    	TSharedPtr<FExtender> Extender;
    };
    ▲EUTools21Editor.h

    StartupModule でメニューを登録し、OnWindowMenuBarExtension が実行されるように設定します。

    OnWindowMenuBarExtension 内では、UEditorUtilitySubsystem いうサブシステムをロードし、SpawnAndRegisterTab 関数を呼び出してEUWを起動します。

    #include "EUTools21Editor.h"
    
    #include "EditorUtilitySubsystem.h"
    #include "LevelEditor.h"
    #include "EditorUtilityWidgetBlueprint.h"
    
    #define LOCTEXT_NAMESPACE "FEUTools21EditorModule"
    
    void FEUTools21EditorModule::StartupModule()
    {
        if (IsRunningCommandlet())
        {
            return;
        }
    
        FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
        Extender = MakeShared<FExtender>();
        if (Extender.IsValid())
        {
            Extender->AddMenuBarExtension(
                "Help",
                EExtensionHook::After,
                nullptr,
                FMenuBarExtensionDelegate::CreateRaw(this, &FEUTools21EditorModule::OnWindowMenuBarExtension)
            );
        }
    
        TSharedPtr<FExtensibilityManager> menuManager = LevelEditorModule.GetMenuExtensibilityManager();
        if (menuManager.IsValid())
        {
            menuManager->AddExtender(Extender);
        }
    }
    
    void FEUTools21EditorModule::ShutdownModule()
    {
        if (Extender.IsValid() && FModuleManager::Get().IsModuleLoaded("LevelEditor"))
        {
            FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
            TSharedPtr<FExtensibilityManager> menuManager = LevelEditorModule.GetMenuExtensibilityManager();
            if (menuManager.IsValid())
            {
                menuManager->RemoveExtender(Extender);
            }
        }
    }
    
    void FEUTools21EditorModule::OnWindowMenuBarExtension(FMenuBarBuilder& MenuBarBuilder)
    {
        MenuBarBuilder.AddMenuEntry(
            LOCTEXT("Menu_OpenMyTool", "Open My Tool"),
            LOCTEXT("ToolTip_OpenMyTool", "Open My Tool"),
            FSlateIcon(),
            FUIAction(FExecuteAction::CreateLambda([]()
            {
                UEditorUtilitySubsystem* EUSubsystem = GEditor->GetEditorSubsystem<UEditorUtilitySubsystem>();
                
               if (UEditorUtilityWidgetBlueprint* euw = LoadObject<UEditorUtilityWidgetBlueprint>(nullptr, TEXT("/Game/EUW_MyTool.EUW_MyTool")))
               {
                   EUSubsystem->SpawnAndRegisterTab(euw);
               }
            }))
        );
    }
    
    #undef LOCTEXT_NAMESPACE
    
    IMPLEMENT_MODULE(FEUTools21EditorModule, EUTools21Editor)
    ▲EUTools21Editor.cpp

    ビルド後、メニューバーから実行すると、指定したパスのEUWが起動します。

    ▲メニューバーから実行

    4:まとめ

    今回は、EUWを起動する処理について解説しました。今回はメニューバーからの実行を例に取り上げましたが、EUWから別のEUWを起動するなど汎用的に使える処理なので、ぜひ活用してみてください。

    本記事で作成したプロジェクト一式は、以下よりダウンロード可能です。

    今回のプロジェクトデータ

    株式会社Leon Gameworks

    ●公式サイト
    www.leon-game.co.jp

    ●X(Twitter)
    @Leon_Gameworks

    トンコツ(遠藤俊太)

    ●トンコツ開発ブログ
    shuntaendo.hatenablog.com

    ●X(Twitter)
    @tonkotsu3656

    TEXT_トンコツ(Leon Gameworks)
    EDIT_小村仁美 / Hitomi Komura(CGWORLD)、オムライス駆