UE4 Commandletでアセットを一括修正してみる

# 概要
UnrealEngine での コマンドレット(Command-Line Applet)についてのメモです。
アセット変更をコマンドレットを使って行ってみます。

▼関連
[UE4 アセットを一括修正してみる](https://qiita.com/unknown_ds/items/fc53af4a0c46cba63201) ＃EditorUtilityWidgetを使っています。
[UE4 CommandletでBPを変更してみる](https://qiita.com/unknown_ds/items/c9dff4cd4e5f03f4dc6c)

## 環境
Windows10
Visual Studio 2017
UnrealEngine 4.24

## 参考
以下を参考にさせて頂きました、ありがとうございます。

[[UE4] コマンドレットを使って、コマンドラインに任意の処理を実装する](https://qiita.com/Takezoh/items/d419c6b7771157233cca)
[UnrealC++で画像ファイルからテクスチャアセットを作成する](https://qiita.com/Naotsun/items/31b3de4dbcf0ec241398)
[C++からCommandletを実行する方法](https://shama-coo.hatenablog.com/entry/2020/04/05/171232)
[UnrealEngine : UCommandlet](https://docs.unrealengine.com/en-US/API/Runtime/Engine/Commandlets/UCommandlet/index.html)
[UnrealEngine : Commandlets](https://docs.unrealengine.com/en-US/API/Editor/UnrealEd/Commandlets/index.html)


# コマンドレット(Command-Line Applet)について
コンソールアプリのことで、GUIエディタを起動させずに実行することができます。

"Engine\Source\Runtime\Engine\Classes\Commandlets\Commandlet.h"

コマンドレットは、ゲームが読み込まれていない、クライアントコードが読み込まれていない、レベルが読み込まれていない、アクターが存在しない "生の"環境で実行されます。

# 作成例
2Dテクスチャアセットを探し出して特定のパラメータを書き換えをして保存するコマンドレットの作成例です。

```cpp:MyCommandlet.h
#pragma once

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

// テクスチャパラメータを書き換えるコマンドレット
UCLASS()
class TEST_API UMyCommandlet : public UCommandlet
{
	GENERATED_BODY()
	
public:
	UMyCommandlet(const FObjectInitializer& ObjectInitializer);

	virtual int32 Main(const FString& CmdLineParams) override;

	// 対象アセットリストを取得する
	void GetAssetList(TArray<FAssetData>& _AssetList) const;
	// アセット書き換え
	bool ChangeAsset(FAssetData&	_AssetData, float _ChangeParam);

};
```


```cpp:MyCommandlet.cpp
#include "MyCommandlet.h"
#include "AssetRegistryModule.h"
#include "AssetRegistryHelpers.h"
#include "Editor/LevelEditor/Public/LevelEditor.h"
#include "FileHelpers.h"
#include "Misc/FileHelper.h"

DEFINE_LOG_CATEGORY_STATIC(LogMyCommandlet, Log, All);

// コンストラクタ
UMyCommandlet::UMyCommandlet(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	LogToConsole = false;
}

// メイン
int32 UMyCommandlet::Main(const FString& CmdLineParams)
{
	TArray<FAssetData>	_AssetList;
	TArray<UPackage*>	_PackagesToSave;
	
	// アセットリストの取得
	GetAssetList(_AssetList);

	// 引数パース
	FString _Value;
	if (FParse::Value(*CmdLineParams, TEXT("param="), _Value))
	{
		// パラメータ値を引き数から取得
		float _Param = FCString::Atof(*_Value);
		
		// アセット変更
		for(auto _AssetData : _AssetList){
			// パラメータ変更
			if( ChangeAsset(_AssetData, _Param) ){
				// 変更したパッケージ追加
				_PackagesToSave.Add(_AssetData.GetPackage());
			}
		}
		
		// アセットセーブ
		for(const auto& _It : _PackagesToSave){
			// ファイル名
			const auto _FileName = FString::Printf(TEXT("../../../test_app/Content/%s%s"), *_It->GetFullName().RightChop(14), *FPackageName::GetAssetPackageExtension());

			// パッケージ保存
			bool _Ret = UPackage::SavePackage(_It, nullptr, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone, *_FileName);
		}
		
		return(0);
	}

	// エラー終了
	return(1);
}

// 対象アセットリストを取得する
void UMyCommandlet::GetAssetList(TArray<FAssetData>& _AssetList) const
{
	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(FName("AssetRegistry"));
	IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
	
	IFileManager& _FileMgr = IFileManager::Get();
	
	
	// 対象ディレクトリを探す
	FARFilter Filter;
	Filter.bRecursivePaths = true;
	// 対象パス（Contentフォルダ全体を対象にするなら "/Game/" を指定）
	Filter.PackagePaths.Add(TEXT("/Game/"));
		
	// 対象クラス指定
	Filter.ClassNames.Add(UTexture2D::StaticClass()->GetFName());

	AssetRegistry.GetAssets(Filter, _AssetList);

}

// アセット書き換え
bool UMyCommandlet::ChangeAsset(FAssetData&	_AssetData, float _ChangeParam)
{
#if WITH_EDITORONLY_DATA
	if (UTexture2D* _Texture = Cast<UTexture2D>(_AssetData.GetAsset())){
		// 変更する
		_Texture->AdjustRGBCurve = _ChangeParam;
		
		// 編集フラグ設定
		_Texture->MarkPackageDirty();
		
		return(true);
	}
#endif	
	return(false);
}


```


## 実行
-runで指定するコマンドレット名は `Commandlet` を省略できます。
UMyCommandletクラスの場合、`My` か `MyCommandlet` のいずれかで指定ができます。
-param でパラメータ値を指定するようにしています。

```
\Engine\Binaries\Win64\UE4Editor-Cmd.exe {PROJECT_PATH}/MyProject.uproject -run=My -param=2.0 -stdout -UTF8Output

```


# まとめ
コマンドレットを使うとエディタを起動せずに任意の処理を適用できます。コマンドレットの記事があまり見当たらないためエンジン付属コマンドレットすら大して使えていないです、どなたか有用な使い方を教えて下さい。