シェーダーでいろいろ作ったゲージをすぐに試せるように、簡単なゲージテスト用プロジェクトの作り方について書いてみようと思います。アーティストでブループリントを普段あまり触らない方向けの内容にしようと思うので、操作方法についてはいつもよりは細かめに書くつもりです。(後半はそれなりに端折ります)
ゲームの UIってプログラマ(エンジニア)に実装してもらわないと動きの確認が難しいですよね。場合によっては事前に仮組みされたUIが実装されていて、デザインをブラッシュアップするだけということもあるかと思います。なかなか自分で考えた最強のUIを試せないと自信もつかないし、デザインを推すこともためらいがちに。というわけでアンリアルエンジンを使ってDIYできる環境を整えていきましょう。
一応前置きさせてください。
UIの実装方法はゲームによって、またプログラマによって違いがでるものなので、これが唯一の答えではないです。無責任に聞こえますが、あくまでもゲージのふるまいをテストするためにできるだけ簡単な手順で環境をつくることを目的に試行錯誤しました。
実際のゲームで動かすとなるととても地味で手間のかかる管理構造が必要となります。そのあたりはケースバイケース過ぎるのであくまでもテスト用として見ていただけると助かります。このサンプルで試しながら、どういったパラメータ制御が必要だろうか、とかテクスチャの容量とかを実際に触ってから現場で相談すると自分の狙ったデザインが実装できる近道になると思います。
使用したエンジンのバージョンは 5.1.1 です。手持ちのPCスペックとストレージの都合でアップデートできていないのが実情。いやはや悔しい限りです。
個人的な経験になってしまいますが、機能がなくなったりしない限りは多少ノード名が変わっていたりするかもですが、プラグインやアドオンも使っていませんし、フツーと思しき使い方で作ることができているので、近縁のバージョンならそれほど差分はないかと思います。たぶん。より便利になっている可能性はあります。
あと、エディタの言語は個人的に英語のほうがなにかと作業効率がいい気がしています。なのでスクショは英語版です。ローカライズ(というかカルチャライズ?)に触れる機会として考えると少しはポジティブになれるかと思うのだけれど、どうかな?
ではさっそく
エンジン起動
プロジェクトブラウザがポップアップしたら、
左のカテゴリバナーからGAMES をクリックし中央のサムネイルから First Person を選択。Starter Content のチェックはつけたままで。
適当なプロジェクト名を付けたら、Create ボタンをクリックします。
エディタが起動したら
まずはコンテンツブラウザを開きます(Ctrl キー押しながら Spaceキー)
画面左下の Content Drawerボタンでも開閉できます。
All > Content > FirstPerson
ひとまずこの FirstPerson フォルダ以下に いろいろとアセットを追加していきます。
概要
再生してみるとわかりますが、
この1人称シューティングのテンプレートは、ステージを移動しながらショットが撃てます。撃った弾はステージに置かれている青いキューブを弾き飛ばします。それ以外の地形は弾をはじくだけで変化はありません。
そこで、青いキューブ個別に体力値を持たせて、弾が当たると体力値が減るようにします。
その体力値をゲージとしてキューブの上に表示します。
ついでにキューブに名前も付けられるようにします。
動いているサンプルを X にポストしています
体力ゲージ をテストするためのサンプルを簡単に作れないかといろいろお試し
— みつまめ杏仁 (@MMAn_nin) 2024年11月17日
Widgetをプールせずに使いまわしてるので画面内に一つしか表示されないけど、いい感じにできたと思う pic.twitter.com/a2ZFJyIrui
流れについてもう少し細かく書いてみます。
弾が当たったキューブは、当たったことを検知して相応の物理現象をシミュレートするように動きます。当たったことをエンジンは検知できるわけです。
その検知のタイミングで、キューブは自身の残体力を記録していて「HUD」に通知します。Widgetブループリントを管理する「HUD」は、通知を受け取ってゲージ用のWidgetブループリントにゲージの更新を指示します。
HPやMP、スタミナなど、ゲームプレイに関係するステータスの管理は実際はもっと複雑です。専用のマネージャクラスを用意したりするのですが今回はそういったコア部分の処理はスキップして、シンプルにゲージのふるまいを楽しむのを目的としています。
ということで今回作るのは3つ(ゲージのマテリアルは過去記事参照で)
HUDに 「 」 をつけて書いていますが、これは世間一般で語られるHUD(HeadUpDisplayの頭文字。本来の意味から拡張されてゲーム画面に表示されているものをザックリと命名した言葉として一般化してると思われる)ではなく、特殊な扱いのアセットを指す言葉として区別するのが理由です。
アンリアルエンジンでは、UI表示を管理する目的の HUDクラスという便利なものが用意されていてるので、それを利用します。
アセットが先にあると作りと説明がスムーズに進むので、ゲージのWidgetブループリントから作っていきます。
Blueprints フォルダを開きます
コンテンツブラウザの左上にある +Add というボタンをクリックするか、アイコンのない空いているところで右クリック
右クリックメニューから、User Interface > Widget Blueprint を選択
下のようなダイアログが出てくるので
UserWidget を選択①
Selectボタンをクリック③ するとコンテンツブラウザにアイコンが現れます。
適当に命名します。とりあえず WB_GaugeTest としました。
さっそく編集していきます
まずは Hierarchy または Canvas に 次の3つを配置します
Overlay
Image
TextBlock
Paletteパネルから探してマウスでドロップすると追加できます。
Hierarchyは下のような構成
順番はマウスドラッグで上下を入れ替えることができます。
Hierarchyの順番が描画順になります。
Overlayパネルは Canvasパネルと違って、画面内のポジションを相対的に扱います。
今回作るゲージは画面内のキューブを追随する形で表示するのと、ゲージが主役なので、中央に配置します。
Overlayパネルの子供にすると、Padding値で位置を調整することになります。テキストはおまけで名前を表示するために、ゲージから少し離れた位置に置いています。
TextBlock は置いただけだと、ブループリントから内容を書き換えることができないので、Is Variable を有効にしておきます。Detailsパネルの一番上にあります。
よく忘れるので、最初から有効にしておいてほしいけど、それだけ負荷が懸念されているということでしょうね。必要最小限だけにしてくれということでしょう。
ゲージの大きさは Brush > Image Size で設定します。
Overlayパネルの子にする場合とCanvasパネルの子にする場合でサイズの指定方法が少し変わります。
Image Size のすぐ上にある、Image のところにゲージ用に作ったマテリアルをセットします。
セットの方法はいくつか用意されています。
- セットしたいマテリアルをここへドロップする
- プルダウンリストから探す
- コンテンツブラウザでフォーカスしてから白い矢印をクリックする
- ブループリントから差し込む
とりあえず下のようなゲージマテリアルを別途用意しました。
固定の2色でアルファ無し、ScalarParameterがひとつ ”Value” と命名
これを Image にセットすると下図のようになります。
Widgetブループリントは2種類の編集画面を持っています。
デザインを作る Desinger と ブループリントを編集する Graph です。
レイアウトが完了したので、ブループリントの編集画面に切り替えます。
左の My Blueprintパネルに VARIABLES という項目の中に、さきほど配置したImageとTextBlock の名前が並んでいるので、グラフにドロップします。
指をマウスのボタンから離すと小さなポップアップが現れるので、 Get~ のほうを選択します。
TextBlockのほうは後で使うのでいったん脇にやっておきます。
まずは Imageのほうから。ドラッグして適当なとこで指を離すとノード検索ウィンドウが開く①
検索フォームに "dyn" と入力② ヒットした Get Dynamic Material を選択 ③
ノードを取り出したら、Event Construct の赤いノードとつなぎます④
Get Dynamic Material ノードの Return Value ピンからドラッグ⑤
Promote to variable を選択 ⑥ (確か日本語だと 「変数へ昇格」だったかな)
新しく変数が作られて その Setノードがつながるので、名前を変えておきます。
この方法はマテリアルに何度もアクセスする場合に効果的でます。また 都度 Get Dynamic Material するよりグラフがシンプルになります。
次にカスタムイベントを用意します。
グラフの空いているところで右クリックして Add Custom Eventノードを探します。
updateGauge と命名。HUDから呼び出す名前になります。
ここに先ほど Get Dynamic Material ノードで作った変数 mat_Gaugeを グラフにドロップして、 Set Scalar Parameter Value ノードを検索して取り出します。
Parameter Name のフォームには、 ゲージのマテリアルで用意したScalarParameter に命名した名前を入れます。
脇にやっていた TextBlock も SetText(Text)ノードを検索して取り出したら上図のようにつなぎます。
仕上げです。
Value と In Text のピンは値の入力を必要とするので、カスタムノードの上にドラッグ&ドロップするとつながってくれます。
カスタムイベントのノードに前もってピンを追加することもできますが、結構便利なので紹介しました。
このカスタムイベントを「HUD」が呼び出します。
その時 名前とゲージの割合 0~1.0 を渡してくれる仕様です。
Widgetブループリントは、ゲージのマテリアルに受け取った値を渡すとゲージの長さが変わります。
これで Widgetブループリントは完成です。
Compile ボタンをクリックして
?マークが OKになったら
保存して閉じます。
オリジナルの「HUD」を作る
Blueprints フォルダを開きます
コンテンツブラウザの空いているところで右クリック
コンテキストメニューがポップアップするので、Blueprint Class を選択。
次のようなダイアログがポップアップします。
上図のように ALL ClASSES が開かれていない場合はクリックして開きます。
検索フォームに "HUD" を入力① 出てきたHUDを選択②
Selectボタンをクリック③ するとコンテンツブラウザにアイコンが現れます。
適当に命名します。
ダブルクリックして編集します。
エディタが開いて編集モードが Viewport になっている場合は、Event Graph に切り替えます。
さっそく Create Widget ノードを取り出します。
グラフの何もないところで右クリック、検索フォームに "create w" と入力 ①
"widget" と最後まで入力しなくても出てきてくれます。
試しに "cr w" と入力しても大丈夫でした。
出てきたノードを Event BeginPlay ノードと接続 ④
Class のところに用意しておいた Widgetブループリントをセット⑤
Return Value ピンからドラッグして Promote To Variable ⑥
できた変数に名前を付けます。
この Event BeginPlay のイベントとしては
Widgetブループリントを実体化して変数の格納するとこまで。
つぎにゲージを表示するイベントを用意します。
まずは 今変数化したばかりの WB_GaugeTest をグラフの空いているところに取り出します①
取り出したノードのピンからドラッグして② 検索フォームに "call" と入力 ③
UpdateGauge というカスタムイベントを選択④
これ英語環境で使える方法なんですが、 "call" (コール)というワードで検索するとイベントとか関数を見つけやすくなります。適当な名前で作っていてもこれで思い出しやすくなるのでとても重宝します。オススメです。
続けて今度は Add to Viewport ノードを取り出してつなぎます。
WidgetブループリントをViewport(画面)に描画してくれるやつです。
次に変数を追加します。
ゲージを対象のオブジェクトにくっつけるのですが、その対象のオブジェクトの居場所を特定するためのものです。
エディタ左のほうにある VARIABLES の項目に プラスボタンがあるのでクリック。
すぐ下のリストに新しく "New Var" が追加されるので名前を付けます①
とりあえず HitObject と命名
(下図は Detailsパネルで設定しています)
Variable Type を 変更します。プルダウンリストをクリックして②
検索フォームに "static comp" と入力③
Static Mesh Component が出てくるのでマウスを重ね④
小さなポップアップから Object Reference を選択⑤
その変数をグラフにドロップ。ここは Set ~ の方を選択。
(Altキー押しながらドロップでもOK)
さきほどの Add to Viewport ノードとつなぎます。
これをカスタムイベントにします。
グラフの空いているところで右クリックして Add Custom Event を探して取り出したら、下図のようにつなぎます。
名前は適当につけます
仕上げに EventTick を利用します。
用意した変数 HitObject に、先のカスタムイベントで Static Mesh Component の参照を受け取ったら、そのコンポーネントのWorld座標(ステージ内の座標)をViewportの座標に変換するノード Project World Location to Widget Position を利用します。
このノードでは カメラで捉えた3D空間内の座標を、2D画面の座標に変換された値がゲットできます。Set Position in Viewport に渡すことで、その場所にWidgetを表示できます。EventTick につなぐと、毎フレーム計算するので、カメラが動いて位置が変わっても追随することになります。
変数を作っても中身が空っぽだと、エラーになることが多いので、is Valid ノードでチェックします。HitObject に何かが入るのは、カスタムイベントを呼ばれたとき。
呼ばれなくてもEventTick は毎フレーム動くので空っぽの変数に対して座標を取り出そうしないように、Is Valid でチェックしています。Is Not Valid に何もつないでいないので、何もしないことになります。
Project World Location to Widget Positionノードは、座標変換の結果が画面内であるかどうかを Return Value のピンで返してくれるので、この結果を利用して判定します。
画面内であれば true → 座標を更新。
画面内になければ false → 見えないのに計算と描画を続けるのは処理の無駄になるので Remove from Parent ノードを使ってViewport から消します。
Add to Viewport したものは、Viewport の子供という扱いなので、親から削除すると画面から消えることになります。
最後の HitObject 変数を Set の形で何も値をつながないようにしているのは、中身を空にするという意図です。
これで HUDは完成。コンパイルして問題なければ保存して閉じることができます。
HUDの登録
できた オリジナルHUDは 登録しないと機能してくれません。
コンテンツブラウザにある BP_FirstPersonGameMode を探して編集
開くと下のような状態で開きます。
HUD Class を探して、自分の作ったHUDをセットします。
コンパイルして保存したら閉じてOK。
ダメージを受ける青いキューブ
次で大詰め。ゲージを表示するための青いキューブにダメージ処理を仕込みます。
適当なキューブを選択します。
その状態でメインツールバーからブループリントメニューを開いて①
Convert Selection to Blueprint Class を選びます②
そのまま 下の Select ボタンをクリック。
青いキューブは、ただのスタティックメッシュ(StaticMesh)で自身では何もアクション出来ない静的に配置されたメッシュモデルですが、いい感じにコリジョンと物理の設定がされているので利用します。この操作は配置されているスタティックメッシュを後からブループリントで取り込んでしまう方法です。
Selectボタンを押すとエディタがオープンします。
エディタをEventGraph に切り替えます。
変数を3つ追加します。右端の目のアイコンについては後述
用途については 上から
ゲージの上に表示する名前
最大体力
現在の体力
エディタ右のDetailsタブから設定を変更します。
Instance Editable を有効にします。
有効にすると、ステージに配置している状態で、個別に値を持たせることができるようになります。
CurrentVital は InstanceEditableを無効にしたので出てきていません。
ここに値を個別に設定できるので、将来的にステージにいくつも配置しながら表示名と最大体力をセットする想定です。
EventGraphに戻ります。
あとは不用意に書き換えてしまわないように Blueprint Read Only を有効にしたり、他のBPなどから触れないように、Privateを有効にしています。
次に、開始時に最大体力を現在の体力とします。
要するに満タンからスタートするようにします。
事前にHUDへのアクセスをしやすくしておきます。
DynamicMaterial作ったときと似た感じです。
Cast To ~ ノードは、登録した オリジナルのHUDに型を変換(キャスト)するノードです。
Get Player Controller は何となく HUDというものの存在を知っています。GetHUD でそのHUDについて取得しますが、あくまでもHUDクラスという大きなイメージです。詳しくアクセスするためにより具体的なキャストが必要になります。
キャストが成功すると MyHUDへのアクセス権のようなものが手に入るので変数化します。
次に何かが当たった時のイベントを用意します
エディタ左 Components のパネルから Static Mesh Componentを選択
グラフの空いているところで右クリック
Add On Component Hit を探して取り出します。
何かが当たるとこのイベントが呼び出され発火します。
体力を減らします。とりあえず CurrentVital から 200 減らして、CurrentVital に入力。
これで200ダメージ受けました。
これをHUDに反映させましょう。でもゲージは 0~1.0 の割合で変化するように作っています。そこでもうひと手間。
現在の体力 CurrentVital を 最大体力 MaxVital で割ると割合になります。
これをMyHUD から カスタムイベントを取り出してつなぎます。
(・・・名前変えるの忘れてるし)
HitObject にはComponentsパネルから Static Mesh Componentをドラッグしてつなぎます。
InText には Name 変数をドロップしてつないだら完成。
基本的な流れはできました。
ここでコンパイルが問題なければ保存して再生してみましょう。
銃を取りに前に進みます。
W, A, S, D キーで前後左右に移動、マウスドラッグでカメラの向き
銃を構えたら 左クリックで弾発射。
おや?なんだか右に寄ってる気がする。
ゲージの左端がキューブの中心座標に来ています。
とりあえずWidgetブループリントのレイアウトを調整して解決します。
編集モードを Designer にして
細かくパーツを移動するのも面倒なので、Hierarcheyパネル から 一番上のアイテムを選択。
次にエディタ右の DetailsパネルからPadding の項目を探して Left に補正値を入れます。
今回ゲージの長さを 240 に設定したので、その半分の 120 を左にずらしたいので -120
を入力します。
コンパイルして保存したら確認してみましょう。
いい感じになりました。
ゲージの長さなんて可変するのが当たり前。こんな決め打ちな調整はその場限りの対策で、エンジニアに嫌われます。どんな長さになってもいいように計算して設定する方法も載せておきます。
表示開始時にキャンバスのImageSizeを調べて計算しPaddingにセットします。
ゲージの長さが、動的に変わるのであれば、関数(Function)にしておくと再利用しやすくなります。
ちなみに Padding の Top に値をいれるとゲージの表示位置を上下に移動できます。
下図は Topに -160 を設定した場合。
ゲージが被さるのがイヤ とかいう場合に使えます。
ただ、カメラからの距離を考慮しないと、近づくと重なるし離れたら今度は空きすぎることになるので難しいです。
対象オブジェクトのどの部分の座標を取ってくるかによっても変わるので、一筋縄ではいかないです。この辺の話はまたいずれ。
さてさて
これだけでも十分ゲージの動きは確認できますが、もっとゲージ表示対象を増やしましょう。
再生を止めて、
さきほど、ブループリントでくるんだ青キューブのアセットをOutliner(アウトライナ)から選択して右クリック①
Browse to Asset を選択 ②
その状態で
他の青キューブを選択します。
右クリックしてコンテキストメニューから、Replace Selected Actors with を選ぶとリプレース可能なアセットとして選択できるようになっているので選択するとブループリント付きの青キューブになります。
名前と最大体力が設定できるようになっていたら成功です。
ダメージ量はひとまず 200固定なので、MaxVitalを調整するとゲージの減り具合が加減できます。
この方法でどんどん Replace していきましょう。
最後にレベルを保存することを忘れずに。
動かしてみると、体力がマイナスなっても止まらないことに気が付くと思います。
さすがに対策したほうがいいですね。
あとは
弾以外(プレイヤー自身)が当たっても当たったことになるので弾だけに反応してほしい。
弾が当たったことがわかりすくしたい。
体力がゼロになったらゲージを出さないようにしたい。
ちょっと跳ねすぎ。
などなど
結構記事が長くなったので、この辺にしておこうと思います。
わかりにくいとか、もっと詳しくとかあればコメント下されば追記します。
次回の記事で、対策とおまけの実装を書きます。
今年もあと1か月きりました もうひと踏ん張りしますかね。
ではでは
素敵なゲージライフを!