バージョン:Unreal Engine 5.0.3
実施期間:2022年11月9日~14日
やりたいこと
- ゲーム中にインベントリやプレイヤー情報、コンフィグにアクセスできるメインメニューの表示・非表示
- 特定のキー(ゲームパッドのボタン)で表示する
- 複数列・複数行のメニュー
- キーボード操作も考慮してマウスカーソルを表示する
- ゲームの一時停止はしない
メインメニューの定義について
Unreal Engineでメインメニューを実装する解説や動画を見ると、自分が考えているメニューとイメージしているものが違っていることに気がついた。
大半は小規模のゲームやFPSで採用されるような、項目が少なく、ゲームが一時停止するようなものになっている。
公式の出している解説もまさにそれ。
メインメニューを作成する _ Unreal Engine ドキュメント
メインメニューといえば、自分が想像していたのはRPGで画面全体が切り替わるもの。
今回はPSO2やFF14のようなゲーム画面に重なるUIを目指している。
(FF14ではメインコマンドと呼ばれている。)
タイトルメニューもポーズメニューもゲームや人によってはメインメニューと呼ばれていて、自分が作りたいものと違うものを指しているのが正直面倒くさかった。
実装の流れ
- コンテンツブラウザにフォルダを作成する
- 作成したフォルダ内にウィジェットブループリントを作成する
- ウィジェットを作成する
- キャラクターのブループリントでウィジェットの表示・非表示を実装する
手順1:ウィジェットを作成する準備
コンテンツブラウザ(画面下)で、コンテンツの下の階層にウィジェットを保存するフォルダを作成する。フォルダ名はWidgetにした。
作成したWidgetフォルダ内で、右クリックメニュー>ユーザーインターフェイス>ウィジェットブループリントを選択(Fig1)。
名前はMainMenuにした。
ウィジェットブループリントをダブルクリックすると別ウィンドウでウィジェットの編集ができる。
手順2:ウィジェットの作成
Canvas Panelを配置
画面左の「階層」に配置されていなければCanvas Panelを配置する。
配置してある場合、フルHD(1920×1080)のサイズ1280×720ピクセルで白の点線が表示される(※1)。
(※1)解説では白の点線または緑色の枠線が表示されたスクリーンショットを見ることがあると思うが、自分の場合はそれがなかった。
画像を見比べると、自分の画面にはCanvas Panelがなかったことに気づいて解決した(Fig2)。
Uniform Grid Panelを配置
Canvas Panelの下の階層にUniform Grid Panelを配置する。
Uniform Grid Panelは規則正しく要素を配置することができるらしい。
タテ1列やヨコ1列のメニューだと出番はないが、今回はタテもヨコも複数列の配置なので使うことになった。
【UE4】Widget紹介 Panel編 – トンコツ開発ブログ
出番が少ないせいか、解説がほとんど無い。
そのため当たって砕けろの精神で使ってみる他なかった。
後で設置するボタンは分割されたサイズにフィットさせるので、UIの大きさはUniform Grid Panelで指定することになる。
今回作りたいのはボタン一つがタテ70px、ヨコ140px。
それをタテ2列、ヨコ5列に配置したいので、Uniform Grid Panelの大きさはタテ140px、ヨコ700pxになる。
Uniform Grid Panelの配置は画面下の中央にしたい。
メダルの模様みたいなアンカーを動かすが、座標はともかく、解像度の変更に対応する方法は分かってない。
Unreal Engine UI での UMG アンカー _ Unreal Engine 5.0 ドキュメント
ボタンとテキストを配置
Uniform Grid Panelの下の階層にボタンを10個配置。名前を0~9までつけてみた。
このままではボタンは同じ位置に重なってしまっているので、座標を指定する。
Uniform Grid Panelに追加されたボタンには、画面右側にスロット(Uniform Grid Slot)の項目がある。
RowとColumnの値が配列番号(座標?)になっており、左から右・上から下に向かって数字が大きくなる。
例えば、左上のボタンは(0,0)、右上のボタンは(0,4)、
左下のボタンは(1,0)、右下のボタンは(1,4)になる(Fig3)。
このままではボタンが小さいままなので、割り当てた領域を埋めてもらいたい。
画面右側のUniform Grid SlotのHorizontal AlignmentとVertical Alignmentをそれぞれ幅・高さいっぱいになるようにする(Fig4)。
(これは海外ニキがフォーラムで自己解決したページで発見した。)
UMG – Uniform Grid Panel Button size_ – Development _ Programming & Scripting – Unreal Engine Forums
ボタンの色・透明度は画面右のアピアランス>Normal>Tintで変更できる。
マウスカーソルが乗った場合の色はアピアランス>Hovered>Tintの設定が反映される。
(Tintは「色合い」の意。)(Fig4)
ちなみに、全部のボタンを一括で設定する方法はある。公式のチュートリアルにもあるように、Ctrlキーで複数選択すれば一括で色を変更できるようになる。
テキストはボタンの下の階層に置く。
フォントはデフォルトのRoboto。
サイズは10。
色は白文字。
位置はボタンの下部かつ中央にした(Fig5)。
(ゲームで使用するフォントの選び方は課題。)
手順3:ブループリントでメインメニューの表示・非表示を実装
ここからブループリント(ノード)を触っていく。
ウィジェットのチュートリアルではゲーム開始時に実行するためにEvent Begin Playノードを使うが、今回は特定のキーでメインメニューを表示してほしいのでキーボードイベントノードを使う(※2)。
(※2)日本語環境だと検索欄に「keyboard」と入力してもヒットしなかった。カタカナで「キーボード」と入力するとヒットする(Fig6)。
ウィジェットの基本はCreate Widgetノードでウィジェットを(バックグラウンドで)作成し、Add to Viewportノードで画面に表示されるようになる。
非表示にするにはRemove from Parentノードを使うが、これはウィジェットを削除しているワケではないようで、メモリ上には残るらしい。
Remove from Parent で消えている状態で、CreateWidgetするのは問題ないです。中の変数は初期化されます。
危険なのは、Viewportに乗っている状態(消えていない状態)でCreateWidgetすること。CreateWidgetするたびに新しく生成されてしまいます。そして古い方は触れなくなるので画面に残り続けることになります。
Widgetを出したり消したり – みつまめ杏仁
また、ボタンに画像を使用することを考慮した場合など、ハード参照だと処理性能の低下を招くという知識を耳にした。
RemoveFromParentでWidgetを削除した後にGCを実行したとしても、ハードリファレンスされたWidgetがある場合はまだ再利用される可能性があるため(オブジェクトが完全に切り離されていない状態)GCの対象とはなりません。これはWidget(Slate)の既定動作であり、親Widgetが完全に削除されたり、OpenLevelなどによるシーンの移動が発生するまではリソースは開放されません。
[UE4] Widgetとリソースを切り離す方法 – Qiita
Unreal Engineを触り始めたばかりなのにメモリ管理を気にした俺はCreateWidgetとAdd to Viewportを分けて実装してみた。
正しいのかどうか分からないが、ウィジェットはゲーム開始時に用意してほしいのでEvent Begin PlayノードにCreate Widgetノードをつなぐ。
Create Widgetノードは作成したウィジェットの名前をクラスに指定する。
そして、ウィジェットの表示・非表示を別のフローで実行するためにウィジェットを変数化(?)する。
Create WidgetノードのReturn Valueのピンの上で右クリックすると「変数へ昇格」が実行できる(※3)。これでSetノードが接続される(Fig7)(Fig8)。
(※3)この方法に気づくまで30分かかった。Setノードは検索にヒットせず、読んでいた解説にも書かれていなかったから。
(実はノードのドキュメントには書かれていたりする。右も左も分からない最初のうちは読んでいる解説をふた周りは優しくしてくれないとこうやってつまづく。)
[Promote to Variable (変数へ昇格] を使用しても、変数の作成が可能です。
ブループリント ノードの任意の入出力ピン上で右クリック して [Promote to Variable] オプションを選択します。
ブループリント変数 _ Unreal Engine ドキュメント
メニューを表示・非表示にする処理はキーボードイベントから始まり、トグルスイッチのようにONとOFFを交互に繰り返すようにした。
これはFlipFlapノードで実現可能で、奇数回の処理ではAdd to Viewportノードでメニューを表示し、偶数回ではRemove from Parentノードでメニューを閉じるようになる。
これらのノードには対象(ターゲット)が必要なので、変数化したウィジェットとワイヤーでそれぞれ接続する。
また、キーボード操作を考慮してメニューを表示した際にはマウスカーソルも表示し、メニューを閉じた際はマウスカーソルを消すようにしたい。
マウスやキーボードの状態はGet Player Controllerノードが持っているらしく、そこからワイヤーを伸ばして検索するとShow Mouse Corsorがヒットするので接続する。
カーソルを表示する場合はチェックボックスをONにし、一連の処理をワイヤーで接続する(Fig9)。
これでプレビューを実行すると、Homeキーを押すたびに見た目のみのウィジェットが出たり消えたりする(Fig10)。
アニメーションを作っていないので、本当に出たり消えたりするだけ。
とりあえず他に解説がなかったUniform Grid Panelを使ったメニューの作成ができた。
補足
ウィジェットの非表示(Remove from Parentノード)をしないままさらにメニューを表示しようとすると、Unreal Engineが警告を出してきた(Fig11)。
これはゲーム中では問題がないのか、それともメモリを食っているのか?
ウィジェット’MainMenu_C’はすでに画面に追加されています
課題
- (未実装)メニューの遷移
- (未実装)メニューを開いた直後の状態(初期値)は左上のボタンを選択状態(ハイライト)にする
- (未実装)ボタンに画像(マテリアル?)を貼る
- (未実装)メニューが表示される際のアニメーション
- (不明)適切なゲームモード。メニューを開いた際、WASDキーとゲームパッドの左スティック(移動)・右スティック(カメラ操作)は有効にしたい