Unreal Engineを用いたゲーム・映像制作、ツール開発を手がける株式会社Leon Gameworksが、Unreal Engine 5の標準機能「Editor Utility」を活用したお役立ちツールの制作術を解説する連載がスタート! ツールの開発経験がない方も、プロジェクトデータをみながらぜひ試してみてください。

記事の目次

    この連載について

    こんにちは、株式会社Leon Gameworksのトンコツとキンアジです。

    本連載では、Unreal Engine 5(以降、UE5)の標準機能「Editor Utility」を活用して、開発に役立つツールの作り方について解説していきます。連載ではありますが、記事ごとにツールの完成まで解説しますので、記事単体で見ていただいても完結する内容となっております。

    また、記事の最後に記事内で解説したツールが動作するプロジェクト一式を配布しています。ぜひそちらも活用していただけたら幸いです。

    今回は第1回ということで、「Editor Utility」の概要、そしてアセットの一括リネームツールの作成方法について解説します。

    本記事で作成するツールのプロジェクト一式のデータは以下からダウンロードできます。

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

    0:前提知識や動作環境

    本記事はUE5の基本的な知識や操作方法を習得されている方に向けた記事になります。そのため、基本的なインターフェイスや概念の説明は省くことをあらかじめご了承下さい。

    また、本記事はUE 5.3.1を基に執筆しており、エディタの言語は「英語」でスクリーンショットを撮影しております。

    1:Editor Utilityとは

    「Editor Utility」とは、開発用にUE5内で動作するツールや処理を作成できるUE5標準の機能です。主に作業の効率化やヒューマンエラーの防止などの目的でツールを作成します。あくまでも開発用のツールですので、ゲームに組み込めるものではありません。

    「Editor Utility Widget」(以降、EUW)「Editor Utility Blueprint」(以降、EUB)の2種類のアセットがあり、他のアセットと同様でどちらもContent Browser上で右クリック→[Editor Utilities]から作成できます。

    ▲Editor Utility アセットの作成

    2つのちがいとしては、GUIを作成できるかどうかという点です。

    EUWは、UMGと同じようなエディタになっており、[Designer]タブでボタンなどのUIをデザインし、[Graph]タブでボタンを押した結果などのロジックを組みます。自由にUIをデザインできますので、入力から実行まで独自のツールを作成できます。

    ▲EUWのエディタ画面

    一方でEUBは、アセットやアクタのメニューに独自の項目を追加したり、ツールバーに独自のボタンを追加するといったように、エディタを拡張してツールを作成することができます。様々なクラスがあり、継承するクラスによってできることが変わります。

    本連載では、それらのクラスを網羅するかたちで様々なツールのレシピについて解説していきます。

    ▲アセットのメニューに独自の項目を追加した例

    また、EUW、EUBどちらもブループリントと同様にノードベースでロジックを組むことができます。

    通常のブループリントでは扱うことができないEditor Utility専用のノードも多数ありますので、こちらも都度解説していきます。

    ▲Editor Utility専用ノードの一部

    2:アセットリネームツールの作成

    2-1:前置き

    UEを用いたプロジェクトでは、スペルの間違いや仕様変更に伴うアセット名の変更作業が往々にして発生する場合があります。そんなときに、アセットのリネームを一括で行えるツールがあれば、とても開発の効率が上がりますよね。

    また、Editor Utilityの基本的な機能を使うので、これからUEでツールを作りたいと思っている方にも参考になるでしょう。

    では早速アセットの一括リネームツールの作成手順について解説していきます!

    2-2:アセットの一括リネームツールを作る

    このツールでは、EUWをベースに使用しますので、まずはEUWアセットを作成します。

    Content Browser左上の[Add]から[Editor Utilities → Editor Utility Widget]を選択します。

    ▲Content BrowserからEUWの新規作成

    UE 5.3から、EUWの新規作成時に基準となるWidgetを選択するウィンドウが表示されるようになりました。

    今回は、[VerticalBox]をRootとして使おうと思いますので、[ALL CLASSES]から[VerticalBox]を選択し[Select]ボタンを押してください。

    ▲Root Widgetの選択画面

    なお、Root Widgetの[COMMON]内の候補は、[Project Settings → Editor Utility Widgets (Team) → Common Root Widget Classes]でカスタマイズ可能です。

    ▲[Project Settings]での[Common Root Widget]のカスタマイズ

    作成したアセットの名前は「EUW_AssetRenameTool」としておきます。

    その後、[EUW_AssetRenameTool]を開き、まずは[Designer]ウィンドウ内のウィジェットを構築していきます。今回は、Renameをするための情報を入力するWidgetに[Detail Views]を使っていきます。

    Detail Viewsウィジェットとは、レベルエディタ上の[Details]ウィンドウのように、特定のオブジェクトのプロパティを一覧表示してくれるものです。

    ▲[Detail Views]ウィジェットで表示したプロパティ

    どのオブジェクトのプロパティを表示するかは、[Detail Views]ウィジェットの[Set Object]関数にて指定することができます。

    ▲Detail Viewsウィジェットの[Set Object]関数

    では、Designerウィンドウで以下のようにWidgetを構築しましょう。

    [VerticalBox]
     └[Editor Utility Button]
       └[Text ]
     └[Detail Views]

    のような階層構造になっています。

    ▲[EUW_AssetRenameTool]内のDesignerウィンドウ

    構築したら、以下のプロパティを編集してください。

    ① [Editor Utility Button]と[Detail Views]ウィジェットの[IsVariable]にチェックを入れる
    ② [Detail Views]の詳細の[Categories to Show]プロパティに[Properties]という文字列を追加

    ▲[Detail Views]のプロパティの変更点

    ここまでできたら、[Graph]エディタにてブループリントを記述していきます。

    まずは、[Detail Views]の初期化を行います。

    このEUWもひとつのオブジェクトになるため、自分自身のリファレンスを[Set Object]関数でセットすることで、自身がもつ変数を表示することができます。この処理を、EUWが起動したときに実行される[Event Construct]ノードに繋げます。

    ▲[EUW_AssetRenameTool]の初期化処理

    ポイントは、ひとつ前の手順でDetail Viewsウィジェットの[Categories to Show]プロパティに[Properties]と設定しているため、変数のカテゴリが[Properties]となっているもののみ表示されるようになります。なので、使用する変数を作成し、カテゴリを[Properties]に設定します。

    変数にカテゴリを設定するには、変数を選択した状態で[Details]ウィンドウ内の[Category]プロパティに直接文字を入力します。

    ▲[Graph]エディタの[Details]ウィンドウ

    今回は、以下の3つのプロパティを作成しました。

    [TargetDirectory](String):リネームをするアセットがあるフォルダのディレクトリ
    [ReplaceFrom](String):置き換える文字列
    [ReplaceTo](String):置き換えた後の文字列

    ▲[EUW_AssetRenameTool]内の作成した変数

    ひとまずここまでできたら、EUWを実行してみましょう。EUWの実行方法は、Widget Editorの左上の方にある[Run Utility Widget]で起動できます。

    ▲EUW上部の[Run Utility Widget]ボタン

    起動すると、以下のようなウィンドウが表示されるかと思います。

    ▲起動後のリネームツール

    これでリネームツールの見た目が完成しました。続いて、実際に[Rename]ボタンを押したときの処理を作成していきます。

    まずは、[Designer]ウィンドウで配置したボタンをクリックした際のイベントをグラフに追加します。

    [MyBlueprint]ウィンドウ内の[Variables]にある、作成したボタンウィジェットの変数を選択し、Detailsウィンドウ内にある[On Clicked][+]ボタンを押すことでイベントを作成できます。

    ▲ボタンウィジェットをクリックした際のイベントの作成方法

    次に、[TargetDirectory]変数で指定したディレクトリのアセットの一覧を取得する処理を作成します。

    特定のディレクトリ以下のアセットの情報は、[Asset Registry]というインターフェイスの[Get Assets by Path]関数を使用すると[Asset Data]という構造体として簡単に取得できます。

    この[Asset Data]に[Break Asset Data]ノードを使用して、必要な情報を取り出します。

    ▲[Get Assets By Path]関数を用いたディレクトリ以下のアセット情報の取得

    [Asset Data]の各種プロパティ等の説明は、公式ドキュメントを参照してください。

    さて、あとは取得したAsset Dataを基にリネームを行うだけです。

    今回はリネームを行う関数として、[Editor Asset Library][Rename Asset]関数を使用します。[Source Asset Path]にリネームするアセットのパス(Package Name)を代入し、[Destination Asset Path]にリネーム後のアセットのパスを代入することでリネームを行えます。

    ▲[Rename Asset]関数

    なお、文字列を置き換える処理として、[Replace]関数を使用します。Replace関数は、[Source String]に代入した文字列の中から、[From]に代入した文字列と一致する部分を[To]に代入した文字列に置き換えてくれます。

    ▲[Replace]関数

    これらを、先ほどの[Asset Registry]の処理と組み合わせることで、リネーム処理を作成することができます。

    ▲[EUW_AssetRenameTool]内リネーム処理の完成版

    補足として、[Get Assets by Path]関数で取得した[Asset Data]がアセットかどうかを判定するために[Is UAsset]関数を用いて判定を行なっています([Get Assets by Path]はファイルとして存在するアセット以外も取得する可能性があるので、おまじない程度に思っていただければと思います)。

    さて、ここまで実装できたら、実際に起動してリネームを行なってみましょう。[Run Utility Widget]ボタンから起動後、各種プロパティを編集します。

    今回は、以下のようにプロパティを設定しました。

    [Target Directory]:/Game
    [Replace From]:Dumy
    [Replace To]:Dummy

    そして、[Rename]ボタンを押します。

    ▲リネームツールの実行結果

    このように、複雑なブループリントを組むことなくツールが作成できました!

    2-3:使用者を考慮したツール

    さて、ここまでで作成したリネームツールでも機能としては問題ありません。また、作成者本人が使う分にはある程度実装した中身のことがわかるため問題ないですが、Editor Utilityは、UEにおける開発を効率化するための機能であり、ツールを作る側は使う人のことを考えるべきだと思っています。

    では、このリネームツールはどのようにしたらより使い勝手の良いツールになるでしょうか?

    いろいろ思いつくかと思いますが、リネームツールにおいて一番実装するべき機能は「プレビューを行える」という部分だと個人的には考えます。

    例えば「Cat」という部分を「Dog」にリネームする場合、「Cat」という文字を含むような「Catalog」等の単語にも影響を与えることになり、意図しないリネームが発生してしまいます。

    ▲意図しないリネームの結果

    こういったものを、リネームする前に確認できるようなフローがあると、使う側としても安心してツールを実行できるのかなと思います。

    では、実行する前と後のプレビューを確認できる機能も入れ込んでみましょう。

    まずは、リネーム前/後をプレビュー表示するためのEUWクラスを新しく作成します。名前は「EUW_AssetRenameTool_PreviewResult」としておきます。EUWを作成したら、Designerウィンドウで以下のようにWidgetを構築します。

    [Horizontal Box]
     ├[Text]
     ├[Text]
     ├[Text]
     └[Editor Utility Button]
      └[Text]

    ▲[EUW_AssetRenameTool_PreviewResult]内のDesignerウィンドウ

    作成したら、Graphエディタでブループリントを作成していきます。

    新しくカスタムイベントノードを作成し、[Init Result Text]と名前を付けます。そして、[Before]と[After]の2つのString型の引数を追加します。

    ▲カスタムイベントの詳細

    その後、[Asset Path]という名前でString型の変数を作成し、[Before]の値を代入します。

    そして、リネーム前/後の結果をウィジェットに反映させる[Set Text]を用いて引数に入れたテキストをウィジェット上に反映します。

    ▲プレビュー表示の初期化

    その後、ボタンウィジェットの[On Clicked]イベントを作成し、ボタンを押した際にContent Browser上で該当アセットをフォーカスする処理を作成します。

    Content Browser上でアセットをフォーカスする処理は、[Sync Browser to Objects]関数を用いることで実装できます。

    ▲Content Browser上で特定のアセットをフォーカスする処理

    作成が終わりましたら、[EUW_AssetRenameTool]の方にも改善を加えていきます。

    まずは、[Designer]ウィンドウで以下のようにウィジェットを構成します。

    [Vertical Box]
     └[Editor Utility Button]
      └[Text]
     └[Editor Utility Button]
      └[Text]
     ├[Detail Views]
     ├[Text]
     └[Editor Utility ScrollBox](IsVariableにチェック)

    ▲改善された[EUW_AssetRenameTool]内の[Designer]ウィンドウ

    次に[Graph]エディタ内のブループリントの追加処理になります。

    新しくプレビュー用に作成したボタンから[On Clicked]イベントを作成したら、最初にスクロールボックスの子となるウィジェットを空にするための[Clear Children]関数を繋げます。

    ▲スクロールボックスの子を空にする[Clear Children]関数

    プレビュー用のウィジェットは、[Construct Object From Class]ノードから、作成した[EUW_AssetRenameTool_PreviewResult]クラスを指定することで生成できます。

    生成後は、スクロールボックスに生成したウィジェットを追加して[Init Result Text]イベントを呼び出します。

    ▲プレビューウィジェットの生成

    プレビュー用の処理でも、リネームのときと同じように対象ディレクトリ以下のアセットを取得する処理が必要になるため、[Rename Asset]関数以外の処理をコピーして繋げます。

    その先に、作成したプレビューウィジェットの生成処理を繋げます。

    ▲最終的なプレビューを表示する処理

    これでプレビューを表示する処理の追加は完了です。改善した[EUW_AssetRenameTool]を実行すると、以下のような結果になります。

    このように、事前にリネームされるアセットがどのようにリネームされるかがわかるようになったので、意図しないリネームを行なってしまうリスクが大きく減ったかと思います。

    2-4:さらに深掘りした実装

    これまではリネームツールにおける最低限の実装を行なってきましたが、ここからはさらに深掘りし、あるとよさそうな便利機能を紹介していきます。

    1:正規表現

    正規表現は、特殊な記号等の文字を駆使することで、より複雑なリネームを行うことを可能にします。詳しくはこちらを参照してください。

    Editor Utilityでは、[Execute Python Script][Execute Python Command]関数にてPythonを実行することが可能なので、正規表現を簡単に使用することができます。

    詳しくはPython を使用した Unreal Editor のスクリプティングを参照してください。

    ▲Pythonを実行する関数

    では、[EUW_AssetRenameTool]を基に複製した[EUW_AssetRenameTool_Regex]に、正規表現でリネームできるブループリントを実装してみます。

    [Execute Python Script]関数を作成します。そして、[Python Script]引数に以下のようにPythonコードを書きます。

    import re
    return_string = re.sub(pattern_string, replace_string, in_string)

    その後、[Execute Python Script]を選択した状態で、[Details]ウィンドウのプロパティを以下のように編集します。

    [Input]:in_string,pattern_string,replace_string
    [Output]:return_string

    ▲[Python Script]のプロパティ

    作成した関数を[EUW_AssetRenameTool]で作成した処理の中の[Replace]関数を削除し、こちらの関数に置き換えます。

    ▲[EUW_AssetRenameTool_Regex]内のブループリント

    これで、ブループリントは完成です。

    実装したツールを使った正規表現リネームのプレビュー結果が以下になります。

    ▲[EUW_AssetRenameTool_Regex]のプレビュー結果

    [ReplaceFrom]:Dumy([a-zA-Z_]*)([0-9]+)
    [ReplaceTo]:Dummy\1_\2

    上記は、「Dumyの後に文字が続き、最後に数字が付くものに対して、DumyをDummyに置き換えつつ最後の数字の前にアンダースコアを追加する」という正規表現になります。

    より複雑な条件で大量のアセットをリネームする際はとても役に立ちそうですね!

    2:Redirectorの自動的な解消

    SubversionやPerforce等のバージョン管理ツールを使用している場合、アセットのリネームや移動を行うと、「リダイレクタ」というアセットが基のアセットのディレクトリに自動的に生成されます。詳しくはリダイレクタをご覧ください。

    このリダイレクタは、放置しておくと後々面倒なことが起きやすいため、プロジェクトによってはリダイレクタは即時「FixUp」を行い解決するというルールがあったりもします。

    ただ、Content BrowserのFilterで[Show Redirector]にチェックを入れておかなければ表示がされないということもあり、FixUp作業は結構忘れがちになってしまいます。

    ▲リダイレクタアセット

    リネームとリダイレクタはある意味密接な関係にあるので、リネームツール側である程度解消できると、開発者も余計な気を使わなくて済むため気軽にリネーム作業を行うことができます。

    そんなRedirectorですが、C++を少し用いることで自動的に解消させることが可能です。以下はそのサンプルのコードになります(BlueprintFunctionLibraryにstatic関数としてブループリントに公開しています)。

    KAEditorUtilityBlueprintLibrary.h

    #pragma once
    
    #include "CoreMinimal.h"
    #include "Kismet/BlueprintFunctionLibrary.h"
    #include "KAEditorUtilityBlueprintLibrary.generated.h"
    
    UCLASS()
    class KAEDITORUTILITYTEST_API UKAEditorUtilityBlueprintLibrary : public UBlueprintFunctionLibrary
    {
    	GENERATED_BODY()
    public:
    	//RedirectorをFixUpする
    	UFUNCTION(BlueprintCallable, Category = "KAEditorUtility")
    	static void FixupRedirectors(const TArray<FAssetData>& Assets, bool bCheckoutDialogPrompt);
    };

    KAEditorUtilityBlueprintLibrary.cpp

    #include "KAEditorUtilityBlueprintLibrary.h"
    #include "AssetToolsModule.h"
    
    void UKAEditorUtilityBlueprintLibrary::FixupRedirectors(const TArray<FAssetData>& Assets, bool bCheckoutDialogPrompt)
    {
    	FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
    	//Redirectorの取得
    	TArray<UObjectRedirector*> Redirectors;
    	for (FAssetData Object : Assets)
    	{
    		if (UObjectRedirector* Redirector = Cast<UObjectRedirector>(Object.GetAsset()))
    		{
    			Redirectors.Emplace(Redirector);
    		}
    	}
    
    	if (Redirectors.Num() > 0)
    	{
    		AssetToolsModule.Get().FixupReferencers(Redirectors, bCheckoutDialogPrompt);
    		return true;
    	}
    	return false;
    }

    この関数は、ブループリントに公開するとこのようになります。

    ▲C++で作成した[FixUp Redirectors]関数

    この関数を使って、リネーム後にリダイレクタを解消する処理を追加していきます。

    まず、リネーム処理でも使用した[Get Assets by Path]関数を作成し、返り値の[Out Asset Data]からドラッグ&ドロップでグラフ上にコンテキストメニューを表示し、[Promote to Variable]を選択します。

    すると、新しくAsset Dataの配列として変数が作成されるかと思います。作成した変数の名前は[Redirectors]としておきます。

    ▲AssetDataの配列の変数

    そして、リネームのときと同様に[Get Assets by Path]を使用してAssetDataを取得し、リダイレクタアセットなら[Redirectors]変数に追加していきます。リダイレクタかどうかの判別には、[Is Redirector]関数を使用します。

    そして、その処理の後に[Fixup Redirectors]関数で、引数に[Redirectors]を入れます。

    ▲リダイレクタを収集してから解消する処理

    最後に、[Redirectors]変数の中身を実行前に空にするために[Clear]関数を入れてあげてから、リネーム処理の後に続けて作成した処理を繋げます。

    ▲リダイレクタの解消処理のブループリントの完成版

    これで、ブループリントは完成です。以下が実行結果になります。

    3:まとめ

    いかがでしたでしょうか?

    今回は第1回ということで、比較的シンプルでよく使われるリネームツールのレシピを紹介しました。本連載では、引き続き様々なレシピを紹介していきますので、ぜひ活用して、Unreal Engineでより良い開発を目指していただければと思います!

    本記事で作成したツールのプロジェクト一式へのリンクはこちらです。

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

    株式会社Leon Gameworks

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

    ●X(Twitter)
    @Leon_Gameworks

    トンコツ(遠藤俊太)

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

    ●X(Twitter)
    @tonkotsu3656

    キンアジ

    ●キンアジのブログ
    kinnaji.com

    ● X(Twitter)
    @kinnaji_blog

    TEXT_トンコツ&キンアジ(Leon Gameworks)
    EDIT_小村仁美 / Hitomi Komura(CGWORLD)