blog heroalche.studio

【UE5.4】 UATコマンドを使ったエディタ拡張の基礎知識

著者:

Riku Ishikawa

投稿日 2024.08.22更新日 2024.08.22

はじめに

Unreal Engine には、Unreal Automation Tool (UAT)と呼ばれる自動化システムが存在します。 UAT には様々なコマンドが存在しており、ビルド、クック、デプロイ、テストなど、エディタ上から実行するような操作をコマンドラインから呼び出すことができます。

UAT コマンドを使ったエディタ拡張?

UAT は便利なコマンドラインツールですが、それを使ってエディタ拡張をするとはどういうことでしょうか?

実は、 UAT は単なるコマンドライン向けのツールではなく、エディタが提供するビルド機能等のバックエンドでもあります。エディタのパッケージングボタンを押した際なども、エディタが適切な UAT コマンドを組み立てて、背後で UAT を実行することで処理を実行しているのです。

また、UAT コマンドは開発者が自身で定義することも可能です。これについては、以前個人的に記述した以下の記事が詳しいので、興味のある方はご一読ください。

『プロジェクトに独自の Automation Project を追加しよう 』- strv.dev

これらのエディタ機能が UAT コマンドをエディタから呼び出すことで実現されているということは、エディタ拡張においても UAT コマンドを呼び出す処理を書くことで、より広範なツールチェインの実装が可能になるということです。

また、独自定義した UAT コマンドを呼び出す UI 等を作成すれば、まるで既存のエンジン機能かのように、エディタ上にカスタムのビルドプロセスを呼び出すボタンなどを提供することができるでしょう。

パッケージングボタンや Project Launcher が提供しているような機能を、独自にエディタ上に配置できるのです。

本記事では、UAT コマンドを呼び出す C++ コードを紹介することで、この目的を達成します。

解説

CLI から呼ぶ方法

まずは基本として CLI から UAT コマンドを呼び出す方法を確認しておきます。

CLI から呼び出す場合は、以下のようにRunUAT.bat または RunUAT.sh を使って呼び出すことができます。 以下が UAT コマンドを呼び出す際の基本形です。

<UnrealEngineRoot>\Engine\Build\BatchFiles\RunUAT.bat <CommandName> -<Option1>=<Value1> -<Option2>=<Value2> ...
  • <UnrealEngineRoot>
    • Unreal Engine のインストールディレクトリを指します。
  • <CommandName>
    • 実行したい UAT コマンドの名前を指定します。

エディタをインストールしている OS によって、 .sh と .bat は適時読み替えてください。

たとえば、プロジェクトを Zip 化する UAT コマンドである ZipProjectUp を呼び出すとしたら、以下のようになります。

<UnrealEngineRoot>\Engine\Build\BatchFiles\RunUAT.bat ZipProjectUp -project=<ProjectPath> -install=<ZipFileLocation>

ZipProjectUp は以下の2つのパラメータを取っていることがわかります。

  • project
    • Zip 化するプロジェクトのディレクトリ
  • install
    • 出力する Zip ファイルのパス

C++ から呼ぶ方法

さて、では本題として、前節で見た ZipProjectUp をC++ から呼び出す方法を見ていきます。

const FString ProjectPath = "<プロジェクトディレクトリのパス>";
const FString FinalFileName = "<出力する Zip ファイルのパス>";
const FString CommandLine = FString::Printf(TEXT("ZipProjectUp -project=\"%s\" -install=\"%s\""), *ProjectPath, *FinalFileName);

// 特定のプラットフォームに関する処理のとき(パッケージングとか)に、通知UIに表示される対象プラットフォーム名
const FText PlatformName = FText::GetEmpty();
// UAT タスク実行時に記録される統計イベント(EditorAnalytics)に追加で記録する Key/Value の配列
const TArray<FAnalyticsEventAttribute>* OptionalAnalyticsParamArray = nullptr;
// UAT タスク完了時に呼び出されるコールバック
auto ResultCallback = [](const FString& ResultType, const double Duration)
{
    UE_LOG(LogTemp, Log, TEXT("Result: %s, Duration: %f"), *ResultType, Duration);
};

// UAT タスク呼び出し、監視用の別プロセスを起動
IUATHelperModule::Get().CreateUatTask(CommandLine,
                                    PlatformName,
                                    LOCTEXT("ZipTaskName", "Zipping Up Project"),
                                    LOCTEXT("ZipTaskShortName", "Zip Project Task"),
                                    FAppStyle::GetBrush(TEXT("MainFrame.CookContent")),
                                    OptionalAnalyticsParamArray,
                                    ResultCallback));

上記のコードでは、 IUATHelperModule に定義された CreateUatTask() を使って、 UAT タスクを起動しています。

別の方法として、プラットフォームのプロセスとして任意の実行可能ファイルやスクリプトを起動する FPlatformProcess::ExecProcess() を利用することも考えられます。しかし、UAT 専用に用意された CreateUatTask() を利用することには、いくつかの利点が存在します。

UAT タスクの終了をハンドルしやすい

CreateUatTask() の引数には、 TFunction<void(FString, double)> ResultCallback が存在します。 ここにシグニチャの一致する関数オブジェクトを渡しておくと、UAT タスクの終了時にコールバックを受け取ることができます。

第一引数の FString ResultType には以下の4種類の文字列が返ります。

  • Completed
  • Canceled
  • Failed
  • FailedToStart

第二引数の double Duration にはタスクの実行時間[Sec] が返ります。

エディタの右下にステータス通知を表示できる

エディタ上でパッケージング等の操作を行うとき、右下に以下のような通知 UI が表示されることがあります。

EditorNotification(Progress)

EditorNotification(Complete)

CreateUatTask() で UAT タスクを実行した場合、タスクの実行状況に応じて自動的にこの通知 UI の表示と管理を行ってくれます。また、表示内容についても引数のタスク名等を制御することでカスタム可能です。

注意事項

CreateUatTask() は、起動した UAT タスクの進行状況を監視するために背後で専用の監視プロセスを利用しています。 これは隠蔽されている部分なので通常気にする必要はありませんが、 ResultCallback で完了通知を受け取る場合には注意が必要です。

CreateUatTask() のコールバックは監視プロセス上で直接呼び出されるので、コールバック内の処理も監視プロセス上で続行されてしまいます。 UE の関数には、 GameThread でしか安全に実行できないものが多数存在するので、特別な意図がない限りはコールバック内で処理を GameThread に投げ直さなければなりません。

コールバック内で以下のように記述すると、処理をタスクとして GameThread に戻すことができます。

auto ResultCallback = [](const FString& ResultType, const double Duration)
{
    // AsyncTask 関数は指定したスレッドにラムダ式をタスクとして投げることができる。
    AsyncTask(ENamedThreads::GameThread, [ResultType=ResultType, Duration]()
    {
        UE_LOG(LogTemp, Log, TEXT("Result: %s, Duration: %f"), *ResultType, Duration);
    });
};

特にデリゲートを使って Blueprint に処理を戻す場合などは注意してください。

おまけ

RunUAT は Editor であれば Console Command から実行することができます。

RunUATOnConsoleCommand

使い方は簡単、 RunUAT に続けて、 RunUAT.bat 等に渡すのと同じコマンド名とパラメータを指定するだけです。 Blueprint の Execute Console Command ノードでも利用可能なので、簡易なユーティリティを BP を作成するにはこれだけでも十分でしょう。

おわりに

本記事では、 UAT コマンドを C++ から実行する方法について解説しました。エディタとの統合度が高く、UAT の力を簡単にエディタ上に引き出すことができるので、エディタ拡張系のツールを実装する際の選択肢として覚えておくと、非常に強力な武器となります。