[UE5] Blueprint ・Componentが持つプロパティのデフォルト値をSubobjectDataSubsystemを使って確認・変更する方法について

# はじめに
開発を進めていると、Blueprint（BP）やComponentが持つ各プロパティのデフォルト値（初期値）をリスト化したり一括で変更したくなります。例えば、Tickが不要に有効になっていないか確認したり、Componentが使用しているアセットを変更したりなどです。（レベル上のActorの話ではなく、その元となるコンテンツブラウザ上のBPアセットの話です）

しかし、**コンテンツブラウザ上のBPアセット（```UBlueprint```）** と **実装で使っているBPクラス(```AActor```など)** は **別物** なので、実はそこそこ面倒でした…。更にC++またはPythonがほぼ必須になり、BPのみで実現することができませんでした…

が、UE5で追加された```SubobjectDataSubsystem``` を使うことで、**BPからでも簡単にプロパティを確認・変更することができるようになりました！**

https://twitter.com/pafuhana1213/status/1560260002752765952

神！（[実はComponentを追加したりもできます](https://twitter.com/pafuhana1213/status/1560251746206695425)が、それはまた別の記事で…）

ということで、今回はSubobjectDataSubsystemの使ってプロパティのデフォルト値を確認・変更する方法についてご紹介します。少し細かい話もありますので、早く使いたい！って方は後半まで飛ばして大丈夫です。

## UE4時代の場合
UE4時代、BPクラスの各プロパティのデフォルト値はClass Defauilt Object（CDO）からアクセス可能でした。しかし、下図のとおり縦に長く出てしまいますし、よくよく見ると微妙に足りません…（例えば、TickGroupとか）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/b2bb8594-5c01-0c02-a5ea-0c0cea4279ca.png)
そして、Setが用意されていないのでプロパティの初期値を変更することができません。そのため、対象のBPやComponentが持つプロパティの初期値をGet/Setしたい場合は、C++またはPythonの利用が必須になっていました。
### C++の場合
```c++:c++
#include "MyEditorBlueprintFunctionLibrary.h"
#include "Kismet2/CompilerResultsLog.h"
#include "Kismet2/KismetEditorUtilities.h"

void UMyEditorBlueprintFunctionLibrary::EditActorProperty(UBlueprint* Blueprint)
{
	if (!Blueprint)
	{
		return;
	}
	
    // CDOを取得して初期値を変更
	UClass* Class = Blueprint->GeneratedClass;
	AActor* CDO = Class->GetDefaultObject<AActor>();
	CDO->SetTickGroup(TG_DuringPhysics);

	// コンパイルして変更を反映
	FCompilerResultsLog LogResults;
	FKismetEditorUtilities::CompileBlueprint(Blueprint, EBlueprintCompileOptions::None, &LogResults);

	// Blueprintに編集されたことを通知
	Blueprint->MarkPackageDirty();
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/1de14057-64bb-35a1-b9d7-a8ce1433e0d1.png)
### Pythonの場合
```python:python
import unreal

selected_asset_datas = unreal.EditorUtilityLibrary.get_selected_asset_data()
for asset_data in selected_asset_datas:
    asset_class = unreal.load_class(None, str(asset_data.object_path) + "_C")
    cdo = unreal.get_default_object(asset_class)
    cdo.set_tick_group(unreal.TickingGroup.TG_POST_UPDATE_WORK)
    unreal.SystemLibrary.transact_object(cdo)
```
## 参考
[【UE4】DefaultObjectを使ってアセットの初期値を取得](https://toofu0.hatenablog.com/entry/2020/04/20/025321)
[【UE4】【Python】BlueprintClassのデフォルトの変数値を一括変更する【★★★】](https://kinnaji.com/2019/10/01/python-util/)
[Creating and editing Blueprint from C++](https://forums.unrealengine.com/t/creating-and-editing-blueprint-from-c/580589)

# UE5で追加された SubobjectDataSubsystem
この状況を解決するのが、今回ご紹介する ```SubobjectDataSubsystem```です！
最新の機能なので残念ながら情報やドキュメントはほぼ0なのですが、エンジンコード内のコメントを見ると

>The Subobject Data Subsystem will produce the reflected subobject data based on a given root object. A root object can be anything, an actor instance clicked on via the level editor, a UBlueprint* by opening an asset, or something piped in from python or other scripting languages.
> サブオブジェクト データ サブシステムは、指定されたルート オブジェクトに基づいて反映されたサブオブジェクト データを生成します。 ルート オブジェクトは、レベル エディターを介してクリックされたアクター インスタンス、アセットのオープンによる UBlueprint*、Python やその他のスクリプト言語からパイプされたものなど、何でもかまいません。。 (サクッとGoogle翻訳した結果)

レベル上のActor、今回の議題であるBlueprintなど何でも編集できる凄い機能っぽいのが伝わってきます！しかも、BPにたくさんノードが公開されているのでBPオンリーで色々できそうです！神機能の予感！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/b3571c07-183a-d618-675b-92b760110910.png)

ということで、```SubobjectDataSubsystem```を使ってプロパティの初期値を取得・変更してみます！といっても、超簡単です！なお、エディタ限定のノードを複数使用するため、```Editor Utility Blueprint``` や ```Editor Utility Widget```で処理を組んでいきます。

## SubobjectDataSubsystem を使って、BPが持つプロパティの初期値を『取得』してみる
まずは、```SubobjectDataSubsystem``` が持つ ```Gather Subobject Data For Blueprint```ノードを使って、指定のBPアセット（```UBlueprint```）からBPクラス(```AActor```)を取得します…え、もう AActorまでたどり着いちゃいました！超かんたん！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/f4fb7aad-4a96-dd29-7dc0-1e2cb2f0c0e7.png)
### ↑のノードの解説
```Gather Subobject Data For Blueprint``` ノードを使うことで、対象のBPアセットが持つSubobject Dataのリストを取得できます。このリストにはそのBPアセットが管理してる**BPクラス**やその**BPクラスが持つComponent**の情報が格納されています。

そして、```Get Data``` ノードと```Is Actor``` ノードでその情報がBPクラスなのかを判別して、もしBPクラスだったら```Get Object```で実際のデータを取得し、最後に編集したいクラスにCastしています。

あとはいつもの通り、Getノードや専用の関数から各プロパティの値を取得することができます！超かんたん！！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/d5b4eb2f-66a9-4da8-7938-323d05987ec4.png)

## SubobjectDataSubsystem を使って、BPが持つプロパティの初期値を『変更』してみる
BPクラスまでCastできてるので、初期値の変更もその流れで実装できます。かんたんですね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/34e236eb-ad99-5ee3-6cff-c7664e0ae0ec.png)

```Transact Object```ノードを使っているのは、アセットに編集中/未保存マーク(Dirty Flag)をつけるためです。これをしておかないとデータが変わったことがエディタに伝わらないので、保存忘れやバージョン管理がうまくいかないなどのトラブルが発生する可能性があります。なので、結構重要だったりします。
ちなみに、```Transact Object```ノードは [Undo/Redoを実装する際に使うもの](https://qiita.com/EGJ-Kaz_Okada/items/985b98fb934d751f4f69) なのですが、こういった用途でも使えます。便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/7de69ca7-6621-014c-0818-ed82c5c2ddc3.png)

## SubobjectDataSubsystem を使って、Componentが持つプロパティの初期値を『取得・編集』してみる
次は、BPが持つComponentの初期値を変更してみましょう！
はい、できました。かんたんですね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/e2e381e9-e04f-737e-6e55-d23be8e2f133.png)
先程はDataの中身がActorか否かで判別してましたが、Componentの場合は```Is Component```ノードで判別可能です。判別できたら、あとは確認・編集したいComponentにCastすればOKです。

ちなみに、さきほどは割愛したのですが、プロパティの取得・編集は ```Get Editor Property```, ```Set Editor Property```ノードからも可能です。こちらであれば文字列でプロパティを指定できるので、例えば **Excelのデータを使ってプロパティを一括変更したい！** ってケースなどで便利です。また、Dirty Flagも自動でつけてくれます。
ただし、直接プロパティを弄りに行くので予期せぬ不具合が発生する可能性もありますので扱いには少し注意が必要です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/988c420b-9b24-bc88-0b2b-540ac4e79e26.png)

## SubobjectDataSubsystem を使って、レベル上のActorを編集してみる
おまけで、```SubobjectDataSubsystem```を使って選択中のActorに対して編集を行う処理も組んでみました！…といっても、Actorなので直接弄ったほうが速いので```SubobjectDataSubsystem```を使う意味は正直ありません！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363889/81e28f78-4abe-bef9-ff0f-d471c8612b18.png)

しかし、```Gather Suboject Data For Instance / Blueprint```が返す配列の型は同じなので、プロパティの初期値とレベル上に既に配置したActorが持つプロパティの値を同時に編集したり、比較しながらチェック処理を走らせるといったことをする際は便利かと思います。

# さいごに
少し話が長くなった感もありますが、 ```SubobjectDataSubsystem```により**BPだけで様々なプロパティを取得・編集できるようになりました！**
ある程度の開発規模になると、大量のアセット・Actorを手作業でチェック・編集するのは非現実的になってきます。そんな時に非常に便利な機能ですので是非ご活用ください！

おしまい

# つづき（予告）
```SubobjectDataSubsystem``` を使って、BPアセットに対してComponentを追加する方法について