3D空間に浮かぶHPバー
課題
3Dゲームオブジェクト(敵キャラクター、プレイヤーキャラなど)用に、フローティング表示の「HPバー」をつけたい。
解決策
この解決策として、既存の TextureProgressBar ノードをベースにした 2D HPバーを再利用します。すでにテクスチャが設定されており、値と色を更新するためのコードも実装済みです。既に同様のシステムをお持ちの場合は、それをそのまま使用していただいて構いません。サンプルではこのシーンを「Healthbar2D」と名付けます。
必要なアセットがある場合、バーで使用している以下の3つの画像を紹介します。
既存のオブジェクトを再利用すれば、大幅に作業時間を節約できます。HPバーやカメラ、その他一般的なコンポーネントが必要なたびにゼロから作り直す必要はありません。
プロジェクト設定
例として使用する「モブ」の開始点として、CharacterBody3Dノードを設定します。このノードは自動で出現し、直線的に移動するプログラムが組まれています。また、以下のコードでダメージ処理を実装します。
func _on_input_event(_camera, event, _position, _normal, _shape_idx):
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
health -= 1
if health <= 0:
queue_free()
単位をクリックするたびに1ダメージが与えられます。合計10ダメージを与えると、そのユニットは破壊されます。この状態を2Dバーを使って視覚的に表現する必要があります。
2Dを3Dに変換
Sprite3Dを使用することで、2D画像を3D空間で表示することが可能です。新しいシーンに追加し、「Healthbar3D」という名前を付けます。まず設定とサイズ調整を行いますので、 Texture プロパティに緑色のバー画像を設定してください。
Sprite3Dは通常の3Dオブジェクトと同様に動作します。カメラを移動させると、視点が変わるためです。ただし、HPバーは常にカメラの方を向くようにして、いつでも確認できるようにしたいです。
インスペクターで、 Flags セクションの Billboard を「Enabled(有効)」に設定してください。
続いてカメラを動かして、テクスチャが常にプレイヤー側を向いているか確認してください。
このシーンのインスタンスをMobシーンに追加し、バーをモブの体の上に配置してください。
ビューポートテクスチャ
Sprite3D ノードが静的なテクスチャを表示するのではなく、TextureProgressBar を表示したいです。これは、テクスチャをエクスポートできる SubViewport ノードを使用することで実現できます。
以下の手順で操作してください。
SubViewportをSprite3Dの子要素として追加してください。- インスペクタウィンドウで、Transparent BG 設定を オン に設定してください。
さらに、HPバーテクスチャのサイズに合わせてビューポートのサイズを設定する必要があり、そのサイズは(200, 26)です。
インスタンス化する際に、HealthBar2D を Viewport の子要素として配置してください。シーン構成は以下のようになるはずです。
もし SubViewport が Sprite3D の子要素でなかった場合、インスペクター上で直接スプライトのテクスチャとして設定できます。しかしこれは子要素であるため、適切なタイミングで準備が整っていない可能性があります。そのため、以下のように Sprite3D にアタッチされたスクリプト内で設定します。
extends Sprite3D
func _ready():
texture = $SubViewport.get_texture()
作ったものを統合しよう
モブの _on_input_event() メソッド内で、HPを減少させた後に以下を追加してください。
$HealthBar3D.update(health, max_health)
以下の内容を HealthBar3D.gd に追加してください。
func update_health(_value, _max_value):
$SubViewport/HealthBar2D.update_health(_value, _max_value)
このコードは、2Dバーに既に存在するupdateメソッドを呼び出しています。進捗バーの値を設定するとともに、バーの色を選択しています。
func update_health(_value, _max_value):
value = _value
if value < _max_value:
show()
texture_progress = bar_green
if value < 0.75 * _max_value:
texture_progress = bar_yellow
if value < 0.45 * _max_value:
texture_progress = bar_red
クリックしてモブのHPゲージが変化する様子を確認してください。
まとめ
このテクニックを使えば、Node2Dノードはもちろん、Controlノード全般(例:LabelやVideoStreamPlayerなど)を3D空間に表示できます。さらに、SubViewportを使えば、2Dゲーム全体を3D空間に「投影」することもできます。
プロジェクトのダウンロード
プロジェクトコードはこちらからダウンロードできます。https://github.com/godotrecipes/3d_object_healthbars