BLOGブログ

2015.12.04UE4UE/ C++

[UE4] ノードの入力ピンによって出力ピン等の内容を動的に変える仕組みについて ~解説編~

改訂バージョン: Unreal Engine 4.21

こちらは UE4 Advent Calendar 2015 4日目の記事です。
プログラマ向けの内容となります。
実装例編はコチラ


ゲーム等のインタラクティブなコンテンツを作っている時によく使うノードに「SpawnActorFromClass」や「ConstructObjectFromClass」があります。
これらのノードは他と少し違う機能を備えているのですが、お気付きでしょうか?
その機能とは、「入力ピンで指定したクラスによって、他の入力ピンや出力ピン内容が動的に変更される」というものです。

 

例えば SpawnActorFromClass を配置して、入力ピンに Character クラスを指定するとします。
SpawnActorFromClass で入力ピンのクラスを指定する前は以下の様な状態ですが、

DynamicPinNode_00

Character クラスを指定すると、出力ピンの型が「Actor Reference」から「Character Reference」に変わります。
また、新たな入力ピンとして「Instigator」が出現していたり、地味にノードタイトルが変わっていたりします。

DynamicPinNode_01

今回はこれをプロジェクト用に拡張してみます。

 

SpawnActorFromClass を例に取った構成の解説

BlueprintFunctionLibrary のような一般的なノードを作成するのであれば、クラスに static 関数として定義して UFUNCTION マクロを用いて関数指定子を適切に設定することで作成できます。
しかし今回のような特殊なノードを作成したい場合は、UK2Node クラスを継承して実装してあげる必要があります。

 

SpawnActorFromClass ノードを例に取ります。
このノードは UK2Node クラスを継承した UK2Node_SpawnActor クラスで実装されています。
カスタムノードを作成するために override できる関数をいくつか紹介します。
※ SpawnActorFromClass 内のコードは載せませんが、後で具体的な拡張事例を載せてますのでそちらをご覧下さい。

関数名 用途 & SpawnActorFromClass での拡張例
AllocateDefaultPins 入力/出力ピン情報を作成します。SpawnActor では In/Out 実行ピンの他、以下のピンが入力ピンとして定義されています。

  • BlueprintPin … スポーンするアクターのクラス
  • WorldContextPin … スポーンするアクターのクラスが必要としていれば定義
  • TransformPin … スポーンする際のトランスフォーム情報
  • bNoCollisionFailPin … スポーン先の位置にブロックコリジョンがあった場合にどうするか

更に出力ピンとして ResultPin が定義されています。

ReallocatePinsDuringReconstruction 何かしらのタイミングで入力/出力ピン情報に変更が出た場合の処理です。SpawnActor では AllocateDefaultPins して、スポーン予定のアクターのプロパティを参照して bIsExposedToSpawn(スポーンする際にプロパティを入力できるようにするフラグ)があれば、それらを入力ピンとして追加するようになっています。
ExpandNode ノード動作を確定させます。UK2Node はエディタモジュールなので、ランタイムには含まれません。つまり実際にノード実行時に何をするのか、という事をクラス内に定義することができないため、処理内容をここで確定させて KismetCompiler に情報として与えるという事が必要になります。具体的には、KismetCompilerContext から Intermediate ノードをスポーンし、呼ぶ関数リファレンスを関連付け、入力ピンのリンクを Intermediate ノードに紐付けるという事を KismetCompilerContext に通知する、といった事を必要な数だけ繰り返します。

SpawnActor では、

  1. UGameplayStatics::BeginSpawningActorFromBlueprint
  2. ExposedToSpawn が有効なプロパティを順に設定
  3. UGameplayStatics::FinishSpawningActor

といった順番で関数がリンクされています。

コンパイル時にリンクして Intermediate として出力されるため、実行時には関連付けた関数リファレンスが直接呼ばれる形になります。

GetNodeTitle ノードのタイトルを指定します。タイトルを入力クラスに合わせて動的に変更させることも可能です。
GetNodeTitleColor ノードのタイトル色を指定します。
GetClassPinBaseClass ノードの入力クラスピンに指定できるベースクラスを指定できます。これを使用することで、例えば Character クラスを継承したものしか候補に現れないといった事も可能になります。
GetMenuCategory ノードのカテゴリを指定できます。指定がなかった場合は Game カテゴリとなります。

 

兎にも角にも一番のキモは ExpandNode です。
ここで何をやっているのかが理解できれば、大抵のカスタムノードの挙動は理解できると思います。

これら以外にも拡張のための関数がいくつか定義されていますので、もっと深く知りたい方は K2Node_SpawnActor や K2Node_ConstructObjectFromClass、K2Node_CreateWidget あたりを読んでみると理解が進むかと思います。

 

ちなみに単純に SpawnActor や ConstructObjectFromClass の候補を絞りたいといった時は、継承して GetClassPinBaseClass をオーバーライドするだけでいいので簡単です。

 

実装例

上記の実装例に関しては別ページにまとめてありますので、コチラからどうぞ。

 

注意事項

今回紹介したノードの入力/出力ピンを動的に変更する仕組みは、あくまでエディタ上で編集する際に決定するものでしか内部で判断できません。

つまり入力ピンとして Enum を用意し、その値を元に出力ピンの型を変えたいといった場合は、Enum の値が実行時に変化するために出力の型を編集時に決定させることができません。

(ただ、UK2Node_Switch みたいに動的に出力実行ピンの実行先を変更することはできるので、通信内容をデシリアライズして内容に合わせて処理分岐、みたいなことは可能かと思います)

 

終わりに

C++を記述する必要はありますが、ノード拡張周りができるようになると、Blueprint からできることの幅が一気に広がります。

興味がある方は色々なノードの内部実装周りを見てみると良いかと思います。


明日は @kurosaurus さんの記事です。

SpriteStudio 周りを書いてもらえるとのことで、とても期待ですね。