Unityでダメージ表示演出を実装したメモ。数値の色を変えたり点滅させたり

unity ダメージ処理 数字 表示 点滅 3Dゲームの作り方
※記事内に広告が含まれています。

バーチャル3Dクリエイター神部まゆみです(*^_^*)

この記事はUnityでダメージ表示演出を実装したので、それについての記事です。

前回の記事はこちら。

Starter Assetsを改変して作っているため、↓こちらの記事から見ると順番に進めていけます。

●PRスペース●
■PR■

現在、Unityアセットストアで毎日日替わりでアセットが無料配布されています。(2025/12/31 まで)。

A Free Asset Every Day | Unity Assets Store

ページに記載されているクーポンコード入力で0円になりますが、記載の日程は時差で日本より17時間遅れているので注意。

〇まゆみマート|BOOTH

BOOTHでVRoidテクスチャやVRChat向けオブジェクトなどを販売しています。いいねしてくれると励みになります(*^-^*)

やりたいこと:敵味方問わずダメージを受けたらダメージ量を表示、量によって色を変えたり点滅させたりしたい

敵の攻撃はまだ作っていないのでこちらの攻撃でダメージを与えた時にしか表示できないけど。

ダメージ100までは白、それを超えたら赤とか設定できたら良いですね。

できれば敵を点滅させたりしたいかなぁ。

ChatGPTパイセンに聞いてみる

とりあえず彼に聞いてみます。

Unity ダメージ 演出 表示 処理 点滅
神部まゆみ
神部まゆみ

次はダメージを表示したいんですけど、UIとかでやるんでしょうか?低ダメージなら白、大ダメージなら赤みたいにできるのでしょうか?あとダメージを受けたら点滅とかもやりたいんですけど。敵味方どちらでも使える汎用性があるスクリプトがあると嬉しいです

ChatGPT
ChatGPT

いいテーマですね。
ここから一気に「ゲームっぽさ」が跳ね上がるところです。

結論から言うと👇

  • ダメージ数値表示 → UI(World Space)
  • 色分け → ダメージ量で色を切り替え
  • 点滅 → Renderer or UI を一瞬フラッシュ
  • 汎用化 → 「ダメージを受けた側」にまとめる

この設計にすると、敵・味方どちらでもそのまま使えます


全体構成(まずはイメージ)

GunShoot
 └ ダメージ値を渡す
      ↓
IDamageable.TakeDamage(int damage)
      ↓
DamageReceiver(敵でも味方でもOK)
 ├ HP減少
 ├ ダメージUI表示
 └ 点滅エフェクト

「撃った側」は一切UIを知らない
→ これが汎用設計のコツです。


① ダメージ表示の基本(World Space UI)

やり方

  • Canvas(World Space)
  • TextMeshPro(おすすめ)
  • ダメージを受けた位置に生成

DamagePopup.cs(汎用)

using UnityEngine;
using TMPro;

public class DamagePopup : MonoBehaviour
{
    public TextMeshProUGUI text;
    public float moveUpSpeed = 1f;
    public float lifeTime = 1f;

    void Update()
    {
        transform.position += Vector3.up * moveUpSpeed * Time.deltaTime;
    }

    public void Setup(int damage)
    {
        text.text = damage.ToString();

        // ダメージ量で色分け
        if (damage < 10)
            text.color = Color.white;
        else if (damage < 30)
            text.color = Color.yellow;
        else
            text.color = Color.red;

        Destroy(gameObject, lifeTime);
    }
}

DamagePopup Prefab のポイント

  • Canvas
    • Render Mode:World Space
  • TextMeshPro
  • 「ゲーム開始時に再生」は関係なし
  • Scale は 0.01〜0.02 くらいがちょうどいい

② ダメージを受ける側(汎用)

DamageReceiver.cs

using UnityEngine;

public class DamageReceiver : MonoBehaviour, IDamageable
{
    public int maxHP = 100;
    public int currentHP;

    [Header("Damage UI")]
    public GameObject damagePopupPrefab;
    public Transform popupSpawnPoint;

    [Header("Flash")]
    public Renderer[] renderers;
    public Color flashColor = Color.red;
    public float flashDuration = 0.1f;

    private 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;
        }
    }

    public void TakeDamage(int damage)
    {
        currentHP -= damage;

        ShowDamagePopup(damage);
        StartCoroutine(Flash());

        if (currentHP <= 0)
        {
            Die();
        }
    }

    void ShowDamagePopup(int damage)
    {
        if (damagePopupPrefab == null) return;

        Vector3 pos = popupSpawnPoint != null
            ? popupSpawnPoint.position
            : transform.position + Vector3.up;

        GameObject popupObj = Instantiate(damagePopupPrefab, pos, Quaternion.identity);
        popupObj.GetComponent<DamagePopup>().Setup(damage);
    }

    System.Collections.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);
    }
}

これが「汎用」な理由

  • GunShoot は ダメージを渡すだけ
  • 敵・味方の区別なし
  • 見た目演出は 受けた側が責任を持つ
  • UI / フラッシュ / 死亡処理を一元管理

👉 あとから差し替え・拡張が超ラク


③ よくある拡張(すぐできる)

🔴 クリティカルヒット

if (isCritical)
{
    text.color = Color.magenta;
    text.fontSize *= 1.3f;
}

✨ 点滅をShaderでやる(上級)

  • Emission を一瞬強くする
  • URPならFlash用Shader

🧠 味方だけUI非表示

if (isPlayer)
    return;

まとめ

  • ダメージ表示 → World Space UI
  • 色分け → ダメージ量で制御
  • 点滅 → DamageReceiver側で
  • 汎用化の鍵 → 撃たれた側が全部やる

結構簡単にできそう?

とりあえずやってみます。

実際に実装してみる

ちゃんと実装できるのかやってみます。

前回作ったEnemyDamageReceiverコンポーネントは削除しておく

これが残ったままだと今回追加するDamageReceiver.csと重複してしまい正常に動かなかった。

詳しくは↓こちらの記事を参照。

インスペクターでオフにしてもダメなので削除しましょう。

TextMeshProをインポートしておく

これをインポートしておかないと使えません。

ウィンドウ ⇒ TextmeshPro のところ、とりあえずこの二つをインポートしておく。

unity ダメージ処理 数字 表示 点滅

ここでやらなくてもUIでTextMeshProを追加した時にインポートするかメニューが出るけど、まあ先にやっておく。

DamagePopup.csをコピペ

これをコピペしておく。

unity ダメージ処理 演出 数字 表示 点滅
using UnityEngine;
using TMPro;

public class DamagePopup : MonoBehaviour
{
    public TextMeshProUGUI text;
    public float moveUpSpeed = 1f;
    public float lifeTime = 1f;

    void Update()
    {
        transform.position += Vector3.up * moveUpSpeed * Time.deltaTime;
    }

    public void Setup(int damage)
    {
        text.text = damage.ToString();

        if (damage < 10)
            text.color = Color.white;
        else if (damage < 30)
            text.color = Color.yellow;
        else
            text.color = Color.red;

        Destroy(gameObject, lifeTime);
    }
}

キャンバスとTextMeshProを追加、キャンバスにDamagePopUp.csをアタッチしてプレハブに、キャンバスはヒエラルキーから削除

ダメージを表示するキャンバスを作っていく。

①キャンバスとTextMeshProを追加

②キャンバスのレンダーモードをワールド空間に、位置はXYZすべて0、スケール0.01に。TextMeshProはCenter(真ん中)に表示

③キャンバスにDamagePopup.csをアタッチ、TextMeshProの参照を指定。LifeTime(表示する時間)は0.5くらいが良いかな?

④このキャンバスをヒエラルキーからプロジェクトにD&Dしてプレハブ化、ヒエラルキーのキャンバスは削除する

でOK。

④はとりあえずまだやらなくても動くけど、変なところに数値が表示されてしまうので動作確認したら削除したほうが良いかも。

ダメージが通ったらキャンバスのプレハブを呼ぶので、ヒエラルキーになくても勝手にその都度召喚されます。

設定はこんな感じ。

unity ダメージ処理 数字 表示 点滅
unity ダメージ処理 数字 表示 点滅

DamageReceiver.csをコピペしてEnemyにアタッチ

DamageReceiver.csをコピペします。

unity ダメージ処理 数字 表示 点滅
using UnityEngine;
using System.Collections;

public class DamageReceiver : MonoBehaviour, IDamageable
{
    public int maxHP = 100;
    public int currentHP;

    [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;
        }
    }

    public void TakeDamage(int damage)
    {
        Debug.Log($"{gameObject.name} takes {damage}");

        currentHP -= damage;

        ShowDamagePopup(damage);
        StartCoroutine(Flash());

        if (currentHP <= 0)
        {
            Destroy(gameObject);
        }
    }

    void ShowDamagePopup(int damage)
    {
        if (damagePopupPrefab == null)
            return;

        Vector3 pos = popupSpawnPoint != null
            ? popupSpawnPoint.position
            : transform.position + Vector3.up;

        GameObject popup = Instantiate(damagePopupPrefab, pos, Quaternion.identity);
        popup.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];
    }
}

これでEnemyにアタッチして設定します。

unity ダメージ処理 数字 表示 点滅

DamageReceiver.csの設定

設定はこんな感じ。

unity ダメージ処理 数字 表示 点滅

Damage Popup Prefab さっきプレハブにしたキャンバスを指定

Popup Spawn Point ダメージ数値が出現する場所。空のオブジェクトで敵の頭上に配置し、追従するよう敵の子にしてあります。

unity ダメージ処理 数字 表示 点滅

レンダラー 敵のメッシュを指定。被ダメージで色を変えるのに使う

Flash Color 被ダメージで点滅させる色を指定

Flash Duration 被ダメージで点滅させる時間を指定

これで動いた!

これで無事に動きました!

動かない場合の対処法など

今回少しハマったけど、最初に書いたEnemyDamageReceiverコンポーネントを削除せずオフにしていたことが原因だった。

どこが原因か突き止めるためにデバッグログを出していたので、動かない場合はこれをやると解決するかも。

①DamagePopup 側で「呼ばれたか」を確認する

DamagePopup.cs をこう書き換えてください(デバッグ用)

public void Setup(int damage)
{
    Debug.Log("DamagePopup.Setup called with damage = " + damage);

    // 強制的に値を書き換え
    text.text = damage.ToString();

    // 強制的に色も変更(分かりやすく)
    text.color = Color.green;

    Destroy(gameObject, lifeTime);
}

ここで見るポイント

  • Console に DamagePopup.Setup called with damage = 10 が出るか?

❌ ログが出ない

Setup() が呼ばれていない

⭕ ログは出るが TextMeshProが初期値 のまま

TextMeshPro の参照が取れていない


②参照が取れているかを強制チェック

DamagePopup.cs の Start() に追加

void Start()
{
    if (text == null)
    {
        Debug.LogError("TextMeshPro reference is NULL!");
    }
    else
    {
        // 起動直後に強制的に書き換えてみる
        text.text = "START";
        text.color = Color.cyan;
    }
}

❌ Console に

TextMeshPro reference is NULL!

と出る場合、

Inspector で Text を割り当てていない

⭕TextMeshProのテキストが START に変わる

→ 参照はOK、Setup() が呼ばれていないのが原因


③DamageReceiver 側が本当に呼ばれているか

DamageReceiver.cs の ShowDamagePopup にログ追加

void ShowDamagePopup(int damage)
{
    Debug.Log("ShowDamagePopup called damage = " + damage);

    if (damagePopupPrefab == null)
    {
        Debug.LogError("damagePopupPrefab is NULL");
        return;
    }

    GameObject popup = Instantiate(damagePopupPrefab, transform.position + Vector3.up, Quaternion.identity);

    DamagePopup dp = popup.GetComponent<DamagePopup>();
    if (dp == null)
    {
        Debug.LogError("DamagePopup component NOT found on prefab");
        return;
    }

    dp.Setup(damage);
}

ここまでで分かること

状態原因
ShowDamagePopup が出ないTakeDamage が呼ばれてない
ShowDamagePopup 出る / Setup 出ないGetComponent 失敗
Setup 出る / 999 のままText 未割り当て
START にも変わらないCanvas / TMP 設定ミス

TextMeshProの数値 が変わらない「一番多い原因」

90% これです👇

DamagePopup Prefab の Inspector で

  • DamagePopup (Script)
    • Text フィールドが None のまま

👉 Hierarchy 内の
TextMeshProUGUI をドラッグしてセットしてください。

Prefab 化したあとに
参照が外れるのは Unity あるあるです。


念のためチェックリスト(上から順に)

  1. ✅ Console に ShowDamagePopup called 出る?
  2. DamagePopup.Setup called 出る?
  3. TextMeshPro reference is NULL! 出てない?
  4. ✅ Prefab の Text が割り当てられている?
  5. ✅ Canvas が World Space?
  6. ✅ Scale が 0.01 前後?

ここまでやれば必ず原因が特定できます

あとは最初からやり直したりするとうまくいくかもしれません。

ちょっとしたことで動かなくなるため、見落としがある場合もあります(^_^;)

おわりに

少しハマったけどダメージ表示が実装できて良かった。

これで結構ゲームっぽくなってきたかな?

また続きが書けたら追記します(*^_^*)

タイトルとURLをコピーしました