[UE4] PythonからプロジェクトのC++クラスへアクセスする方法

検証バージョン：UE4.26.2, UE4.27.2

#1. 概要
　Pythonを利用してEngineクラスにアクセスする方法は以下のドキュメントにあります。
https://docs.unrealengine.com/4.27/ja/ProductionPipelines/ScriptingAndAutomation/Python/

　本記事ではプロジェクトに作成したC++クラスの変数や関数にアクセスする例を示します。

#2. 目標
　最終的にやりたいことは**「コンテンツブラウザから選択した"C++クラス (AMyActor) を継承したBlueprintクラス (BP_MyActor) "に対してPython経由で変数をセットしたりネイティブ関数の呼び出しを行う」**です。

【コンテンツブラウザ上の表示】
![2021-12-21_03h25_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/4c1f647a-456b-b059-cd83-c39bd0494f0c.png)

【BP_MyActorの親クラスの内容】

```cpp:MyActor.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

USTRUCT(BlueprintType)
struct FMyStruct
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<FVector> VecArray;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	bool BoolValue;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float FloatValue;
};

UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UStaticMesh* MyStaticMesh;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FMyStruct MyStruct;
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
	void TestFunction();
};
```

```cpp:MyActor.cpp
#include "MyActor.h"

void AMyActor::TestFunction_Implementation()
{
	UE_LOG(LogTemp, Warning, TEXT("Call TestFunction"));
}
```

　最終的にBlueprintの変数は以下のようになることを期待するとします。

【設定前】
![2021-12-21_02h56_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/bbd39f3f-b083-ac17-9fc2-947e4c393871.png)
【設定後】
![2021-12-21_03h05_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/939c0a4c-9aa3-37c2-4f4b-52eba993197e.png)

#3. Python実装
　Pythonの実装は以下のようになります。

【前提】
・Plugin: Python Editor Script Pluginを有効にする
・Plugin: Editor Scripting Utilitiesを有効にする

```python:Test.py
import unreal
import sys
import os

@unreal.uclass()
class MyEditorUtility(unreal.EditorUtilityLibrary):
    pass

# Content Browserで選択したアセットを検索
selected_assets = MyEditorUtility().get_selected_assets()
for i, selected_asset in enumerate(selected_assets):
 
    # アセットパス取得 (target_path="/Game/BP_MyActor.BP_MyActor")
    target_path = selected_asset.get_path_name() 
    # アセットのロード
    load_asset = unreal.EditorAssetLibrary.load_asset(target_path)
    # Blueprint GC(generated class)取得 
    # (Blueprintの場合は"_C"を付与 path="/Game/BP_MyActor.BP_MyActor_C")
    bp_gc = unreal.load_object(None, target_path + "_C")
    # Blueprint CDO(class default object)取得
    bp_cdo = unreal.get_default_object(bp_gc)
 
    # 元の構造体を取得
    my_struct = bp_cdo.get_editor_property("MyStruct")

    # 構造体のbool型変数に設定
    my_struct.set_editor_property("BoolValue", True)

    # 構造体のfloat型変数に設定
    my_struct.set_editor_property("FloatValue", 100.0)

    # 構造体のVector配列に要素1追加
    points = my_struct.get_editor_property("VecArray")
    point = unreal.Vector(1.0,1.0,1.0)
    points.append(point)

    # StaticMeshにアセット/Game/Coneを設定
    static_mesh_path = "/Game/Cone"
    # Coneをロード
    static_mesh = unreal.EditorAssetLibrary.load_asset(static_mesh_path)
    # StaticMeshの変数にアセットを設定
    bp_cdo.set_editor_property("MyStaticMesh", static_mesh)

    # TestFunction関数の呼び出し
    bp_cdo.test_function()

    # 構造体をCDOに設定
    bp_cdo.set_editor_property("MyStruct", my_struct)

    # アセットを保存
    unreal.EditorAssetLibrary.save_loaded_asset(load_asset)

```
　ポイントはBlueprintアセットを扱う場合"_C"を付与することや、構造体や関数にアクセスする場合はGC/CDOを経由する必要があるということです。これらに注意しておけばEngineクラスと同様に容易にアクセスすることが可能です。