前回、前々回とキャラ絵をUMGでゲームっぽく作って表示する記事を書きましたが、いいねをたくさんもらえてありがたい限りです。さらにスポットライトに取り上げてもらえて
— アンリアルエンジン (@UnrealEngineJP) 2019年11月25日
びっくりするやら気恥ずかしいやらで、とりあえず浮かれたい気持ちをなんとか堪えつつおまけ編を書きていきます。
できたのはこんなやつ。
さてさて、4人パーティのRPGなので、キャラの表示が4人分。それぞれを状況に応じてコントロールする必要があります。各キャラのパーツをクリックするとリアクションするとこまで、とりあえずグラフに並べて確認できました。
まだ、クリックしたら暗くなるだけです。
そのノードがこんな状態。
せっかく配列にキャラ表示Widgetを4つ分入れているのに、なんだかコピペっぽい感じだし、無駄に同じ処理のイベントが並んでいます。
これをスッキリさせようというのが今回のネタとなります。
中身が同じ4つのWidgetがキャンバスに並んでいて、それぞれは個別に動作はするけど、中身は同じなのでまったく同じ振る舞いをします。
これを並べて管理しているレイアウト用のWidget(以降、親、または親Widgetとします)は、それら各キャラ用Widget(以降、子、または子Widgetと書きます)を個別に扱う必要があります。
親はいつでも好きなタイミングで子へアクセスできますが、子はバインドされていないと親への連絡はできません。
親から子へバインドすると、専用のカスタムイベントを介して通知を受け取ることになります。
必然的に、子の数だけバインドノードと通知を受け取るためのカスタムイベントが必要になります。今回のパターンでは、個別の対応をする以外は全く同じ処理を必要とするのと、カスタムイベントの名前を変えないといけないので、↓ このようなセットが4つ並びます。
↑左端のキャラ用 ↓右端のキャラ用
また、ゲーム画面の中でも、メニューボタンや機能ボタンのいくつもあるような画面では、ボタンごとに処理の方法や遷移方法が変わるために、こういったバインドとカスタムイベントの組み合わせが大量に並ぶことになります。
これをスッキリさせていきますが、まずはこれから。
新しいグラフに引っ越す
単純でシンプルで簡単な方法。
エディタに最初から用意されているEventGraph 以外にもグラフを追加できます。
Excelのシートみたいな感覚で使えます。増やすのはMy BlueprintタブのGraphsのところにある+ボタン。
これでまっさらな新天地のグラフが使えるようになります。
コピペで引っ越しできますが、実行ピン同士をつなぐことができなくなるのでカスタムイベントを使って中継します。
Sequenceノード以降を新しいグラフにペーストして、カスタムイベントを一つ追加。
元のEventGraphに戻って、このカスタムイベントを呼び出すだけ。
セットアップや初期化のような、最初に一度だけ動かして、あとはそのままといったものを置くのに向いています。
ちょっと見えないところにやってしまうだけなので、カスタムイベントが追加になりますが、ノードの量は変わりません。あまり解決にはなってないですが、メンテナンスをする際に効率が上がります。
ちなみに今回の例では4キャラ分のセットアップをSequenceノードを使って並べています。線を分断してコンパクトにできます。処理自体は順番に行われるので途切れません。
Sequenceノードはある程度のまとまりで管理できて見やすくなるし、途中や最後に追加しやすいのも便利な点です。
これで再生して普通に動けば、1スッキリ成功です。
受け取り用のカスタムイベントを共用にする
今回の例ではバインドから受け取った後の処理は同じでも、呼び出す関数がイベントディスパッチャーから通知のあった子が対象になるので、特定の子に対して関数を呼び出す必要があります。
そこで有効なのが、子であるWidget自身が『誰』であるかを一緒に通知する方法です。そうすると、受け取ったカスタムイベントは、『誰』が特定できているので、同じ処理を『誰』ごとに行うことが簡単にできます。せっかく配列にして管理しているんですもの。Index番号を利用しない手はないですね。
さて、同じWidgetなのに、個別の ID を持たせるにはどうすればいいか。それは、Expose on Spawn を使います。
さっそく子Widgetを編集していきます。新しくInteger型の変数をひとつ追加します。
Expose on Spawn と Instance Editable にチェックをつけて有効にしておきます。
これで、このWidgetが、インスタンスとして生成されるとき(Create Widget ノードとか、どこかのキャンバスにUserCreated として配置されたときとか)に値を受け取って生まれることができます。
この変数をイベントディスパッチャー経由で渡せるようにします。
イベントディスパッチャーを選択して、Detailタブから、Inputs を追加します。
Type を Integer型にします。
いったんコンパイルします。
グラフに置いてあるノードにエラーが出ることがありますが、慌てずにエラーが表示されているノードの上で右クリックして Refresh Nodes を選ぶと消えます。
入力ピンが増えるので、ここに先ほど追加した 変数をつなぎます。
これで子Widgetの編集完了。
イベントディスパッチャーからの通知に変化があったので、親Widgetの方にもエラーが出ます。が、まずは、新しくカスタムイベントを新しく用意します。このイベントはどちらのグラフに置いても大丈夫。
Integer型のピンを追加して、子Widgetを配列から取り出すようにします。
新しい受け取り準備ができました。これが共用のイベントになります。
ここで、バインドノードとつながっているカスタムイベントを一掃してキレイにします。
新しくバインドノードとつなぐのは Create Event ノードです。
↓こんなノードが取り出せます。
SelectFunction... と書かれたプルダウンから、さきほど用意した新しいカスタムイベントを探します。
このプルダウンに出てくる関数またはイベントは、パラメータのタイプと数が完全に一致してるものだけです。なので事前に作っておく必要があったのです。試しにピンの数を変えてみるとどうなるか。
カスタムイベントの方にピンを追加してみます。
CreateEventノードはどうなっているか・・・
特に大きな変化はない様子だけど、コンパイルしてみると・・・
やっぱり。
ということで、イベントディスパッチャーのパラメータ―と受け取り側のカスタムイベントのパラメータ―を一致させることが重要です。
個人的に Create Event という名前と役割がいまいちしっくりこない感じがしているのですが、このノードで、離れたところにいるカスタムイベントをつなぐような使い方ができます。コンパイルした時点で、バインドノードに何もつながっていない場合エラーが出るので、このCreateEvent をつないで鎮めます。
これで、受け取りのイベントは共用になったので、メンテナンスや改造が1か所で済むようになりました。
エラーが無くなったところで、子に『誰』であるか特定するための固有番号を与えます。UMGのキャンバスを開いて並んでいる子を一つ選択します。
Detailsタブに新しく Default という項目が増えています。
ここに、Expose on Spawn を有効にした変数の名前が出てきているので、値をセットします。これで初期値として値を持たせておくことができます。
この状態で再生して変わりがなければ2スッキリ成功です。
バインドをシンプルにする
まだスッキリさせる方法が残っています。
バインドノードのTargetピンには配列をつなぐことも可能です。
↓このようになります。
これと、Sequence ノードに5番目のピンを追加してつなぎます。
もともとあった個別のバインドノードたちはいらなくなります。
再生してみて何も変わらなければ、3スッキリ成功です。
かなりスッキリしたと思います。あともう一つスッキリさせる方法があるのですが、今回はこの辺にしておきます。デフォルトの Event Graph 以外のグラフを使う方法と、Sequenceノード、CreateEventを使った方法が紹介できたので満足です。
今回は同じ構造の子Widgetが対象なので、ForLoopを使うことも可能でしたが、まずはUIを作るうえで汎用的に役立てられるかなと思って記事にしました。
ではでは
ステキなUI開発ライフを!
おまけ
TwitterにGIF画像を上げていた、タイマーイベントを使ったゲージ回復を一部ご紹介します。EventTickは使っていません。
実際にゲームのプロジェクトで利用する場合はフラグの制御やらで回復中のクリックを抑制したり、キャラごとに回復速度の補正が入ったり、といろんな都合があると思います。下の例は子Widget内で処理しています。あくまでも一例ということで。
満タンじゃなければ、一定時間後に自身のカスタムイベントを呼び出しています。
右端の青い変数は、Set Timer by Event ノードがReturn Value として出力するタイマーハンドラーを保持しておくもので、満タンになった時に、このタイマーハンドラーに対して停止を指示します。するとタイマーはストップするので、結果このイベントは呼ばれなくなります。上図の流れだけだと、止めなくてもSetTimerByEventが処理されないので大丈夫なはずですが保険的に入れています。入れておくとゲームの都合で止めなきゃいけない時などにこのハンドラーにPause(一時停止)の指示を出すこともできます。必殺技発動なんかのデモ再生中は止めたりしますよね。デモ技発動中にも回復していたら、デモの尺によって有利不利が出てきてしまいます。
あとは、見た目を切り替える関数を適宜呼び出したり、回復中であるかどうかのフラグをセットしたり解除したりといった処理を追加すればいい感じになるはず。
キャラをクリックした時と満タン時にエフェクトとか出したいなーとか、妄想しています。
以上 おまけのおまけでした。