バージョン:Unreal Engine 5.0.3
実施期間:2022年11月15日~17日
やりたいこと
- 操作キャラの頭上にプレイヤー名(とHPバー)を表示したい
- プレイヤー名はプレイヤーデータ(構造体)に保存したものを取得したい
- いずれはNPCの頭上にキャラクター名を表示する仕組みにしたい
実装の流れ
- プレイヤーの様々なデータを保存した構造体を作成する
- プレイヤー名(とHPバー)を表示するウィジェットを作成する
- ウィジェットのテキスト部分を変更(代入)可能にする
- キャラクターBPにウィジェットコンポーネントを追加する
- キャラクターBPにプレイヤー名を取得し頭上に表示するまでのノードを組む
この回では複数の要素を編集し、任意に命名するものも多いので全体像を提示しておきたい。
手順1:構造体の作成
サンプルもドキュメントも活用できるものが見当たらなかったので、英語で検索して見つけた。海外ニキは最後の砦。
How to use Structs in Unreal Engine 4 – Couch Learn
画面下のコンテンツブラウザで構造体を保存するためのフォルダを作成した。名前はStructure。
Structureフォルダ内に構造体を作成。名前はPlayerDataにした。
構造体の中身はプレイヤー名以外はまぁ適当に(Fig2)。
(今の知識で考慮しているのは、プレイヤーのレベルは取得した累計経験値から逆算できるということ。だからレベルは保存する必要がないと考えている。)
構造体を利用できるようにするためには、(ここでは)キャラクターブループリントに任意の変数を用意し、作成した構造体の名前とタイプ(構造体であること)を指定する。
変数には構造体が丸ごと入る。
手順2:ウィジェットの作成とテキスト部分のバインディング
前回作成したウィジェット用のフォルダの中に、プレイヤー名とHPバーを表示するウィジェットを作成する。
(ウィジェット名は「HUD_DisplayPlayerInfo」にした。)
Canvas Panelの下の階層にTextとProgress Barを配置し、大きさや色を変更する。
Textは初期値として「PlayerName」にした(Fig3)。
Canvas PanelがフルHDのサイズでガイドしてくれているので(本当は1280×720)、必要な大きさを掴むのは難しくない(と思う)。
(実際は幅が150px程度でよかったものを300px程度で作ってしまったため、後の手順で0.5倍にすることになった。)
ウィジェットのTextの値にアクセスできるようにするために、バインディングを行う。
画面右のコンテンツ>Textの右側にある「バインド」を実行する。
ノードが表示されるので、リターンノードのReturn Valueピンからワイヤーを引っ張り、変数化する(Fig4)(Fig5)。
変数名はVar_PlayerNameにした。
手順3:キャラクターBPにウィジェットコンポーネントを追加
コンポーネントがどんな役割を持っているのか分からなかったが、公式ドキュメントによれば、「アクタに追加できる機能の要素」らしい。
公式のドキュメントには15種類のコンポーネントが紹介されており、個別のページが用意されている。
今回はWidgetコンポーネントを追加することで、作成したウィジェットを3D空間に表示するということになる。
キャラクターBPを開いて、画面左上の「追加」ボタンからウィジェットコンポーネントを追加する。
名前は付ける必要があるので、今回は「Widget_AboveHead」にした。
(WidgetConp~の方が良かっただろうか?)
アクタに表示させたいウィジェットを指定する必要があるので、画面右側のユーザーインターフェイス>Widget Classの値を作成したウィジェット(ここでは「HUD_DisplayPlayerInfo」)を指定する。
それと、今のままでは3D空間上に厚みのない平面のウィジェットが存在している状態なので、キャラクターが横を向いているとウィジェットが見えなくなる。
なので、画面右側のユーザーインターフェイス>Spaceの値を「World」から「Screen」に変更する必要がある(Fig6)。
そして位置を頭上に来るように変更し、必要があればTransformの値で拡大・縮小すること。(0.5倍にしたのはここ。)
手順4:キャラクターBPのイベントグラフで一連の処理をノードで組む
ここでノードをつなぐ説明をするために、ここまでに触ったオブジェクトや変数名をおさらいしておきたい。
- 頭上に表示するウィジェットの名前「HUD_DisplayPlayerInfo」
- テキスト部分をバインディングした際にリターンノードから変数に昇格したもの「Var_PlayerName」
- TPSキャラクターのブループリントに追加したウィジェットコンポーネント名「Widget_AboveHead」
- プレイヤーデータを格納することを想定した構造体の名前「PlayerData」
- TPSキャラクターのブループリントに追加した、構造体を収めた変数名「PlayerInfo」
要件が似ている解説を読むと、Cast toノードでウィジェット(のテキスト部分)にアクセスできるようにしているようだ。
キャラクターブループリントでの話なので、キャスト元のオブジェクトはアクタに追加されたWidgetコンポーネント(ここではWidget_AboveHead)であり、キャスト先はウィジェット本体(HUD_DisplayPlayerInfo)になる。
ブループリント で Cast To ノードは、簡単に言うと、キャスト元のオブジェクトがキャスト先の特定のオブジェクトであるかを確認する時に使用します。つまり、その中に変数または他のカスタム機能を持つ特殊な Character ブループリント (例えば、MyCharacter と呼ばれるもの) を作成し、それをデフォルトの Pawn クラス (またはすべてのプレイヤー キャラクターがデフォルトで使用する Character ブループリント) として割り当てたとします。
プレイヤー キャラクターのプロパティに別のブループリントからアクセスするには、Get Player Character ノードを使って一般的な方法 (位置、回転などを設定/取得するなど) でプレイヤー キャラクターに影響を及ぼすことができます。しかし、MyCharacter ブループリントに追加設定したカスタム機能にはアクセスできません。プレイヤー キャラクターを get していても、特定のタイプのキャラクター タイプは get していないからです。
Get Player Character ノードを使用し、次に Cast To MyCharacter ノード (特殊な Character ブループリント) を使用すると、プレイヤー キャラクターは MyCharacter であり、ブループリント内の変数、関数、イベント、その他の特殊な機能にアクセスしたい旨を示すことができます。
ブループリントでキャストする _ Unreal Engine ドキュメント
Get User Widget Objectノードを設置して、左上のWidgetコンポーネントをドラッグ&ドロップしたノードをつなぐ。
Cast toノードは検索で出すことになるが、ここでウィジェットの名前が必要になる(ここではHUD_DisplayPlayerInfo)(※)。
もし検索にヒットしない場合、小窓の右上にある「状況に合わせた表示」のチェックを外す(Fig7)。
(※検索すると「ウィジェット名」と「ウィジェット名 クラス」と書かれた2つの候補が表示されるかもしれない。元にした解説には特に書かれていなかったので、「クラス」と書かれていない方を選択した。)
(ノードには作者が定義した変数名やオブジェクト名が表示されるので、解説と異なる名前をつけていた場合は検索にヒットせず手間取る原因になると思った。名前を振り返ったのはそのため。)
ウィジェットが持っているテキスト部分の変数にアクセスしたいので、Cast toノードの青いピン(As ウィジェット名)からワイヤーを伸ばし右クリックで検索。
変数にセット(代入)したいので、Set Var_PlayerNameを選択する(Fig8)。
ここに構造体に入っているプレイヤー名を与えれば一連の処理は完成した(Fig9)。
構造体をノードにするには、手順1で用意した画面左側の変数一覧からドラッグ&ドロップする。
このままではSetノードの変数に繋げないので(1敗)、構造体を入れたノードの上で右クリックし、ピンを分割(Split)する。
「構造体名 PlayerName」のピンとSetノードの変数を接続すると、自動的にTo Text(String)ノードが追加された。
見ての通り、String型のプレイヤー名をテキストに変換しているんだろう。
プレイヤー名の表示はゲーム開始時から行って欲しいので、Begin Playノードとつなぐ。
これで一連の処理は完成し、プレビューするとキャラクターの頭上に構造体に登録していたプレイヤー名(ここでは「Player1」)に置き換わっている(Fig10)。
補足
(1)構造体のPlayer名を格納する変数の型はString型にしたが、Name型はどうなんだろうか。
Name型は処理が軽いらしいが、適切な使い道も知らずに多用するのは控えた。
(2)Event Begin Playノードには、前回作成したメインメニューの表示(Create Widgetノード)をつなぎたかったが、一つのピンから分岐することはない? 同時に接続できなかった。
ゲーム開始時から複数の処理を走らせたいことは多くあると思うので、このあたりがまだ分かってない。
追記 – 2022年11月24日
(2)について、ググっていたら「Sequenceノードを使えばいい」という情報があったので、調べてみたら解決できた。
Event begin playについて – Programming & Scripting _ Blueprint – Unreal Engine Forums
Sequenceノードを間に噛ませると、複数のノードを並列につなぐことができると分かった。
予想では、それぞれの処理を関数化して串に刺した団子のようにノードを接続するのかと思っていたが、その必要はなさそうだ。
課題
いずれは、これをNPCの頭上に名前を表示する仕組みにしたい。
まだ想像だが、NPCの雛形にこの処理を入れておけば、継承(インスタンス化?)してNPCを管理するリスト(構造体)から自動で名前を引っ張ってくる処理ができそうな気がする。
ゲーム開発経験がないから分からないが、大量のウィジェットをそれぞれのNPCに表示させるのは負荷的にどうなのだろうか。