前回のつづき
検証もできて満足したので、そのまま選択するとこまで作ってしまおうと勢いづいて作り込んでみました。
あらかたできた時点でTwitterに上げたのですが、このブログで記事にしていく途中で、いろいろ足りてないところを発見。不具合を直したりあれこれ触っているうちに、気が付けば それなりに機能の入ったメニューUI ができていました。
ゲームの UI は状況と仕様に合わせて柔軟に作っていくものなので、あくまでも一例として参考程度に流し見してくださると助かります。
いろいろメンテナンスとか、融通の利く作りを考えてたら、このようなアセット構成になりました。
今回の記事では、とりあえず並べてボタンのアイコンとラベルをテスト表示するところまでを書こうと思います。
ひととおり動くやつをGoogleドライブにアップしましたので、興味のある方はこちらをDLして触ってみてください。
さてさて
最終的に扱うWidgetは前回作ったのと、今回作る2つ。
(命名が適当・・・)
ひとまず並べるための新しいWidgetブループリントを用意します。
前回の記事で作ったボタンWidgetを 子とするなら それを管理する 親Widget になります。
キャンバスに HorizontalBox を一つ、ドラッグ&ドロップします。
画面中央に配置します。
正確に中央に置くためには、ターゲットになるプラットフォームの解像度を調べて計算してもいいのですが、ここは アンカー と アライメント を利用します。
キャンバスの適当な位置にドラッグ&ドロップして、Anchors の設定を変更します。
ついでに Is Variable にもチェックを付けて有効にしておきます。
このチェックは ブループリントから触るためです。
Position X を ゼロ にして、Alignment X を 0.5 にすると 画面の中央に配置されます。
Size To Content にチェックを付けます。
これは 内容物に応じて自動的にHorizontalBox のサイズが変動するようにするフラグです。
有効にすることで、Alignment の効果も効いて、常にセンタリングされるようになります。座標計算しなくていいし簡単!ありがとうUE4!
この HorizontalBox に ボタンWidgetを並べる方法は2つ。
あらかじめキャンバスにある程度の数を配置してしまう方法と、ブループリントで必要な数のみ動的に配置する方法があります。
事前にキャンバスに置く場合↓
ゲームの仕様によりますが最大数を8個としています。初期表示設定 は Collapse で。
Widgetブループリントから動的に必要な数だけ生成する場合 ↓
試しにこの2種類のWidgetを用意して Reference Viewer で確認してみるとどちらも同じ構成でした。(下図は 動的配置した場合のもの)
となるとWidgetのViewport配置時のスパイクが心配になるので、何度か再生してみた。
あらかじめ追加する方法は、保険の意味もあって8個分 Collapse の状態で仕込んでおいて、必要なボタンだけを表示してアクティブにしています。実際に表示するのは6個。こちら再生するたびにピークが上下するので、ちょっと不安定な印象。
一方のブループリントから動的に生成する法は安定して同じカーブになる印象。
GPU(黄色いライン)にわかりやすく差が出ました。 環境やらプロジェクトによって同じになるかわからないのと、検証の方法が合ってるか自信がないので情報をお持ちの方はそのあたりを共有していただけると嬉しいです。
ということで、ひとまず動的に生成する方法について書いていきます。
ブループリントから、他のWidgetアセットを生成するのは、
Construct Object from Class ノードを使います。
このノードは名前が変わるので、スクショでの説明が難しいやつです。
取り出した直後 ↓ Object~ が NONE(設定無し)になっています。
前回の記事のWidgetをセットしたところ ↓
このノードで召喚されたWidgetが Return Value から出てくるので、あとはそれを HorizontalBox に子供として追加します。
この流れを ForLoop ノードで表示したい数ぶん繰り返せば並びます。
前回の記事で、レベルブループリントで簡単な表示テストをつなぎました。
そこの Create Widget にセットしたWidgetを 親Widgetに差し替えます。
再生してみると並んでいるのが確認できます。
スキマもいい感じでバッチリ。
さすがに味気ないので、ボタンのアイコンやラベルを管理する部分を作っていきます。
ゲームのメカニクスやプレイサイクルなどは、なかなか最初から完成しているというのは少なくて、やっぱりスクラップ&ビルドによって育てていくのがゲーム開発の常です。特にUI は遊び方が変われば、そっくり作り直しなんてこともしょっちゅうです。デザインを差し替えるだけならまだましですけどね。
よし遊びができたぜ! となっても今度は、ゲームの進行に合わせてUIが変化する仕組みも必要になることがよくあります。キーアイテムを拾ってない、まだレベルが解放されていない、魔法を覚えていない、などなど。最初から見せるのもモチベにはなりますが、たいていはネタバレになってワクワクが減るし、解放された感を無駄に強めにアピールする必要があったり、また情報が多いのでUIの学習コストが高くなります。
というわけで、選択肢の数と状態、並び順をコントロールできる仕組みを考えてみました。
ストラクチャ(User Defined Structure)を用意します。
アセットブラウザで右クリック > Blueprints > Structure を選択します。
ダブルクリックすると専用のエディタがポップアップします。
New Variable ボタンをクリックして必要な数の変数を定義します。
今回用意したのは以下の4つ
ID ・・・ 選択肢の内容を特定するための管理番号 Integer型
ItemLabelText ・・・ ボタンのラベル名 Text型
IconIndex ・・・ アイコンの絵を指定するIndex番号 Integer型
isEnable ・・・ 有効か無効のフラグ Boolean型
必要最小限だと思いますが、あとはお好みで。
次にアイコンテクスチャを用意します。
解像度が256x256px で アイコンひとつの大きさが 64x64pxです。
これも最初からデザインと並びがきっちり管理できていれば問題ないですけど、やはり途中で意味が合わなくなって使わなくなったり、追加で増えたりするので、並び順と管理番号が噛み合わなくなることがよくあります。テクスチャ容量も有限ですし、欠番を空けたままというのも勿体ない話。なので、ここはデザイナー都合で並べることにします。
これをインポートして Grayscale テクスチャにします。
インポートできたら、このテクスチャからマテリアルを用意します。
アセットブラウザのアイコンの上で右クリック > Create Material を選択。
マテリアルエディタが開きます。
ノードはこのようになります。
アイコンの大きさは一律なので、TexCoordノード(赤いやつ)の Tiling 値はUとV共に 0.25を入れてあります。
ブループリントからアイコンのIndex番号のみを受け取って計算するようにしています。もしGPUの負荷をちょっとでも減らしたい場合は、ブループリントの方でUV座標を用意して渡すようにします。
今回は、マテリアル内で計算するタイプを採用します。
このマテリアルを、ボタンWidget の Imageパーツにセットします。
セットする場所は、Appearance の Brush のところに アセットブラウザからマテリアルをドラッグ&ドロップするとセットできます。
ボタンの状態をアニメーションで用意します。
尺はなくて、色をけるだけのキーがあるだけです。
以下の2つはアニメーションをつけています。
まずは決定時の余韻。
選ばれなかった未練。
変数を5つほど追加。
変数を用意できたところで
ボタンをセットアップするためのカスタムイベントを用意します。
Inputs にピンを2つ追加します。ひとつは用意したストラクチャの型を選択。
ストラクチャ →
この追加したピンからドラッグして、Breakノードを取り出します。
このBreakノードで、中の変数に対して個別にアクセスできるようになるので、受け取った値を処理していきます。
最初の始めのほうにあるInteger型の変数はここで保存しておいて、決定処理をした際に親のWidgetに通知するときに使います。
右端のブランチノードはBoolean型の変数 isEnable が false のときに DISABLE のアニメーションを再生するようにつなぎます。基本 true なので、false のときだけ処理します。
作ったアニメーションを再生するためのカスタムイベントを用意します。
下段の2つは、ボタンをクリックした後のイベントになります。
決定時だけいつもと違う再生ノードを使っています。
これは最近のUE4で追加されたノードで、アニメーションの再生終了を待って処理を行いたいときに便利です。ここでは、イベントディスパッチャーをつないで、親Widgetにアニメーション終了を通知しています。
クリック処理は onMouseButtonDown という関数をオーバーライドして作成しますが、ちょっと判定がややこしいので今回は説明を省きます。
ボタンWidget(子)はいったんこの辺にしておきます。イベントディスパッチャーやフラグの取り扱いは、コンテンツをダウンロードして確認していただけると助かります。
次に、親Widgetを編集していきます。
まずは変数を新たに2つ用意。
デバッグ用にマクロを用意します。DebugButtonDataと命名。
ボタンを6個分用意しています。
左のでっかいノードたちは、順番に取り出していくとスムーズに取り出せます。
まずストラクチャ型の変数(あらかじめ配列にしておく)を Set のかたちで取り出します。その左のピンから、ドラッグして "make" で検索。Make Array ノードを見つけて取り出します。Add pin + ボタンを必要な数だけクリックしてピンを増やします。
今度もそこからドラッグして "make" で検索。 Make menuButtonData を選びます。
あとは複製(Ctrl + W)
このMakeノードを配列に入れた数が画面に並びます。そこで数を調べて変数に保存します。
このマクロをボタンを並べるForLoop の前に置きます。
ボタンを生成する部分を改造します。
それぞれ並んだボタンをブループリントから処理するために、ボタン型の配列を作ってセットしています。
作り方は Construct ノードの Return Valueピンからドラッグして Promote to variable を選択。
できた変数を適当にリネーム。今回は Buttons と命名。(複数形にすると配列であることがわかりやすい気がする)
Details(詳細)タブ から、配列に変更します。
右端の部分。
子Widgetの持つ関数やイベントにアクセスするには、Construct ノードの Return Valueピンからドラッグして検索すると出てきます。 このときUE4のエディタを英語環境にしていると、"call" で簡単にリストアップできます。
具体的な名前を憶えていなくてもこれで探せるので私はこのために英語環境にしているといってもいいくらいです。
試しに再生してみます。
グレイマン イイケツしてんな~
ではなく、無事アイコンとラベルがセットできました。
並べ替えや追加も簡単です。
さて、あとは入退場の動き作って、クリックされたら決定して、また次に備えて・・・
という流れを作っていくことになります。
汎用性とか柔軟性とか拡張性、そういったことを考えるとUIの実装難度はどんどん上がっていきます。さらに仕様バグが出やすくなるのでチェックコストも増えます。手間やら気遣いやらを考えると、作る側はあまり報われないのがUIなのかと、つい思ってしまいます。おっと目から水が・・・
とはいえ、ここっていうタイミングでそっとハンケチを差し出すようなUIが作れたら、それこそが UI 作ってて良かった、という喜びみたいなもので心が満たされるのです。
ひとまず、記事ではここまでの解説にしようかと思います。
なかなかのボリュームにもかかわらず読んでいただきありがとうございます。
途中で放り出しやがってけしからん!という方は以下のリンクからダウンロードして、お使いのUE4のプロジェクトフォルダの Content/Developers フォルダに解凍すると、エンジンで触れるようになるはずです。Verは 4.25.3 です。
クリックすると下のようなポップアップが出ますが無視して問題ありません。
不明な点や不具合等あれば、ブログのコメント欄かTwitterにてツッコミいただければ対応します。
ではでは
ステキなメニューライフを!