LoginSignup
0
1

More than 3 years have passed since last update.

UE4 CommandletでBPを変更してみる

Last updated at Posted at 2021-02-05

概要

UnrealEngine の Commandlet についてのメモです。
BPの設定変更を試してみます。

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

環境

Windows10
Visual Studio 2017
UnrealEngine 4.25

参考

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

UnrealEngine : UCommandlet
UnrealEngine : Commandlets

関連ソース

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

作成例

ブループリントの設定をコマンドレットにて書き換えてみます。

元となるBP

サードパーソンのC++で新規作成した AMyProjectCharacterクラスに対し、テスト用のメンバ AnimClassTest を追加します。アニメーションブループリントを想定します。配列にしているのは特に意味はありません。

MyProjectCharacter.h
UCLASS(config=Game)
class AMyProjectCharacter : public ACharacter
{
    GENERATED_BODY()
public:
    // ..省略..

    // テスト用
    UPROPERTY(Category = "Test", EditAnywhere, BlueprintReadWrite)
    TArray<TSubclassOf<class UAnimInstance>> AnimClassTest;

};

これを親とするBP ThirdPersonCharacter は以下の様になっています。
追加された AnimClassTest が確認できます。
ここを書き換えてみます。

ThirdPersonCharacterBP.jpg

BP書き換え用コマンドレットクラス

対象BPを探し出してデフォルトオブジェクトを取得し、書き換えます。
(ファイル検索時のアセットレジストリのフィルタの使い方が微妙です。)
また予めコンストラクタで書き換えるクラスを FClassFinder で探します。クラスなので、パスには _Cを付ける必要があります。(もしくは拡張子を省略)

MyCommandlet.h
#pragma once

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

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


public:
    UMyCommandlet(const FObjectInitializer& ObjectInitializer);

    virtual int32 Main(const FString& CmdLineParams) override;

    // 対象アセットリストを取得する
    void GetAssetList(TArray<FAssetData>& _AssetList) const;

    // 参照保持するクラス
    TArray<TSubclassOf<UAnimInstance>> AnimBPList;
};

コンストラクタで書き換え先のクラスを取得します。
ファイル名を配列にしているのはこのコード例では無意味です。直接Findしてしまってもいいです。
変更したBPに対し、MarkPackageDirtyを付けていますが、コマンドレットの実行では不要かと思います。

MyCommandlet.cpp
#include "MyCommandlet.h"
#include "AssetData.h"
#include "AssetRegistryModule.h"
#include "AssetRegistryHelpers.h"
#include "Misc/FileHelper.h"
#include "UObject/Object.h"
#include "UObject/Class.h"
#include "./MyProjectCharacter.h"

DEFINE_LOG_CATEGORY_STATIC(LogTextMyCommandlet, Log, All);

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

    // 書き換えるファイルパス
    const TArray<const TCHAR*> _AnimBPList = {
        TEXT("AnimBlueprint'/Game/Mannequin/Animations/ThirdPerson_AnimBP.ThirdPerson_AnimBP_C'"),
    };
    AnimBPList.SetNum(1);

    for (int32 _Lp = 0; _Lp < AnimBPList.Num(); _Lp++) {
        ConstructorHelpers::FClassFinder<UAnimInstance> find_class(_AnimBPList[_Lp]);
        if (find_class.Succeeded()) {
            // アニムインスタンスクラス保持
            AnimBPList[_Lp] = find_class.Class;
        }
    }

}

// メイン処理
int32 UMyCommandlet::Main(const FString& CmdLineParams)
{
    TArray<FAssetData>  _AssetList;
    TArray<UPackage*>   _PackagesToSave;

    // アセットリストの取得
    GetAssetList(_AssetList);

    // アセット変更
    for (auto _AssetData : _AssetList) {
        // BPにキャスト
        UBlueprint* _Blueprint = Cast<UBlueprint>(_AssetData.GetAsset());

        if( _Blueprint ){
            // クラスデフォルトオブジェクトを取得
            auto _MyChara = _Blueprint->GeneratedClass->GetDefaultObject<AMyProjectCharacter>();

            if (_MyChara) {
                // 変数を書き換える             
                if( _MyChara->AnimClassTest.Num() > 0){
                    _MyChara->AnimClassTest[0] = (UClass*)(AnimBPList[0]);
                }

                // 編集フラグを立てる
                _Blueprint->MarkPackageDirty();

                // 変更したパッケージ追加
                _PackagesToSave.Add(_AssetData.GetPackage());
            }
        }

    }

    // アセットセーブ
    for (const auto& _It : _PackagesToSave) {
        // 保存ファイル名(Chopで文字操作して無理矢理作成しています)
        const auto _FileName = FString::Printf(TEXT("%s%s%s"), *FPaths::GameContentDir(), *_It->GetFullName().RightChop(14), *FPackageName::GetAssetPackageExtension());

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

    }

    return(0);
}

// 対象アセットリストを取得する
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(TEXT("Blueprint"));

    AssetRegistry.GetAssets(Filter, _AssetList);
}


実行テスト

エンジンフォルダにあるexeに uproject と-runでコマンドラインクラス名を指定します。MyCommandlet のうち、Commandletの部分は省略できます。

コマンドプロンプト
"Engine\Binaries\Win64\UE4Editor-Cmd.exe" "MyProject\MyProject.uproject" -run=My -stdout -UTF8Output

結果

テスト用の変数 AnimClassTest が書き変わります。

Result.jpg

クラスだけではなく、もちろん変数なども同様の方法で書き換え可能です。

備考

起動するエンジンのバージョン違い

以下のようなエラーになります。
VerError.jpg

*.uproject のエンジンバージョンを確認しましょう。

*.uproject
{
    "FileVersion": 3,
    "EngineAssociation": "4.24",
    "Category": "",
    "Description": "",
    "Modules": [
        {
            "Name": "MyProject",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        }
    ]
}

C++側からのコマンドレット呼び出し

ログを出力するだけのコマンドレット MyTest1CommandletMyTest2Commandlet を呼び出す場合のコード例。

MyCommandlet.cpp

// メイン処理
int32 UMyCommandlet::Main(const FString& CmdLineParams)
{
    // コマンドレット1呼び出し
    ExecuteCommandlet(FString("MyTest1Commandlet"), FString("-stdout -UTF8Output"));
    // コマンドレット2呼び出し
    ExecuteCommandlet(FString("MyTest2Commandlet"), FString("-stdout -UTF8Output"));

    return(0);
}

// コマンド実行
void UMyCommandlet::ExecuteCommandlet(const FString& _CommaneletName, const FString& _Params)
{
    // コマンドレットクラスを探す
    UClass* _CommandletClass = FindObject<UClass>(ANY_PACKAGE, *_CommaneletName, false);
    if( ::IsValid(_CommandletClass) ){
        // コマンドレットオブジェクトを作成
        UCommandlet* _Commandlet = NewObject<UCommandlet>(GetTransientPackage(), _CommandletClass);
        if( ::IsValid(_Commandlet) ){
            // 実行
            _Commandlet->Main(_Params);
        }
    }

}

この方法だと、エディタ1回の起動で複数コマンドレットを呼び出せます。

まとめ

コマンドレットの有用な使い方を模索中です。:grinning:
多数のブループリントを条件によって書き換える場合などには使えると思います。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1