1. サイトトップ
  2. ブログ
  3. Unreal Engine
  4. 【UE5】コマンドレットの実装と、実際に使用して

【UE5】コマンドレットの実装と、実際に使用して

こんにちは、情熱開発部・プログラマーの日高です。

先日、CEDEC2025が開催されましたが、皆さんは参加されましたでしょうか?
私はオンラインパスで配信を視聴していました。
見逃してしまったセッションや、1度で分からなかった箇所を見返すことができるタイムシフト配信にはとても助かっています。


今回は、UE5でのコマンドレットの実装方法から、実際に直面したトラブルと解決方法について紹介します。
これからコマンドレットを使ってみようと考えている方や、すでに導入済みの方にとっても、何かのヒントになれば幸いです。

※ 本記事内のコードは、Unreal Engine 5.6で動作を確認しています。

コマンドレットとは

UE5のコマンドレットはエディタを起動せずにアセットの編集や処理を行う仕組みです。

主に以下のようなシーンで活躍します

  • アセットの一括編集、検証、最適化
  • パッケージングの前処理
  • 自動化環境などへの組み込み

コマンドレットの実装と解説

では早速、テクスチャのサイズを変更するコードを例にして、コマンドレットを実装する際の注意点について紹介していきます。

MyCommandlet.h

#pragma once

#include "CoreMinimal.h"
#include "Commandlets/Commandlet.h"
#include "MyCommandlet.generated.h"

UCLASS()
class UMyCommandlet : public UCommandlet
{
	GENERATED_BODY()

public:
	virtual int32 Main(const FString& Params) override;
};

MyCommandlet.cpp

#include "MyCommandlet.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "UObject/SavePackage.h"

int32 UMyCommandlet::Main(const FString& Params)
{
#if WITH_EDITOR
	//
	// アセットの取得
	//
	TArray<FAssetData> Assets;
	{
		FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
		IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
		AssetRegistry.SearchAllAssets(true);

		// "/Game/UI/Texture"フォルダ以下のTexture2Dを取得するフィルタ
		FARFilter Filter;
		Filter.PackagePaths.Add(FName(TEXT("/Game/UI/Texture")));
		Filter.ClassPaths.Add(UTexture2D::StaticClass()->GetClassPathName());
		Filter.bRecursivePaths = true;

		// アセットの取得
		AssetRegistry.GetAssets(Filter, Assets);
	}

	for (const FAssetData& AssetData : Assets)
	{
		//
		// テクスチャのサイズ変更
		//
		UTexture2D* Texture = Cast<UTexture2D>(AssetData.GetAsset());

		// 編集前の処理
		Texture->Modify();

		// テクスチャーのサイズを変更
		Texture->MaxTextureSize = Texture->GetImportedSize().GetMax() / 2;

		// 編集後の処理
		Texture->PostEditChange();
		Texture->MarkPackageDirty();

		//
		// アセットの保存
		//
		UPackage* Package = AssetData.GetPackage();
		FSavePackageArgs SaveArgs;
		SaveArgs.SaveFlags = EObjectFlags::RF_Public | EObjectFlags::RF_Standalone;
		const FString FileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
		UPackage::SavePackage(Package, nullptr, *FileName, SaveArgs);
	}
#endif
	return 0;
}

コマンドレットを実行するbatファイル

"E:\Program Files\Epic Games\UE_5.6\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" MyProject.uproject -run=MyCommandlet -AllowCommandletRendering

実行結果

コマンドレットの実行に成功すれば、画像の様にテクスチャーのサイズがインポート時の半分(2の累乗)になっているはずです。

コードの解説

  • アセットの取得
Filter.PackagePaths.Add(FName(TEXT("/Game/UI/Texture")));
Filter.ClassPaths.Add(UTexture2D::StaticClass()->GetClassPathName());
Filter.bRecursivePaths = true;

AssetRegistry.GetAssets(Filter, Assets);

FARFilterで検索するアセットのフィルターを作成し、AssetRegistry.GetAssets()でアセットの一覧を取得しています。
この方法はパスとクラスの両方を組み合わせたフィルターを使用できて便利だと思います。

  • テクスチャのサイズ変更
Texture->Modify();

Texture->MaxTextureSize = Texture->GetImportedSize().GetMax() / 2;

Texture->PostEditChange();
Texture->MarkPackageDirty();

テクスチャーのサイズを、インポート時の半分に変更しています。

アセットを編集する際に注意したいのが以下の関数です。
アセットの種類や変更内容によっては、これらの関数が無いと変更が正しく保存されなかったり、エラーが起きる場合があります。

Modify()
アセットの編集前に置く。
(UndoやRedoに変更履歴を登録するための機能?)

PostEditChange()
アセットの編集後に置く。
変更したパラメータの反映や検証を行います。

MarkPackageDirty()
アセットが変更されたフラグを立てます。

  • アセットの保存
SaveArgs.SaveFlags = EObjectFlags::RF_Public | EObjectFlags::RF_Standalone;
const FString FileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
UPackage::SavePackage(Package, nullptr, *FileName, SaveArgs);

最後に、UPackage::SavePackage()でアセットの保存を行っています。
アセットを保存する関数を呼ばないと、これまでの変更は保存されずに破棄されてしまいます。

  • コマンドレットの実行
UnrealEditor-Cmd.exe MyProject.uproject -run=MyCommandlet -AllowCommandletRendering

コマンドレットはbatファイルやコマンドプロンプトから、 UnrealEditor-Cmd.exe [プロジェクトファイル] -run=[コマンドレット名] を実行することで動作します。
exeファイルはデフォルトで、[UE5インストールフォルダ]\Engine\Binaries\Win64\UnrealEditor-Cmd.exeにあります。
使用中のバージョンに合わせてパスは書き換えてください。

-AllowCommandletRenderingについて
このオプションは、コマンドレットの実行時に描画関連の動作を行えるようにする物です。
今回の例では、指定しなくてもテクスチャーのサイズ変更は動作するので必須ではありませんが、その場合はエディタ上のサムネイル画像が更新されません。

コマンドレット運用中に実際に起きたトラブル

ここからは、実際にコマンドレットの運用を行っている際に起きたトラブルと解決方法について触れたいと思います。

パッケージングの自動化を行っているPCでコマンドレットが動かない

  • 問題

Jenkinsで定期的パッケージングを行う環境が構築されいた所に、コマンドレットの実行を追加しました。
Shipping BuildCommandlet → Cook → Stage → Pak

Jenkinsを動かすPC(JenkinsPC)は別に用意されていたのですが、自分が使用しているPCでは動くのに、JenkinsPC上ではコマンドレットが動かない問題が発生しました。

  • 解決方法

コマンドレットの実行より前に、エディタービルド(Development Editor)を行うようにしました。
Development Editor Build → Shipping Build → Commandlet → Cook → Stage → Pak

コマンドレットはエディタービルドを行わないと更新されません。
JenkinsPCでは非エディタービルドしか行っていなかった為、コマンドレットの環境が更新されず失敗するようになっていました。

テクスチャのサイズ変更が実際に機能しているか分からない

  • 問題

コマンドレットを実装中にエディタを開いて実行結果を確認することはしましたが、実際にJenkins環境でパッケージングされた結果で確認することが出来ていませんでした。

  • 解決方法

コマンドレットで編集したテクスチャのサイズやMip数などを表示するデバッグ用の機能を用意することで、各プラットフォーム上で素早く確認することができるようになりました。

最後に

これまでUE上のアセット編集するツールにはEditor Utility Widgetを使っていたのですが、今回はJenkinsの環境に組み込む為にコマンドレットを採用しました。
コマンドレットを使ったのが初めてだったこともあり、トラブルもありましたが、問題を解決していく中でコマンドレットへの理解も深まりました。

次にコマンドレットを使う機会があれば、今回のようなトラブルを未然に防げるよう、準備や確認をより丁寧に進めていきたいと思います。

最後までお読みいただき、ありがとうございました!


【免責事項】

本サイトでの情報を利用することによる損害等に対し、
株式会社ロジカルビートは一切の責任を負いません。