前回の記事で、RPGバトル画面風のUIを作る想定で、キャラのバストアップをテクスチャにしてUMGに仕込むところまでを書きました。その続きを作っていきます。
材料はあらかた用意できたので、あとは実用に耐えるように関数をいくつか準備します。
まずはゲームの企画仕様を元に、UIで必要な処理を考えてみます。
1.パーティ編成がプレイヤーによって自由にできる想定
→ キャラの絵を変えられる
2.職業がいくつか設定されている
→ 職業の名称を変えられる
3.棒状のゲージで体力量を表し、必要なタイミングで更新される
→ ゲージの長さを変えられる。ゲージは比率で増減。
4.棒状のゲージでスキルポイントの所持量を表し、適時値が更新される
→ ゲージの長さを変えられる。ゲージは比率で増減。
5.四角のスキルゲージによって、使用できるまでの時間を表す
→ 任意の量で見た目が増減する
6.プレイヤーが編集可能なスキルのアイコンを表示
→ アイコンの種類を変えられる
7.キャラを使用できるまでの間のタイマーを表示
→ 任意の値で数字が増減する
8.キャラの絵を待機中の見た目に変更する
9.キャラの絵を満タン時の見た目に変更する
10.スキルアイコンとゲージをチャージ中の見た目に変更する
11.スキルアイコンとゲージを満タン時の見た目に変更する
ひとまず思いつくのはこのあたり。
まずはシンプルなタスクをこなす役割としてごく単純な機能のみを備えた関数を用意する。
今回の作りでは、いろいろとマテリアルでいじれるようにしているので、まず最初に準備として マテリアル インスタンス ダイナミック を作ってそれを変数化する。
だらっと数珠つなぎになるので、マクロにまとめてしまって、Event Construct につないでおく。
中身は、マテリアルを適用したImageパーツをVariables からGetノードで取り出して、Get Dynamic Material ノードをつないだら、ReturnValue ピンより Promote to Variable を選択すると、変数が作られる。
それをひたすら並べていく。
順番は適当でOK。
マテリアルはダイナミック化しないといじれないのと、マテリアルを触るたびに Get Dynamic Material するのは効率が悪そうなので、先に変数化しておく。
マテリアルの準備ができたところで、関数作成開始。
1. キャラの絵を変える setCharFaceTexture
先に用意しておいたキャラ顔用のマテリアルインスタンスダイナミックより Set Texture Parameter Value ノードをつないで、Parameter Name を セットしたら、Value ピンを関数の Inputsピン につなぐ。
2. 職業の名称を変える setJobName
もらったテキストをそのままセットするだけ。
3. 体力ゲージの長さを変える setGaugeHP
そのままマテリアルに渡すだけだけど、ゲーム内で扱う体力の値は、受け渡しの扱い方を事前に決めておかないといけない。今回は比率でいいとのことなので 0.0 ~ 1.0 で扱う想定。で直接 0.0~1.0の範囲で値を受け取る仕様。
キャラクターのパラメータが成長するタイプの場合、体力の値は整数で、例えば 800 とか 15000 みたいな値で管理する方が都合がいいので、どこかのタイミングで変換してやる必要がある。
この関数内で変換する場合はこんな感じになる。
その時点での最大値も一緒に受け取ることになる。
それと大事なのが、整数同士の割り算は結果が整数になるので、1.0までの結果はすべて 0 になってしまう。先にInt型からFloat型に変換してから割り算しないといけない。
UIのゲージ系表示で、長さが固定の場合、この計算は割と汎用的に行うので、マクロライブラリに追加してしまった方がいいかもしれない。マクロライブラリの管理担当と相談してOKが出たので追加してみる。
getRatio と命名。
このマクロを先の体力ゲージに組み込む。
完成。
4. スキルポイントゲージの長さを変える setGaugeSP
体力ゲージと同じマクロでつなぐ。
完成。
5. スキルゲージ使用チャージの状況を変える setSkillChargeGauge
これだけだとゲージの増減しかできない。
今回のデザインではゲージの見た目に溜まり具合が分かりにくいので、一緒にテキストも表示するようにしている。
0.0~1.0の値に100を掛け算するとパーセンテージになるのでそれを FormatTextノードで「%」記号を付けて書きだす。
FormatTextノードは結構柔軟に数値を取り込んでくれる(テキストに変換してくれる)ので便利だけど、桁のコントロールが必要な場合は、間にキャストノードを挟むといい。ToTextノードを展開すると以下のような設定ができるようになる。
チャージ量を表すとき、四捨五入や切り上げをしてしまうと、99.9% なのに 100%と表示されてクレームの元になるので、不都合な期待感を持たれないように切り捨てる。
上の画像のように設定すると、 1~100の整数しか表示されなくなる。
完成。
6. スキルのアイコンを指定したものに変える setSkillIconTexture
スキルのID番号を Float に変換してパラメータとして渡すだけ。
完成。
7. キャラを使用できるまでのタイマーを指定した数字に変える setWaitTimerCount
ひとまず直接パーセンテージの値で受け取ることにする。
小数点以下の桁数はデザインの都合で1桁に決めたので、この関数内で整える。
見た目にこだわって、小数部分の文字サイズを変えてしまったので、操作対象のTextBlockが2つに分かれることになった。
整数部分は、Floorで小数部を切り捨てるだけ。
小数部は、まずFrac で整数部を捨てた後、欲しい桁数×10倍 して小数点を移動し、Floorで小数部分を切り捨てれば完了。
8.キャラの絵を待機中の見た目に変更する setCharWait
9.キャラの絵を満タン時の見た目に変更する setCharReady
10.スキルアイコンとゲージをチャージ中の見た目に変更する setSkillUseWait
11.スキルアイコンとゲージを満タン時の見た目に変更する setSkillUseReady
レイアウトしてみる
これで一通りの表示をコンとロールできるようになったので、画面に仮組みしてみる。
新しくレイアウト用のWidgetを作って、キャンバスにUser Createからドラッグ&ドロップして並べる。
ドラッグするときのコツは、直接キャンバスにドロップすること。この時に中身のサイズを計算しくれる。
ヒエラルキーパネルにドロップすると、Widgetのサイズ情報がリセットされる。
Size To Content にチェックを付ければいいだけの話なのだけれど・・・
特に問題ではないんだけど手数が減るに越したことはない。
並べるときに、Anchors の設定を Bottom Centerにする。
あわせて、並べたWidgetの Aligment X を 0.5 にすると中央揃えのパーツはものすごく並べやすくなる。理由は、画面中央のX座標が0になるから。
右側か左側を並べたら、片方は符号を逆にするだけでシンメトリーになる。
キャンバスに並べ終えたら、左から0~3の順でリネームしておく。
グラフモードに切り替えたら変数を新しく追加する。→①
Variable Type に先ほどキャンバスに並べたWidgetと同じ名前があるので、それを選択。→②
配列型に切り替え。→③
適当にリネーム。 →④
この配列にキャンバスに配置したWidgetをセットして利用する。
こうしておくことで、ForLoopなどでまとめて処理できるようになるし、各Widgetを番号(0~3の整数)で扱うことができるので、なにかと便利になる。
再生するために、テスト用にレベルブループリントからViewportに書き出してみる。
CreateWidgetノードに、レイアウト用Widgetをセット。
キャンバスに並べたまんま。
関数のテストを兼ねていろいろ値を入れてみよう。
関数の動作テスト
レベルブループリントは閉じて、レイアウト用のWidgetを開く。
まずテスト用の関数を用意。関数のグラフを開いたら、
Inputsにキャンバスに配置したWidgetを探して追加。
入力ピンからドラッグして call で検索すると頑張って準備した関数名がずらっと出てくる。(※エディタの言語をEnglishにしている場合)
関数をいくつか呼び出して、ランダムな値でも入れてみることにする。
とりあえずゲージ3種とタイマー。関数名は適当に TestParam と命名。
EventGraphに戻ってつなぐ。
これで再生してみる。
配列の0番を関数に渡したので、左端のものだけ変化した。
あとは名前やらテクスチャやらも試してみよう。
さっきのテスト用の関数の中に追加。
再生。
うまくいってる様子。
この状態でも機能的にはかなり実装できた。
あと残っているのは重要な「イベント」。
イベント
イベントの発生には「条件」が付くのが基本だ。「条件」は銃で言うところの「トリガー」に相当するもので、条件が満たされた瞬間にトリガーが引かれ弾が飛び出す。飛び出した弾が「活動中のイベント」になって、次に何かに当たると当たったものによってリアクションの特定のイベントが発生する。そうやってイベントがドミノ倒しのように次のイベントを起こしていく。それが「イベントドリブン」イベント駆動ってやつだ。常に全体を見張っていて起こった事象を解析し、次の行動を確定する、というのに比べると、状況に応じて必要なイベントのみが処理されていくので無駄が少ない。
まずそのトリガーとなる要素を仕込む。
キャラ用のWidgetを開いて、キャンバスに Buttonパーツを重ねる。
名前に Action と SkillUse を付けた。
個人的に推奨しているのが、名前の先頭にUMGのパーツ名をそのまま残しておくというルール。そうすることで同じ名称が使えるし、そのままグループだということも分かりやすくできる。
見た目だけを非表示にしたいので、カラーを透明にする。
見た目より少し大きくカバーするようにしている。
ButtonパーツのDetailsタブにある緑色のイベント追加ボタンから On Pressed のイベントを追加する。触ってみて操作事故が起こるようだったら On Click に変えるといい。
On Pressed は触れた瞬間にトリガーされて、On Clicked は一度触れ後、離れた瞬間にトリガーされる。押してから間違いに気づいた時、キャンセルできるかできないかの違い。
+ボタンをクリックすると、エディタは EventGraph に飛ばされて、一度イベントノードを作ると、次回から + が View に変わっている。このあたりからもエディタの優しさというか丁寧な作りを感じる。
それぞれのボタンをクリックした時のイベント処理用のノードが置かれるので、ここにイベントディスパッチャー(イベントを起こすもの)をつなげる。
イベントディスパッチャーは、MyBlueprintタブの下の方で追加できる。
これを先のイベントに Call で取り出してつなぐ。
封筒のアイコンがつく。
2つのイベントにつないだら
クリックされたら、この赤いイベントが呼び出され、さらにイベントディスパッチャーが通知イベントを起こします。この時点で通知先はまだ未定。
これでコンパイルして保存したらひとまず完成。
レイアウトWidgetの方に移って、イベントディスパッチャーを受け取る用意をする。
テストで、クリック受け取ったら Print String ノードで画面に表示するようにしてみた。
この バインドノードが対象Widgetにある イベントディスパッチャーと紐づけ(Bind)てくれる。
バインドすることで、イベントディスパッチャーと通知先がつながるので、別々のWidgetでも通信ができるようになる。親のWidgetから子のWidgetへのアクセスはできるけど、子から親へのアクセスはできない。それを特別なルートでつなぐことができるのがこのイベントディスパッチャーとバインドと使った仕組み。
これで再生してみる。
ちゃんと反応してくれている。
これで受け取った時に待機中の見た目にすればさらに雰囲気が出そう。
まず満タン状態にするために、テスト用関数の最後に、満タン状態にする関数をつなぐ。
これで再生した時に、待機中のタイマーが消えていれば成功。
キャラのWidgetの方で初期状態を非表示にしておけばいいのだけど、関数のテストも兼ねてつないでみた。
クリックした時に待機状態にしたいので、Print String を入れ替える。
これで再生してみると。
スキルも同じようにしてみる。
他のキャラにもバインドすれば同じように動作する。
普通につなぐとちょっと冗長(だら~っとすること)になってしまう。
一応これで動くけどスマートじゃない感じ。
とりあえずテスト用関数を少しいじって、キャラ顔のテクスチャとスキルID、職業名を外から渡せるようにしてみる。
これでテクスチャとかを個別にセットして再生してみる。
一応完成。
この辺からは、キャラクターのステータスを管理する仕組みと連動するので、ひとまずUIとして仕込めるのはこの辺までにしようと思います。バインドのところがスマートじゃないので、次回《番外編》を書こうと思います。
ではでは
ステキなキャラ顔表示ライフを!