バーチャル3Dクリエイター神部まゆみです(*^_^*)
この記事はUnityで敵体力HPゲージを実装したので、それについての記事です。
動作確認した最新バージョンは Unity 6000.3.1 です。
やりたいこと:攻撃したら敵体力ゲージを表示させたい
前回↓こんな感じで攻撃したらダメージが表示されるようにしたけど、敵の体力ゲージも表示させたい。
攻撃した時だけ表示されるようにして、一定時間で消えるようにしたいですね。
ChatGPTパイセンに聞いてみる
彼に聞けばたちどころに答えてくれるに違いない…。




次は敵の体力ゲージを表示したいです。普段は非表示で、こちらが攻撃してダメージを与えた時に見えるようにしたいです。デザインはシンプルな横のバー形式で、最大HPがわかりやすく半透明で表示されていると理想的です。あまり大きいと邪魔になってしまうためほどほどの大きさでお願いします。これもUIワールド空間でいけるのでしょうか。


いいですね、その仕様はTPSだと王道かつ「気持ちいい」やつです 👍
結論から言うと UIワールド空間でバッチリ実現できます。
しかも構成をちゃんと分ければ、敵味方どちらにも流用できます。
まず結論まとめ
- ✅ World Space Canvas でOK
- ✅ 普段は非表示、ダメージを受けた瞬間に表示
- ✅ 最大HPが分かる 半透明の背景バー
- ✅ HP量に応じて 前面バーが減る
- ✅ サイズは小さめで敵の頭上に表示
全体構成(これが完成形イメージ)
Enemy
├─ Collider
├─ EnemyHealth.cs
└─ HPBar (Canvas : World Space)
└─ HPBarRoot
├─ Background (Image 半透明)
└─ Fill (Image HP量)
① HPバー用UIを作る(World Space)
手順
- Enemyの子に Canvas を作成
- Render Mode:
World Space - Scale:
0.01, 0.01, 0.01(超重要) - サイズ例:
Width 100 / Height 20
- Render Mode:
- Canvasを敵の頭上へ移動
例:Y = 1.8くらい
中身のUI
Background(最大HP用・半透明)
- Image
- 色:黒 or グレー
- Alpha:
100〜150くらい(半透明)
Fill(現在HP用)
- Image
- 色:緑(後で赤にもできる)
- Image Type:Filled
- Fill Method:Horizontal
- Fill Origin:Left
② HPバー制御用スクリプト
EnemyHealth.cs(既存があれば統合OK) ※IDamageble重複を避けるためDamageReceiver.csと統合したので後述
using UnityEngine;
public class EnemyHealth : MonoBehaviour, IDamageable
{
public int maxHP = 100;
public int currentHP;
[Header("HPバー")]
public EnemyHPBar hpBar;
public float visibleTime = 2f;
float hideTimer;
void Start()
{
currentHP = maxHP;
hpBar.SetHP(currentHP, maxHP);
hpBar.gameObject.SetActive(false);
}
public void TakeDamage(int damage)
{
currentHP -= damage;
currentHP = Mathf.Max(currentHP, 0);
Debug.Log($"Enemy Hit! HP: {currentHP}");
hpBar.gameObject.SetActive(true);
hpBar.SetHP(currentHP, maxHP);
hideTimer = visibleTime;
if (currentHP <= 0)
{
Die();
}
}
void Update()
{
if (hpBar.gameObject.activeSelf)
{
hideTimer -= Time.deltaTime;
if (hideTimer <= 0)
{
hpBar.gameObject.SetActive(false);
}
}
}
void Die()
{
Destroy(gameObject);
}
}
③ HPバー側のスクリプト
using UnityEngine;
using UnityEngine.UI;
public class EnemyHPBar : MonoBehaviour
{
public Image fillImage;
public void SetHP(int current, int max)
{
fillImage.fillAmount = (float)current / max;
}
}
④ Inspector設定(ここ重要)
EnemyHealth
Hp Bar→ HPBar(Canvas)を指定
EnemyHPBar
Fill Image→ 緑のFill Imageを指定
⑤ カメラに向かせる(必須)
World Space UIはそのままだと横向きになります。
Billboard用スクリプト
using UnityEngine;
public class Billboard : MonoBehaviour
{
Camera cam;
void Start()
{
cam = Camera.main;
}
void LateUpdate()
{
transform.LookAt(
transform.position + cam.transform.forward,
cam.transform.up
);
}
}
👉 HPBar(Canvas) にアタッチ
デザイン面のコツ(かなり大事)
- 横幅:60〜100
- 高さ:8〜12
- 背景は必ず半透明
- HPバーは原色すぎない緑
- 敵が多い場合は 表示時間短め(1.5〜2秒)
発展(あとで簡単にできる)
- HPが減るほど色を
👉 緑 → 黄色 → 赤 - ボスだけ常時表示
- クリティカル時だけ赤くフラッシュ
- 味方用HPバーに流用
結論
World Space Canvasは「敵HPバーのためにある」と言っていい
今の構成(Raycastダメージ・IDamageable)とも
相性100% です。
あーなるほど、最大HPと現在HP用のバーを用意すれば良いんだ。
背景に最大HPを、その上に現在HPを表示して、ダメージを受けたら現在HPを減らしてバーを縮めれば良いって感じ。
実際に実装してみる
とりあえず実装していってみます。
Enemyの子にキャンバスを追加、子に最大HPと現在HP用のImageを追加
Enemy
ーImage
ーImage
って感じ。Imageの一方は最大HPを表示する背景、もう一つは現在HPを表示する用です。
名前は適当にリネームしてください。


キャンバスはワールド空間にしてスケールを0.01に、位置はほぼXYZ0でいいけどYだけちょこっと上にして頭のあたりに表示する。
幅は100 200あたりがちょうどいいかな。


Backgroundは最大HPになるやつなので、色を黒っぽくしてアルファ値を100~150くらいにして半透明にする。
幅も100 20くらいで細長くしておく。


Fillは現在HP。後で詳しく設定するけど、とりあえず最大HPのBackgroundより幅高さを小さめに、色とアルファ値だけ設定しておく。


これでこんな感じに表示されます。


スクリプトをコピペする
スクリプトを3つ使うけど、ここで全部コピペしておきます。
EnemyHealth.cs
using UnityEngine;
using System.Collections;
public class EnemyHealth : MonoBehaviour, IDamageable
{
public int maxHP = 100;
public int currentHP;
[Header("HP Bar")]
public EnemyHPBar hpBar;
public float hpBarVisibleTime = 2f;
float hideTimer;
[Header("Damage UI")]
public GameObject damagePopupPrefab;
public Transform popupSpawnPoint;
[Header("Flash")]
public Renderer[] renderers;
public Color flashColor = Color.red;
public float flashDuration = 0.1f;
Color[] originalColors;
void Awake()
{
currentHP = maxHP;
originalColors = new Color[renderers.Length];
for (int i = 0; i < renderers.Length; i++)
{
originalColors[i] = renderers[i].material.color;
}
if (hpBar != null)
hpBar.gameObject.SetActive(false);
}
public void TakeDamage(int damage)
{
Debug.Log("EnemyHealth.TakeDamage 呼ばれた");
currentHP -= damage;
currentHP = Mathf.Max(currentHP, 0);
ShowDamagePopup(damage);
StartCoroutine(Flash());
if (hpBar != null)
{
hpBar.gameObject.SetActive(true);
hpBar.SetHP(currentHP, maxHP);
hideTimer = hpBarVisibleTime;
}
if (currentHP <= 0)
{
Die();
}
}
void Update()
{
if (hpBar != null && hpBar.gameObject.activeSelf)
{
hideTimer -= Time.deltaTime;
if (hideTimer <= 0)
hpBar.gameObject.SetActive(false);
}
}
void ShowDamagePopup(int damage)
{
if (damagePopupPrefab == null)
return;
Vector3 pos = popupSpawnPoint != null
? popupSpawnPoint.position
: transform.position + Vector3.up;
Instantiate(damagePopupPrefab, pos, Quaternion.identity)
.GetComponent<DamagePopup>()
.Setup(damage);
}
IEnumerator Flash()
{
foreach (var r in renderers)
r.material.color = flashColor;
yield return new WaitForSeconds(flashDuration);
for (int i = 0; i < renderers.Length; i++)
renderers[i].material.color = originalColors[i];
}
void Die()
{
Destroy(gameObject);
}
}


EnemyHPBar.cs
using UnityEngine;
using UnityEngine.UI;
public class EnemyHPBar : MonoBehaviour
{
public Image fillImage;
public void SetHP(int current, int max)
{
fillImage.fillAmount = (float)current / max;
}
}


Billboard.cs
using UnityEngine;
public class Billboard : MonoBehaviour
{
Camera cam;
void Start()
{
cam = Camera.main;
}
void LateUpdate()
{
transform.LookAt(
transform.position + cam.transform.forward,
cam.transform.up
);
}
}


スクリプトをアタッチして設定する
EnemyにEnemyHealthをアタッチ、CanvasにEnemyHPBarとBillboardをアタッチします。
EnemyHealthをキャンバスにアタッチしてしまうと動きません。
EnemyHealthは前回追加したDamageReceiverと統合したので、HPBar以外は前回の設定を流用します。
HPBarにはキャンバスを指定する。


キャンバス(HPBar)の設定はこんな感じ。
BillboardコンポーネントはつけるだけでOK。EnemyHPBarには現在HPのほうのImageを指定します。


【重要】DamageReceiverは消す
IDamageableっていうのはInterface命令を使ったスクリプトを呼んでるんだけど、前回作ったDamageReceiverにも使っていて重複している。
public class EnemyHealth : MonoBehaviour, IDamageable
public class DamageReceiver : MonoBehaviour, IDamageable
Interface命令は重複しているとどちらが呼ばれるか分からないみたいで一つにした方が良いっぽい。
スクリプトではEnemyHealthにDamageReceiverの機能も入れて統合させています。
DamageReceiverがあるとGetComponentで呼ばれてしまうため削除しましょう。
インスペクターオフにしても呼ばれてしまうので削除するしかないです。
Fill(Image)の設定
Fill(Image)の設定はこんな感じ。


Imageのソース画像がNoneだと「画像タイプ」より下の設定が出ないので注意。
とりあえず適当な画像がなかったので、Unityデフォルトで入っていたUISPriteを表示しています。
UISPriteは四角いスプライト画像なので細長くすると端っこが滲んでしまうけど、とりあえず動けばいいので後で細長い画像を作って指定するとベストみたい。


これで動いた!
これで無事に動きましたね。
HPが50%以下なら黄色に、20%以下なら赤色にする
ついでにやってみました。
EnemyHPBar.csを書き換えればOK。
using UnityEngine;
using UnityEngine.UI;
public class EnemyHPBar : MonoBehaviour
{
public Image fillImage;
[Header("HP Color")]
public Color highHPColor = Color.green;
public Color midHPColor = Color.yellow;
public Color lowHPColor = Color.red;
public void SetHP(int current, int max)
{
float ratio = (float)current / max;
fillImage.fillAmount = ratio;
UpdateColor(ratio);
}
void UpdateColor(float ratio)
{
if (ratio <= 0.2f)
{
fillImage.color = lowHPColor;
}
else if (ratio <= 0.5f)
{
fillImage.color = midHPColor;
}
else
{
fillImage.color = highHPColor;
}
}
}
これでこんな感じになりました。
おわりに
無事に動いて良かった。
ダメージ関係はこれでだいたいできたかなぁ。
また何かあれば追記します(*^_^*)





