こんにちは、葉乃音 (HANON) と申します。
普段は Shiden という Unreal Engine 5 用ノベルゲームエディタプラグインの開発をしています。(現在ベータ版を GitHub で公開中です。)
今回はこのプラグインを作る過程で直面した小ネタを。
悩んだこと
Shiden にはインゲームやエディタ上で使用するアセットをテンプレートから複製する機能があります。
たとえばセーブ画面を自作したい時、こんな感じに選択をすると...
本プラグインにおけるセーブ画面はメインの SaveMenu
クラスと各セーブスロットの SaveSlot
クラスの2つからなります。
そして、これらには SaveMenu
クラスから SaveSlot
クラスへの依存関係が存在します。
さて、テンプレートから各々のアセットを複製するだけでは、これらの依存関係までは設定されないので、どうにか設定してあげる必要があります。
ですが、どうやったら生成したばかりの Widget の設定を変更できるのでしょうか? もちろんユーザに手動で設定してもらうのもナシではないですが、出来れば避けたい...。
ここで小一時間悩むことになりました...。
下準備
とりあえず参照関係の自動設定を実現するために、出来ることをやっていきましょう。
まず SaveMenu
のテンプレート (これは単なる Widget の Blueprint です) を開き、 SaveSlot
クラスを参照できるよう、任意の User Widget クラスを指定できる変数 SaveSlotClass
を用意しました。
つまり SaveMenu
のテンプレートを複製した際 (以降、複製したものを MySaveMenu
クラスとします)、この SaveSlotClass
変数に同時生成した SaveSlot
クラス (以降、MySaveSlot
クラスとします) を自動設定するのがゴールになります。
テンプレートから複製する処理と参照関係を自動設定する処理は EditorUtilityBlueprint に書いていきます。
テンプレートからの複製は Duplicate Asset
ノードを使って簡単に行えます。ここで注意したいのは、複製したオブジェクトそのものはあくまで BlueprintWidget であることです1。
今回やりたいのは UBlueprintWidget
オブジェクトのプロパティの値変更ではなく、MySaveMenu
クラスの SaveSlotClass
変数のデフォルト値を変更すること。そのため、Load Blueprint Class
ノードの引数に複製したオブジェクトのパスを指定して、Blueprint に紐づいている MySaveMenu
クラスを取ってくる必要があります。
あとは取得した MySaveMenu
クラスのデフォルト値を変更するだけです。しかしここからが厄介です。なぜなら、Blueprint ではクラスのデフォルト値を変更することはできないからです。
こんな感じの Get Class Defaults
というデフォルト値を取得するノードはありますが、デフォルト値を変更するノードは Blueprint にはありません。
したがって、ここからはコードの出番です。
解決策1: C++ を使用する
C++ を使用する場合はこんな感じのコードを書くことで実現できます。
TargetClass から DefaultObject を取得し、SetObjectPropertyValue() によって PropertyName
変数の値を変更しています。
void SetClassProperty(const UClass* TargetClass, const FName PropertyName, UClass* PropertyValue)
{
FProperty* Prop = TargetClass->FindPropertyByName(PropertyName);
const FClassProperty* ClassProperty = CastField<FClassProperty>(Prop);
void* ValuePtr = ClassProperty->ContainerPtrToValuePtr<void>(TargetClass->GetDefaultObject());
ClassProperty->SetObjectPropertyValue(ValuePtr, Value);
}
EditorUtility のほうはこんな感じ。CreateAsset ノードは自作ですが、やっていることは既存ファイルとファイル名を被らないようにしつつテンプレートから複製しているだけです。
解決策2: Python を使用する
Execute Python Script
ノードを使用するのも一つの手です。
C++ のコードを書かなくて済むのでサクッと済ませたい場合はこちらが良いでしょう。
UE5 には Python Editor Script Plugin というプラグインがデフォルトで入っており、これを有効にすることで python を使用できます。
EditorUtility はこんな感じになります。Execute Python Script
ノードは Detail から引数を設定することができます。
スクリプトの中身はこんな感じです。
import unreal
bp_save_menu_default_object = unreal.get_default_object(save_menu_class)
bp_save_menu_default_object.set_editor_property("SaveSlotClass", save_slot_class)
やっていることは SaveMenu
クラスの default object を取得して、set_editor_property() で SaveSlot
クラスをセットしているだけです。
まとめ
Unreal Engine 5 のエディタ拡張は機能が豊富かつ優秀ですし、Blueprint だけでも大体どうにかなりますが、それだけで実現できない個所は、今回みたく C++ や python も積極的に使っていきたいですね!
明日は @T_Sumisaki さん、@tukigaselio さん、@Szr_Asakura さん、@uxtuno_ さんです!
どんな記事になるのか楽しみです!
-
ちょっとややこしいのですが、エディタ上で目にする Widget のアセットは
UWidgetBlueprint
が特定のUUserWidget
継承クラスと関連づいた形で出来ています。 ↩