FStringからブループリントをSpawnする

UnrealEngineでは、ブループリントの変数や関数にアクセスするには、キャストを行う必要があるかと思います。
ブループリント自体が、Classである為、当たり前の事ですが、ブループリントはどちらかというと、
システム的な振る舞いではなく、必要な機能の集合(UnityのPrefabのような扱い方)として、ブループリントを量産したい場合があります。
それらをスポーンする際に、必要なClassをGetAllActorsClass等で探してきてブループリントのキャストをスポーンするというのが、とても面倒だったという事がありました。

![スポーンアクタ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/f03277b1-5f37-9b74-c484-5b6b1944b131.png)

上記のノードは、UnrealEngineにおいて、ブループリントからActorをスポーンする事のできる、最も一般的なノードです。
先ほども記載しましたが、このノードでActorをスポーンする為には、GetAllActors系のノードから明確なClassの特定が必要になっています。
例えば、ブループリント名からそのClassの参照の取得やActor化ができないかと考え、調査したメモになります。


#環境

UnrealEngine 4.26.2で検証しています。
また、Runtimeでの動作ではなく、Editor時での動作のみを念頭においています。

#実現方法

実現するにあたって、方法を二通り考えました。

* AssetDataを見つける
* Stringからオブジェクト参照やClass参照を得る

#AssetDataを見つける

この方法は、簡単で分かりやすいですが、負荷もかかり泥臭い方法です。
具体的な方法としては、AssetRegistryのGetAssetsから、名前一致でAssetDataを見つけるという方法です。
例えば、下に載せるようなノードで実現できます。ResultAssetが見つけたClassの参照になります。
AssetDataを取得できている為、そのままActorからスポーンする事も可能です。
注意点として、リストを上から見て、目的のAssetを探すという方法である為、フィルタリングをかけて、検索対象になるClassの数を減らす必要があります。
例として上げたノードでは、「Blueprint」のクラスでフィルタリングしています。

![AssetDataを見つけてレベルに配置する.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/dc5abcaf-deea-1d38-b356-46789a7fc4a3.png)


#Stringからオブジェクト参照を得る

続けて、文字列からActorのインスタンス化を行ってみます。
ブループリントのクラス名だと、AssetDataを探す事しか出来ませんが、ObjectPathからオブジェクト参照にキャストする事ができます。
ObjectPathは、フォーマットが決まっている為、生成する事も可能です。

![文字列からオブジェクト参照を取得する.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/8c9da5ea-b9e3-f4ac-107d-7392816bd689.png)

以下のノードを試してみます。

![ObjectPathからレベルに配置.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/2a6d9f81-59e5-8d56-7f3f-53a8944e973b.png)

成功しました。

![オブジェクト参照をレベルに配置成功例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/c8124c91-67c2-b4ca-ddb7-857b7b18bf87.png)

また、キャストをしなくとも、ObjectPathから直接、オブジェクト参照を取得するノードが実装されていました。

![LoadAssetノード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/3892f9e2-add3-71b3-7f7e-f66905a6dc57.png)

こちらのLoadAssetノードでも同じ結果が得られます。

![LoadAssetノードの使い方.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/62318e5c-66a4-dc09-b977-9b8b083510ba.png)



#StringからClass参照を得る

オブジェクト参照でのスポーンが上手くいった為、Classからのスポーンも期待できそうです。
本来、実現したかったのがこちらの方法になります。
オブジェクト参照をキャストした時と同じように、ObjectPathからClass参照をキャストします。
こちらのノードを使う事で、StringからClass参照を取得する事ができます。

![文字列からClass参照を取得する？.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/bd191051-cd25-f6ae-479d-c43e03d836e5.png)

ノードを組んでみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/7650f0c5-6e64-ae70-d7b5-48aaf65e9e63.png)

では、これは上手く行くのか、実行してみます。

![ObjectPathからClass参照を取得した結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/c888753e-aa7e-3b00-83e9-6497b88ad601.png)

何も表示されません。調査してみます。

#StringからClass参照を得る(原因調査)

PrintStringで出したログを見てみます。

![ObjectPathからClass参照を取得のアウトプットログ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/8db46471-9f18-114a-b2c1-7de65699c769.png)











すると、以下のエラーが発生しました。


```
LogStats: FPlatformStackWalk::StackWalkAndDump -  0.624 s
LogOutputDevice: Error: === Handled ensure: ===
LogOutputDevice: Error: Ensure condition failed: !FPackageName::IsShortPackageName(Path) [File:D:/Build/++UE4/Sync/Engine/Source/Runtime/CoreUObject/Private/UObject/SoftObjectPath.cpp] [Line: 79]
LogOutputDevice: Error: Cannot create SoftObjectPath with short package name 'TestClassA'! You must pass in fully qualified package names
LogOutputDevice: Error: Stack: 
LogOutputDevice: Error: [Callstack] 0x00007ffe1cca1909 UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1cb78b7c UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1c795810 UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1c79554e UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffde2846959 UE4Editor-Engine.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffde3500664 UE4Editor-Engine.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1cb8ae84 UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1cb8d061 UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1cb622fd UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1cb36b6a UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1cb61ef1 UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1cb622fd UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1cb61704 UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1c8d2604 UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe1cb60e63 UE4Editor-CoreUObject.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe0740208b UE4Editor-UMG.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe07531637 UE4Editor-UMG.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe0749a103 UE4Editor-UMG.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe24b9859e UE4Editor-Slate.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe24bb071e UE4Editor-Slate.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe249f46be UE4Editor-Slate.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe24a77f14 UE4Editor-Slate.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe24a60337 UE4Editor-Slate.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe24a53632 UE4Editor-Slate.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe585dbcb5 UE4Editor-ApplicationCore.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe585c9197 UE4Editor-ApplicationCore.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe585de389 UE4Editor-ApplicationCore.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe585c4310 UE4Editor-ApplicationCore.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe7c33e858 USER32.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe7c33e299 USER32.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe585df914 UE4Editor-ApplicationCore.dll!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ff69bc28848 UE4Editor.exe!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ff69bc4117c UE4Editor.exe!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ff69bc4125a UE4Editor.exe!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ff69bc552bd UE4Editor.exe!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ff69bc57fea UE4Editor.exe!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe7af57034 KERNEL32.DLL!UnknownFunction []
LogOutputDevice: Error: [Callstack] 0x00007ffe7cca2651 ntdll.dll!UnknownFunction []
LogStats:                SubmitErrorReport -  0.000 s
LogStats:                    SendNewReport - 12.710 s
LogStats:             FDebug::EnsureFailed - 13.341 s
LogScript: Error: D:/Build/++UE4/Sync/Engine/Source/Runtime/Engine/Private/KismetSystemLibrary.cpp(1045): Runtime Error: "MakeSoftClassPathで無効なオブジェクトパス TestClassA。"
LogScript: Error: Script Msg: MakeSoftClassPathで無効なオブジェクトパス TestClassA。
LogScript: Error: Script call stack:
    StringToClassTool_C.ExecuteUbergraph_StringToClassTool
    StringToClassTool_C.BndEvt__StringToClassTool_Button_0_K2Node_ComponentBoundEvent_0_OnButtonClickedEvent__DelegateSignature
LogUObjectHash: Compacting FUObjectHashTables data took   9.01ms
```

#何が問題であったか

原因は、以下の部分にあるかと思います。

```
LogOutputDevice: Error: Cannot create SoftObjectPath with short package name 'TestClassA'! You must pass in fully qualified package names
```

Class名を渡してみる

```

LogScript: Error: D:/Build/++UE4/Sync/Engine/Source/Runtime/Engine/Private/KismetSystemLibrary.cpp(1045): Runtime Error: "MakeSoftClassPathで無効なオブジェクトパス TestClassA。"
LogScript: Error: Script Msg: MakeSoftClassPathで無効なオブジェクトパス TestClassA。
LogScript: Error: Script call stack:
    StringToClassTool_C.ExecuteUbergraph_StringToClassTool
    StringToClassTool_C.BndEvt__StringToClassTool_Button_0_K2Node_ComponentBoundEvent_4_OnButtonClickedEvent__DelegateSignature
LogBlueprintUserMessages: [StringToClassTool_C_1] SoftClassReferenceを存在しない

```
MakeSoftClassPathでパスが無効という事で、ObjectPathを渡してみる。

```
LogBlueprintUserMessages: [StringToClassTool_C_4] SoftClassReferenceが存在する
LogBlueprintUserMessages: [StringToClassTool_C_4] ClassReferenceを存在しない
```

SoftClassReferenceまでは、取得する事ができた。


#LoadBlueprintClassノードとLoadClassAssetBlockingノード
他に方法がないか調べたところ、目的のノードが実装されてました。
「LoadBlueprintClassノード」と「LoadClassAssetBlockingノード」になります。

LoadBlueprintClassノード
![LoadBlueprintClassノード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/362108aa-ee1b-ea09-3e82-2a32f02c2278.png)

LoadClassAssetBlockingノード
![LoadClassAssetBlockingノード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/eb1a0b0f-c02c-58a1-c0b6-3b669ab946dd.png)




それぞれ、テスト用のノードを組みテストしてみます。

LoadBlueprintClassノードのテスト
![LoadBlueprintClassノードのテスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/b52a37a1-9625-fbf0-4e31-0c377d0cf375.png)

LoadClassAssetBlockingノード
![LoadClassAssetBlockingノードのテスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/153021/247a3a51-04bc-3c34-b6a6-068f55d8f04b.png)

画像にも記載してある通り、LoadBlueprintClassノードのテストは上手く行きました。


# 結論

* StringのClass名から直接オブジェクト化やClass参照を取得はできないが、ObjectPathからは可能
* ObjectPathで、オブジェクトを取得したい場合は、LoadAssetを使用する
* ObjectPathで、Class参照を取得したい場合は、LoadBlueprintClassノードを使用する
* ObjectPathも分からず、手掛かりがClass名しか分からない場合は、AssetDataで探す