⏳️

【UE5】Commandletを用いて指定したクラスのアセット名に特定のサフィックスがついてないか調べる

2024/10/25に公開

概要

アセットデータが多くなってくると、特定のアセットの命名規則が正しくついてるか調べるのが大変なのではないか?と思いました。
便利になる機能がないか調べたところ、今回Commandletがエディター自体を起動しなくても確認でき、アセットの一覧も取得できることがわかったので、調べながら作った機能について記事にしました。

Commandlet自体の情報が少なかったため、参考になれば幸いです。

環境

UE5.4

手順

以下の手順で作成していきました。

  1. コマンドレットを実行させるクラスUCommandletを継承させたクラスをC++で作成する
  2. 作成したCommandletを実行させるためにコマンドプロンプトからコマンドを叩く

コマンドレットを実行させるクラスをC++で作成する

今回は指定したクラスのアセット名に指定したサフィックスがないか調べるクラスはこういう形になっています。

NamingRule.h
#pragma once

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

UCLASS()
class UNamingRuleCommandlet : public UCommandlet
{
	GENERATED_UCLASS_BODY()
public:
	virtual int32 Main(const FString& CmdLineParams) override;
	void GetAssetList(TArray<FAssetData>& AssetList, FString FilterClassName) const;
};
NamingRule.cpp
#include "NamingRuleCommandlet.h"

#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/IAssetRegistry.h"

DEFINE_LOG_CATEGORY_STATIC(LogNamingRuleCommandlet, Log, All);

UNamingRuleCommandlet::UNamingRuleCommandlet(const FObjectInitializer& ObjectInitializer):Super(ObjectInitializer)
{
		
}
int32 UNamingRuleCommandlet::Main(const FString& CmdLineParams)
{
    int32 Success = 0;

    FString ClassName;
    if(FParse::Value(*CmdLineParams, TEXT("className="), ClassName))
    {
        //指定したクラスのアセットを撮ってくる
        TArray<FAssetData> AssetList;
        GetAssetList(AssetList,ClassName);
    
        FString SuffixName;
        if(FParse::Value(*CmdLineParams, TEXT("suffixName="), SuffixName))
        {
            for (auto Asset : AssetList)
            {
                //TODO 様々なログが出て見にくいため、確認用にWarningにしてます。
                UE_LOG(LogNamingRuleCommandlet,Type::Warning,TEXT("Asset Name=%s"),*Asset.AssetName.ToString());
    
                //アセット名の先頭にサフィックスがついているか確認し、ついてなければ対象のアセット名を表示する
                if(!Asset.AssetName.ToString().StartsWith(SuffixName))
                {
                    Success = 1;
                    UE_LOG(LogNamingRuleCommandlet,Type::Error,TEXT("Naming Suffix Failed Asset Name=%s"),*Asset.AssetName.ToString());
                }              
            }
        }
    }
    return Success;
}

void UNamingRuleCommandlet::GetAssetList(TArray<FAssetData>& AssetList, FString ClassName) 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/"));

    //リフレクションで名前からクラスを生成する
    UClass* AssetClass = FindFirstObjectSafe<UClass>(*ClassName);
    const FName AssetPackage = *AssetClass->GetPackage()->GetName();
    
    //対象クラス指定
    //元々はFilter.ClassNames.Addだったが、ClassNamesは非推奨になっていたのでClassPathsを使用
    Filter.ClassPaths.Add(FTopLevelAssetPath(AssetPackage,*ClassName));
    
    AssetRegistry.GetAssets(Filter, AssetList);
}

主要な部分を解説します。

コマンドライン引数からパラメーターを受け取る

Commandletの特徴は実行引数を渡すことで、文字列の情報を受け取れます。
それをParseしてC++で使える文字列にすることができます。

FString tmpParse;
if(FParse::Value(*CmdLineParams, TEXT("tmpParse="), tmpParse))
{
    //パースした結果を用いて処理する
}

今回はクラス名とサフィックスをParseしています。

アセットの取得

アセットの取得方法はこちらの記事を参考にさせていただきました。
その中で、変えた部分はフィルタ対象のクラス指定方法です。
今回は文字列から受け取ったクラス名でフィルタリングしたかったので、リフレクションを用いました。

//リフレクションで名前からクラスを生成する
UClass* AssetClass = FindFirstObjectSafe<UClass>(*ClassName);
const FName AssetPackage = *AssetClass->GetPackage()->GetName();

//対象クラス指定。
//元々はFilter.ClassNames.Addだったが、ClassNamesは非推奨になっていたのでClassPathsを使用
Filter.ClassPaths.Add(FTopLevelAssetPath(AssetPackage,*ClassName));

AssetRegistry.GetAssets(Filter, AssetList);

アセットの検査

取得したアセットを用いて検査してるのはこの部分になります

for (auto Asset : AssetList)
{
    //TODO 様々なログが出て見にくいため、確認用にWarningにしてます。
    UE_LOG(LogNamingRuleCommandlet,Type::Warning,TEXT("Asset Name=%s"),*Asset.AssetName.ToString());

    //アセット名の先頭にサフィックスがついているか確認し、ついてなければ対象のアセット名を表示する
    if(!Asset.AssetName.ToString().StartsWith(SuffixName))
    {
        Success = 1;
        UE_LOG(LogNamingRuleCommandlet,Type::Error,TEXT("Naming Suffix Failed Asset Name=%s"),*Asset.AssetName.ToString());
    }              
}

やっていることはアセット名を取得して、StartsWith関数で先頭文字にサフィックスが含まれているのかチェックをしています。

作成したCommandletを実行させるためにコマンドプロンプトからコマンドを叩く

Commandlet自体の実行方法は以下になります

実行したいエンジンバージョンのcmdのパス 実行したいuprojectのパス コマンドラインに渡す引数

5.4で実行する場合、デフォルトだと以下のパスになっていると思われますのでそれを使います。
C:\Program Files\Epic Games\UE_5.4\Engine\Binaries\Win64\UnrealEditor-Cmd.exe

例として、GameplayAbilityBlueprintGA_というサフィックスがついてないか調べるようにしたのがこちらになります。

"C:\Program Files\Epic Games\UE_5.4\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" "プロジェクトパス.uproject" -run=NamingRule -className=GameplayAbilityBlueprint -suffixName=GA_ -log

-runに今回作ったCommandletNamingRuleを指定します。

-classNameにはGameplayAbilityBlueprintを入れます。

-suffixにはGA_を入れます。

-logはつけるとWarningやErrorに色がつくので見やすいためそうしてます

結果

実際の結果がこちらになります

https://x.com/daria_nicht/status/1849472915798196333

サフィックスなどはプロジェクトやチームごとに違うと思いますので、よしなに使っていただければと思います。
ここまで読んでいただきありがとうございました。

参考

https://forums.unrealengine.com/t/findobject-withough-any-package/742812/3

https://qiita.com/t-okugawa/items/414341671238f0bcc1e3

Discussion