みつまめ杏仁

アンリアルエンジン(UE4)でGUIを作るためにゴニョゴニョしてます。UIデザイナーの皆様の助けになれば幸いです。

斜めになったメニューボタンを並べてみる《テスト編》

前回のつづき

f:id:hiyokosabrey:20200815200712g:plain

検証もできて満足したので、そのまま選択するとこまで作ってしまおうと勢いづいて作り込んでみました。

あらかたできた時点でTwitterに上げたのですが、このブログで記事にしていく途中で、いろいろ足りてないところを発見。不具合を直したりあれこれ触っているうちに、気が付けば それなりに機能の入ったメニューUI ができていました。

ゲームの UI は状況と仕様に合わせて柔軟に作っていくものなので、あくまでも一例として参考程度に流し見してくださると助かります。

 

いろいろメンテナンスとか、融通の利く作りを考えてたら、このようなアセット構成になりました。

f:id:hiyokosabrey:20200823122830p:plain

今回の記事では、とりあえず並べてボタンのアイコンとラベルをテスト表示するところまでを書こうと思います。

 

ひととおり動くやつをGoogleドライブにアップしましたので、興味のある方はこちらをDLして触ってみてください。

 

drive.google.com

 

 

 

さてさて

 最終的に扱うWidgetは前回作ったのと、今回作る2つ。

f:id:hiyokosabrey:20200823234129p:plain

命名が適当・・・)

 

 

ひとまず並べるための新しいWidgetブループリントを用意します。

前回の記事で作ったボタンWidgetを 子とするなら それを管理する 親Widget になります。

 

キャンバスに HorizontalBox を一つ、ドラッグ&ドロップします。

f:id:hiyokosabrey:20200823121833p:plain

 

画面中央に配置します。

f:id:hiyokosabrey:20200823001953p:plain

正確に中央に置くためには、ターゲットになるプラットフォームの解像度を調べて計算してもいいのですが、ここは アンカー と アライメント を利用します。

 

キャンバスの適当な位置にドラッグ&ドロップして、Anchors の設定を変更します。

f:id:hiyokosabrey:20200823002652p:plain

ついでに Is Variable にもチェックを付けて有効にしておきます。

このチェックは ブループリントから触るためです。

 

Position Xゼロ にして、Alignment X0.5 にすると 画面の中央に配置されます。

f:id:hiyokosabrey:20200823003135p:plain

Size To Content にチェックを付けます。

これは 内容物に応じて自動的にHorizontalBox のサイズが変動するようにするフラグです。

有効にすることで、Alignment の効果も効いて、常にセンタリングされるようになります。座標計算しなくていいし簡単!ありがとうUE4

 

 

この HorizontalBox に ボタンWidgetを並べる方法は2つ。

あらかじめキャンバスにある程度の数を配置してしまう方法と、ブループリントで必要な数のみ動的に配置する方法があります。

 

事前にキャンバスに置く場合↓

f:id:hiyokosabrey:20200823142511p:plain

ゲームの仕様によりますが最大数を8個としています。初期表示設定 は Collapse で。

 

Widgetブループリントから動的に必要な数だけ生成する場合 ↓

f:id:hiyokosabrey:20200823142456p:plain

 

 

試しにこの2種類のWidgetを用意して Reference Viewer で確認してみるとどちらも同じ構成でした。(下図は 動的配置した場合のもの)

f:id:hiyokosabrey:20200823140531p:plain

 

となるとWidgetのViewport配置時のスパイクが心配になるので、何度か再生してみた。

f:id:hiyokosabrey:20200823140655p:plainあらかじめ追加する方法は、保険の意味もあって8個分 Collapse の状態で仕込んでおいて、必要なボタンだけを表示してアクティブにしています。実際に表示するのは6個。こちら再生するたびにピークが上下するので、ちょっと不安定な印象。

一方のブループリントから動的に生成する法は安定して同じカーブになる印象。

GPU(黄色いライン)にわかりやすく差が出ました。 環境やらプロジェクトによって同じになるかわからないのと、検証の方法が合ってるか自信がないので情報をお持ちの方はそのあたりを共有していただけると嬉しいです。

 

ということで、ひとまず動的に生成する方法について書いていきます。

 

ブループリントから、他のWidgetアセットを生成するのは、

Construct Object from Class ノードを使います。

f:id:hiyokosabrey:20200823212423p:plain

このノードは名前が変わるので、スクショでの説明が難しいやつです。

取り出した直後 ↓  Object~ が NONE(設定無し)になっています。

f:id:hiyokosabrey:20200823212816p:plain

前回の記事のWidgetをセットしたところ ↓

f:id:hiyokosabrey:20200823213432p:plain

 

このノードで召喚されたWidgetが Return Value から出てくるので、あとはそれを HorizontalBox に子供として追加します。

この流れを ForLoop ノードで表示したい数ぶん繰り返せば並びます。

f:id:hiyokosabrey:20200823232741p:plain

 

前回の記事で、レベルブループリントで簡単な表示テストをつなぎました。

そこの Create Widget にセットしたWidgetを 親Widgetに差し替えます。

f:id:hiyokosabrey:20200823233743j:plain

 

再生してみると並んでいるのが確認できます。

f:id:hiyokosabrey:20200823233023j:plain

 スキマもいい感じでバッチリ。

 

 

 

さすがに味気ないので、ボタンのアイコンやラベルを管理する部分を作っていきます。

 

ゲームのメカニクスやプレイサイクルなどは、なかなか最初から完成しているというのは少なくて、やっぱりスクラップ&ビルドによって育てていくのがゲーム開発の常です。特にUI は遊び方が変われば、そっくり作り直しなんてこともしょっちゅうです。デザインを差し替えるだけならまだましですけどね。

よし遊びができたぜ! となっても今度は、ゲームの進行に合わせてUIが変化する仕組みも必要になることがよくあります。キーアイテムを拾ってない、まだレベルが解放されていない、魔法を覚えていない、などなど。最初から見せるのもモチベにはなりますが、たいていはネタバレになってワクワクが減るし、解放された感を無駄に強めにアピールする必要があったり、また情報が多いのでUIの学習コストが高くなります。

というわけで、選択肢の数と状態、並び順をコントロールできる仕組みを考えてみました。

 

ストラクチャ(User Defined Structure)を用意します。

アセットブラウザで右クリック > Blueprints > Structure を選択します。

f:id:hiyokosabrey:20200824234507p:plain

f:id:hiyokosabrey:20200824234704p:plain

 ダブルクリックすると専用のエディタがポップアップします。

New Variable ボタンをクリックして必要な数の変数を定義します。

f:id:hiyokosabrey:20200824235023p:plain

今回用意したのは以下の4つ

 

 ID ・・・ 選択肢の内容を特定するための管理番号 Integer型

ItemLabelText ・・・ ボタンのラベル名 Text型

IconIndex ・・・ アイコンの絵を指定するIndex番号  Integer型

isEnable ・・・ 有効か無効のフラグ Boolean型

 

必要最小限だと思いますが、あとはお好みで。

 

 

次にアイコンテクスチャを用意します。

解像度が256x256px で アイコンひとつの大きさが 64x64pxです。

f:id:hiyokosabrey:20200825000602p:plain

これも最初からデザインと並びがきっちり管理できていれば問題ないですけど、やはり途中で意味が合わなくなって使わなくなったり、追加で増えたりするので、並び順と管理番号が噛み合わなくなることがよくあります。テクスチャ容量も有限ですし、欠番を空けたままというのも勿体ない話。なので、ここはデザイナー都合で並べることにします。

 

これをインポートして Grayscale テクスチャにします。

f:id:hiyokosabrey:20200825001853p:plain

インポートできたら、このテクスチャからマテリアルを用意します。

アセットブラウザのアイコンの上で右クリック > Create Material を選択。

f:id:hiyokosabrey:20200825002547p:plain

f:id:hiyokosabrey:20200825153951p:plain

マテリアルエディタが開きます。

 

ノードはこのようになります。

f:id:hiyokosabrey:20200825002925p:plain

アイコンの大きさは一律なので、TexCoordノード(赤いやつ)の Tiling 値はUとV共に 0.25を入れてあります。

 

ブループリントからアイコンのIndex番号のみを受け取って計算するようにしています。もしGPUの負荷をちょっとでも減らしたい場合は、ブループリントの方でUV座標を用意して渡すようにします。

f:id:hiyokosabrey:20200825004020p:plain

 

今回は、マテリアル内で計算するタイプを採用します。

 

このマテリアルを、ボタンWidget の Imageパーツにセットします。

f:id:hiyokosabrey:20200825154757p:plain

f:id:hiyokosabrey:20200825154145p:plain

セットする場所は、Appearance の Brush のところに アセットブラウザからマテリアルをドラッグ&ドロップするとセットできます。

f:id:hiyokosabrey:20200825154303p:plain

 

ボタンの状態をアニメーションで用意します。

尺はなくて、色をけるだけのキーがあるだけです。

f:id:hiyokosabrey:20200825163701p:plainf:id:hiyokosabrey:20200825163711p:plain

f:id:hiyokosabrey:20200825163725p:plain

以下の2つはアニメーションをつけています。

まずは決定時の余韻。

f:id:hiyokosabrey:20200825163752g:plain

選ばれなかった未練。

f:id:hiyokosabrey:20200825163806g:plain

 

変数を5つほど追加。

f:id:hiyokosabrey:20200825201444p:plain


 

変数を用意できたところで
ボタンをセットアップするためのカスタムイベントを用意します。

 

f:id:hiyokosabrey:20200825202407p:plain


Inputs にピンを2つ追加します。ひとつは用意したストラクチャの型を選択。

f:id:hiyokosabrey:20200825202330p:plain


ストラクチャ → f:id:hiyokosabrey:20200824234704p:plain

 

この追加したピンからドラッグして、Breakノードを取り出します。

f:id:hiyokosabrey:20200825202534p:plain

 

このBreakノードで、中の変数に対して個別にアクセスできるようになるので、受け取った値を処理していきます。

f:id:hiyokosabrey:20200825210613p:plain

最初の始めのほうにあるInteger型の変数はここで保存しておいて、決定処理をした際に親のWidgetに通知するときに使います。

右端のブランチノードはBoolean型の変数 isEnable が false のときに DISABLE のアニメーションを再生するようにつなぎます。基本 true なので、false のときだけ処理します。

 

作ったアニメーションを再生するためのカスタムイベントを用意します。

f:id:hiyokosabrey:20200826233935p:plain

下段の2つは、ボタンをクリックした後のイベントになります。

 

決定時だけいつもと違う再生ノードを使っています。

f:id:hiyokosabrey:20200826234122p:plain

これは最近のUE4で追加されたノードで、アニメーションの再生終了を待って処理を行いたいときに便利です。ここでは、イベントディスパッチャーをつないで、親Widgetにアニメーション終了を通知しています。


クリック処理は onMouseButtonDown という関数をオーバーライドして作成しますが、ちょっと判定がややこしいので今回は説明を省きます。

 

ボタンWidget(子)はいったんこの辺にしておきます。イベントディスパッチャーやフラグの取り扱いは、コンテンツをダウンロードして確認していただけると助かります。

次に、親Widgetを編集していきます。

f:id:hiyokosabrey:20200827003333p:plain

まずは変数を新たに2つ用意。

f:id:hiyokosabrey:20200827003359p:plain

デバッグ用にマクロを用意します。DebugButtonDataと命名

f:id:hiyokosabrey:20200827004334p:plain

ボタンを6個分用意しています。

左のでっかいノードたちは、順番に取り出していくとスムーズに取り出せます。

まずストラクチャ型の変数(あらかじめ配列にしておく)を Set のかたちで取り出します。その左のピンから、ドラッグして "make" で検索。Make Array ノードを見つけて取り出します。Add pin + ボタンを必要な数だけクリックしてピンを増やします。

今度もそこからドラッグして "make" で検索。 Make menuButtonData を選びます。

あとは複製(Ctrl + W)

 

f:id:hiyokosabrey:20200827081802p:plain

このMakeノードを配列に入れた数が画面に並びます。そこで数を調べて変数に保存します。

f:id:hiyokosabrey:20200827082203p:plain

 

このマクロをボタンを並べるForLoop の前に置きます。

ボタンを生成する部分を改造します。

f:id:hiyokosabrey:20200827083836p:plain

それぞれ並んだボタンをブループリントから処理するために、ボタン型の配列を作ってセットしています。

作り方は Construct ノードの Return Valueピンからドラッグして Promote to variable を選択。

f:id:hiyokosabrey:20200827084446p:plain

できた変数を適当にリネーム。今回は Buttons と命名。(複数形にすると配列であることがわかりやすい気がする)

Details(詳細)タブ から、配列に変更します。

f:id:hiyokosabrey:20200827084432p:plain

 

右端の部分。

f:id:hiyokosabrey:20200827085335p:plain

Widgetの持つ関数やイベントにアクセスするには、Construct ノードの Return Valueピンからドラッグして検索すると出てきます。 このときUE4のエディタを英語環境にしていると、"call" で簡単にリストアップできます。

f:id:hiyokosabrey:20200827085717p:plain

具体的な名前を憶えていなくてもこれで探せるので私はこのために英語環境にしているといってもいいくらいです。

 

試しに再生してみます。

f:id:hiyokosabrey:20200827212953j:plain

レイマン イイケツしてんな~

ではなく、無事アイコンとラベルがセットできました。

並べ替えや追加も簡単です。

f:id:hiyokosabrey:20200827213124j:plain

 

さて、あとは入退場の動き作って、クリックされたら決定して、また次に備えて・・・

という流れを作っていくことになります。

汎用性とか柔軟性とか拡張性、そういったことを考えるとUIの実装難度はどんどん上がっていきます。さらに仕様バグが出やすくなるのでチェックコストも増えます。手間やら気遣いやらを考えると、作る側はあまり報われないのがUIなのかと、つい思ってしまいます。おっと目から水が・・・

とはいえ、ここっていうタイミングでそっとハンケチを差し出すようなUIが作れたら、それこそが UI 作ってて良かった、という喜びみたいなもので心が満たされるのです。

 

ひとまず、記事ではここまでの解説にしようかと思います。

なかなかのボリュームにもかかわらず読んでいただきありがとうございます。

途中で放り出しやがってけしからん!という方は以下のリンクからダウンロードして、お使いのUE4のプロジェクトフォルダの Content/Developers フォルダに解凍すると、エンジンで触れるようになるはずです。Verは 4.25.3 です。

drive.google.com

 

クリックすると下のようなポップアップが出ますが無視して問題ありません。

f:id:hiyokosabrey:20200827221552p:plain

 

不明な点や不具合等あれば、ブログのコメント欄かTwitterにてツッコミいただければ対応します。


ではでは

ステキなメニューライフを!