【UE4】【Python】BlueprintClassのデフォルトの変数値を一括変更する【★★★】

※この記事で使用しているUnrealのVersionは04.23.0です。

※この記事のサンプルプロジェクトは以下URLにアップされています。
サンプルプロジェクト

※今回からエディターは英語版で統一することにいたしました。

Blueprintでメインのクラスを作り、その派生クラスをいくつか作った理軟化したときに、
一括で編集できたらいいな、なんてことありませんか?

今回はそのお悩みを、Pythonを用いて解決いたします。

Pythonを使って選択しているBlueprintClassのDefalt値を一括変更する レベル【★★★】

エディターを起動できましたら、まずは下準備をします。
エディター左上の「Edit」をクリックし、「Plugins」を選択します。

開いたら、プラグインの中から、「Python Editor Script Plugin」のチェックを入れましょう。

出来ましたら、一旦エディターを再起動します。

再びエディターを起動できましたら、まずは編集するための適当なBlueprintクラスのアセットを作成します。
今回はActorクラスのBlueprintを作成しましょう。

ContentBrowser左上の「Add New」から、「BlueprintClass」を選択します。

そして、「Actor」を選択して、「Select」を押しましょう。

作成したら適当に名前を付け、中を開きます。

その中に、新しい変数を追加しましょう。
名前を適当につけ、変数の型は「Integer」にします。

作成しましたら、そのウィンドウは閉じ、ContentBrowser上の今作成したBPを右クリックして、「Create Child Blueprint Class」を選択します。
これにより、今作成したクラスを継承した子のクラスを作成できます。

これを適当な回数行い子クラスのBPを大量に生産してください。

さて、ここまで出来ましたら一旦エディターを離れ、Pythonの処理を書くほうに移ります。

このプロジェクトの「Content」部分のあるフォルダーをエクスプローラー上で表示します。
そして、そこに「Python」というフォルダーを作成しましょう。

作成しましたら、中に「~.py」の形式でファイルを作成します(.txtなどを作成して拡張子を返還などしてください)

そのファイルを何らかのテキスト編集系のアプリで開くことで編集ができます。

それでは早速処理を書いてみます。

PyUtil.py

# coding: utf-8
import unreal

#EditorUtilityLibrary
@unreal.uclass()
class EUL(unreal.EditorUtilityLibrary):
    pass

def GetDefaultClassObj(AssetPath):

    #アセットのPathを"."で分ける
    Str = AssetPath.split(".")

    #分けた右側部分 + "Default__" + 分けた左側部分 + "_C"となるように文字列を生成
    result_path = ".".join((Str[0], "Default__{}_C".format(Str[1])))

    #生成したデフォルトクラスオブジェクトのパスからオブジェクトを生成して返す
    return unreal.load_object(None, result_path)

def SetDefaultProperty(AssetPath,PropertyName,PropertyValue):

    #デフォルトクラスのオブジェクトを取得
    DefaultClassObj = GetDefaultClassObj(AssetPath)

    #指定した変数のデフォルトの値をセットする
    DefaultClassObj.set_editor_property( PropertyName,PropertyValue)

def SetSelectedAssetsProperty(PropertyName,PropertyValue):

    #EditorUtilityLibraryのインスタンスを生成
    EULInst = EUL()

    #現在コンテンツブラウザで選択しているアセットを取得
    SelAssets = EUL.get_selected_assets()

    #選択しているアセットをFor分で回す
    for Asset in SelAssets:

        #Blueprintクラスかをチェック
        try:
            unreal.Blueprint.cast(Asset)
            
        except:
            #BPクラスじゃないなら次へ
            ErrorLog = str(Asset.get_name()) + "は、BlueprintClassではありません。"
            unreal.log_error(ErrorLog)
            continue

        try:
            #指定した変数のデフォルトの値をセットする
            SetDefaultProperty(Asset.get_path_name(),PropertyName,PropertyValue)
            
        except:
            #セットできなくても次へ
            ErrorLog = str(Asset.get_name()) + "は、Property「" + PropertyName + "」を持っていません。"             
            unreal.log_error(ErrorLog)
            continue
        

↑で重要なのは、Blueprintアセットのデフォルトクラスのオブジェクトのパスは、
AssetPath + AssetName + “.” + “Default__” + AssetName + “_C”となっているということです。この命名規則にのっとったオブジェクトをロードすることで、エディター上のデフォルトのアセットにアクセスすることができます。

ここまで出来ましたら、一旦このファイルをセーブして、エディターに戻ります。

ContentBrowser上で、作成した子のBPアセットを複数選択し他状態にしておきます。

その後、エディター左上の「Window」→「Developer Tool」→「OutputLog」を選択して、OutputLogウィンドウを表示します。

表示したら、 OutputLogウィンドウ左下の「Cmd」と書いてある部分を選択し、「Python」に変更します。

変更しましたら、その変更した部分の右側にあるテキストを打ち込めるウィンドウに、以下のようにコマンドを入力します。

import PyUtil
reload(PyUtil)
PyUtil.SetSelectedAssetsProperty("Var_Int",100)

そして、エンターを押すことで、ContentBrowser上で選択しているBPアセットに編集済みマークがつき、指定した変数が指定した値に代わっているかと思います。

https://kinnaji.com/wp-content/uploads/2019/10/Python_Movie.mp4

上記ではコンソールコマンドで実行しましたが、Editor Utility Blueprintなどから「Execute Python」ノードを使ってBlueprintからでも実行できます。

ちなみに、上記ではIntegerに代入しましたが、例えばBlueprintClass型の変数などの参照も入力できます。

例:「Object」型を継承したBPを作成。

最初に作ったActorクラス内に、今作成したObjectClass型の変数を追加。

この変数を持つBPクラスのアセットを選択した状態で以下のようなPythonコマンドを実行する。

import PyUtil
reload(PyUtil)
PyUtil.SetSelectedAssetsProperty("Var_TestObjClass",unreal.load_object(None,"BlueprintGeneratedClass'/Game/Blueprint/BP_Test_Obj.BP_Test_Obj_C'"))

結果↓

しっかりと変数にクラスの参照が入力されています。

このあたりの機能を応用することで、プロパティの値更新を一括で行ったり、何かの更新に伴い自動で変更した李ができるようになります。

面倒な作業はどんどん楽にしていきましょう!

+α 自分で定義したネイティブの関数をPythonで呼び出す レベル【★★★】

※ここから先は、C++プロジェクトじゃないと行えないことを記載します。
尚、サンプルプロジェクトはC++プロジェクトなので、そちらで試してみてください。
すこし容量が大きくなってしまいましたがBinaryは消していないので、.uprojectから起動で確認できます。

ちょいたしレベルの情報ですが、自分で作成した、C++側で定義した関数にUFUNCTION(BlueprintCallable)がついているものに関しては、自動的にPython側からも呼び出すことができます。

試しにC++で適当なFunctionLuibraryを作成してみます。

中身は以下のような感じです。

MyFLIB.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyFLIB.generated.h"

/**
 * 
 */
UCLASS()
class KA_PYTHON2_API UMyFLIB : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

		UFUNCTION(BlueprintCallable)
		void DebugLog(FString Str);
};

MyFLIB.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyFLIB.h"

void UMyFLIB::DebugLog(FString Str)
{
	UE_LOG(LogTemp, Log, TEXT("(%s)"),*Str);
}

これをコンパイルし、エディター上で以下のようなPythonコマンドを実行します。

FLIB = unreal.MyFLIB()
FLIB.debug_log("LogOutput is Success!")

すると、しっかりと呼び出されているのがわかります。

ネイティブで定義した関数を呼び出すには、関数にBlueprintCallableをつけてあげ、呼び出す際はすべて小文字にして、C++側で大文字だった部分の前に「_」をつけることで、呼び出せます。
(例:CallFunction → call_function)

簡単な説明ですが以上です。

※この記事のサンプルプロジェクトは以下URLにアップされています。
サンプルプロジェクト