Unityを触り始めた頃って、つい「とりあえず全部Updateに書いちゃえ!」となりがちですよね。
プレイヤー操作、HP管理、エフェクト、被弾時の点滅処理…全部ひとつのスクリプトに押し込んでしまうと、

  • どこを直せばいいか分からない
  • 別のキャラや敵に流用しづらい
  • ちょっとした演出変更でも大改造が必要

といった問題が一気に噴き出します。
そこでこの記事では「ダメージを受けたときに一瞬白や赤に点滅させる」処理だけに責務を絞ったコンポーネントを作ります。

名前は DamageFlash(被弾点滅)
「ダメージ演出」というよくある機能を、ひとつの小さなコンポーネントとして切り出しておくことで、プレイヤーにも敵にも、動くギミックにも、ドラッグ&ドロップだけで簡単に再利用できるようにしていきましょう。

【Unity】被弾演出を一行で!「DamageFlash」コンポーネント

ここでは、親オブジェクトのSpriteRendererの色(Color / Modulate)を一時的に白や赤に変えるコンポーネントを実装します。
特徴はこんな感じです:

  • ダメージを受けた瞬間に PlayFlash() を呼ぶだけ
  • 点滅色(白・赤など)、時間、回数をインスペクターから調整可能
  • SpriteRendererが子オブジェクトについていてもOK(親から自動取得)
  • 複数のSpriteRenderer(影や武器など)を一括で点滅させることも可能

フルコード:DamageFlash.cs


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>  
/// ダメージ時にスプライトの色を一時的に変えて点滅させるコンポーネント。  
/// - 親オブジェクトにアタッチして使う想定  
/// - 子階層の SpriteRenderer をまとめて制御可能  
/// - ダメージを受けたタイミングで PlayFlash() を呼ぶだけ  
/// </summary>
public class DamageFlash : MonoBehaviour
{
    // --- 設定項目(インスペクターから調整) ---

    [Header("点滅対象")]
    [Tooltip("自動検出を使う場合はチェックを入れたままにします。\n" +
             "オフにすると manualSpriteRenderers のみが対象になります。")]
    [SerializeField] private bool autoFindSpriteRenderers = true;

    [Tooltip("自動検出に加えて、明示的に点滅させたい SpriteRenderer を指定したい場合に使います。")]
    [SerializeField] private List<SpriteRenderer> manualSpriteRenderers = new();

    [Header("点滅パラメータ")]
    [Tooltip("点滅させる色(白・赤など)")]
    [SerializeField] private Color flashColor = Color.white;

    [Tooltip("点滅 1 回あたりの時間(秒)")]
    [SerializeField] private float flashDuration = 0.1f;

    [Tooltip("点滅する回数(2 なら「元色 → 点滅色 → 元色 → 点滅色 → 元色」のようなイメージ)")]
    [SerializeField] private int flashCount = 2;

    [Tooltip("点滅中に新しい点滅が来たとき、前の点滅を中断してやり直すか")]
    [SerializeField] private bool interruptPreviousFlash = true;

    [Header("デバッグ")]
    [Tooltip("ゲーム開始時に自動で一度点滅させて確認したい場合はオンにします")]
    [SerializeField] private bool playOnStart = false;


    // --- 内部状態 ---

    // 実際に操作対象となる SpriteRenderer 一覧
    private readonly List<SpriteRenderer> _targetRenderers = new();

    // 各 SpriteRenderer の元の色を保持しておく
    private readonly Dictionary<SpriteRenderer, Color> _originalColors = new();

    // 実行中のコルーチンを保持(中断用)
    private Coroutine _flashCoroutine;


    private void Awake()
    {
        CacheSpriteRenderers();
    }

    private void Start()
    {
        if (playOnStart)
        {
            PlayFlash();
        }
    }

    /// <summary>
    /// 点滅対象となる SpriteRenderer をキャッシュする。
    /// Awake で一度だけ実行される想定。
    /// </summary>
    private void CacheSpriteRenderers()
    {
        _targetRenderers.Clear();
        _originalColors.Clear();

        // 自動検出が有効なら、自分自身と子階層の SpriteRenderer を全部拾う
        if (autoFindSpriteRenderers)
        {
            var autoFound = GetComponentsInChildren<SpriteRenderer>(includeInactive: true);
            foreach (var sr in autoFound)
            {
                if (sr == null) continue;
                if (!_targetRenderers.Contains(sr))
                {
                    _targetRenderers.Add(sr);
                }
            }
        }

        // 手動指定分を追加
        foreach (var sr in manualSpriteRenderers)
        {
            if (sr == null) continue;
            if (!_targetRenderers.Contains(sr))
            {
                _targetRenderers.Add(sr);
            }
        }

        // 元の色を記録
        foreach (var sr in _targetRenderers)
        {
            if (!_originalColors.ContainsKey(sr))
            {
                _originalColors.Add(sr, sr.color);
            }
        }

        if (_targetRenderers.Count == 0)
        {
            Debug.LogWarning(
                $"[DamageFlash] 対象 SpriteRenderer が見つかりませんでした。オブジェクト名: {name}",
                this
            );
        }
    }

    /// <summary>
    /// 外部から呼び出すメインAPI。
    /// ダメージを受けたタイミングでこのメソッドを呼ぶだけで点滅演出が再生されます。
    /// </summary>
    public void PlayFlash()
    {
        // SpriteRenderer が見つかっていない場合、再度キャッシュを試みる
        if (_targetRenderers.Count == 0)
        {
            CacheSpriteRenderers();
        }

        if (_targetRenderers.Count == 0)
        {
            // それでも見つからなければ何もしない
            return;
        }

        // すでに点滅中なら、設定に応じて中断するかどうかを決める
        if (_flashCoroutine != null)
        {
            if (interruptPreviousFlash)
            {
                StopCoroutine(_flashCoroutine);
                // 念のため元色に戻してから新しい点滅を開始
                RestoreOriginalColors();
            }
            else
            {
                // 前の点滅を優先する設定の場合は、ここで終了
                return;
            }
        }

        _flashCoroutine = StartCoroutine(FlashRoutine());
    }

    /// <summary>
    /// 点滅の本体処理(コルーチン)。
    /// 元の色 <-> 点滅色 を flashCount 回だけ切り替えます。
    /// </summary>
    private IEnumerator FlashRoutine()
    {
        // 念のため最新の色を記録し直しておく(他の処理で色が変わっているかもしれないため)
        foreach (var sr in _targetRenderers)
        {
            if (sr == null) continue;
            _originalColors[sr] = sr.color;
        }

        // 点滅回数分ループ
        for (int i = 0; i < flashCount; i++)
        {
            // 点滅色に変更
            SetAllSpriteColors(flashColor);
            yield return new WaitForSeconds(flashDuration);

            // 元の色に戻す
            RestoreOriginalColors();
            yield return new WaitForSeconds(flashDuration);
        }

        // 念のため最後に元の色に戻して終了
        RestoreOriginalColors();
        _flashCoroutine = null;
    }

    /// <summary>
    /// すべての対象 SpriteRenderer の色を指定色に設定する。
    /// </summary>
    private void SetAllSpriteColors(Color color)
    {
        foreach (var sr in _targetRenderers)
        {
            if (sr == null) continue;
            sr.color = color;
        }
    }

    /// <summary>
    /// すべての対象 SpriteRenderer の色を「記録しておいた元の色」に戻す。
    /// </summary>
    private void RestoreOriginalColors()
    {
        foreach (var kvp in _originalColors)
        {
            var sr = kvp.Key;
            if (sr == null) continue;
            sr.color = kvp.Value;
        }
    }

    /// <summary>
    /// (任意)外部から強制的に点滅を止めて、元の色に戻したいときに呼びます。
    /// 例:死亡時に別のエフェクトに切り替えたいときなど。
    /// </summary>
    public void StopAndRestore()
    {
        if (_flashCoroutine != null)
        {
            StopCoroutine(_flashCoroutine);
            _flashCoroutine = null;
        }
        RestoreOriginalColors();
    }
}

使い方の手順

ここからは、実際にどうやってシーンに組み込むかを具体的に見ていきましょう。
例として「プレイヤー」「敵キャラ」「動く床(トラップ)」の3パターンを挙げます。

手順①:スクリプトをプロジェクトに追加

  1. Project ウィンドウで Scripts フォルダなどを右クリック → Create > C# Script
  2. 名前を DamageFlash に変更
  3. 上記のフルコードを丸ごとコピペして保存

手順②:対象オブジェクトにアタッチ

以下のようなオブジェクト構造を想定します:

Player
 ├─ Body (SpriteRenderer)
 └─ Weapon (SpriteRenderer)
  1. Player オブジェクトを選択
  2. Add Component ボタンから DamageFlash を追加
  3. 特にこだわりがなければ、autoFindSpriteRenderers をオンのままにしておきます(子階層の Body / Weapon の色がまとめて点滅します)
  4. 点滅色を Color.white(白)や Color.red(赤)など好きな色に変更

敵キャラや動く床(トラップ)にも同じように DamageFlash をアタッチすれば、そのオブジェクト専用の被弾演出として機能します。

手順③:ダメージ処理から呼び出す

次に、実際にダメージを受けたときに PlayFlash() を呼ぶ処理を書きましょう。
ここではシンプルな HP 管理コンポーネントの例を示します。


using UnityEngine;

/// <summary>  
/// 非常にシンプルなダメージ受け取り例。  
/// DamageFlash と組み合わせて使う想定です。  
/// </summary>
public class SimpleHealth : MonoBehaviour
{
    [SerializeField] private int maxHp = 5;
    [SerializeField] private int currentHp;

    // 同じオブジェクト上の DamageFlash をキャッシュ
    private DamageFlash _damageFlash;

    private void Awake()
    {
        currentHp = maxHp;
        _damageFlash = GetComponent<DamageFlash>();
        if (_damageFlash == null)
        {
            Debug.LogWarning("[SimpleHealth] DamageFlash が見つかりません。被弾点滅は行われません。", this);
        }
    }

    /// <summary>
    /// 外部からダメージを与えるときに呼ぶメソッド。
    /// </summary>
    public void TakeDamage(int amount)
    {
        currentHp -= amount;
        if (currentHp <= 0)
        {
            currentHp = 0;
            // TODO: 死亡処理など
        }

        // DamageFlash がついていれば点滅演出を再生
        if (_damageFlash != null)
        {
            _damageFlash.PlayFlash();
        }
    }
}

この SimpleHealth をプレイヤーや敵にアタッチし、攻撃スクリプトから TakeDamage() を呼べば、HP 減少+被弾点滅 を一括で扱えます。

手順④:具体的な使用例

例1:プレイヤーの被弾演出
  1. プレイヤーオブジェクトに DamageFlashSimpleHealth をアタッチ
  2. 敵の攻撃スクリプトや弾丸スクリプトで、プレイヤーに当たったときに TakeDamage(1) を呼ぶ
  3. プレイヤーのスプライトが一瞬白く点滅して、被弾したことが視覚的に分かるようになります
例2:敵キャラの被弾演出
  1. 敵プレハブにも同様に DamageFlashSimpleHealth をアタッチ
  2. プレイヤーの弾が敵に当たったときに TakeDamage(1) を呼ぶ
  3. 敵が赤く点滅するように設定しておけば、「ちゃんと当たっている感」が出て気持ちよくなります
例3:動く床(トラップ)の警告演出

「一定時間ごとにダメージを与える床」や「動くトゲ床」などにも応用できます。

  1. 床オブジェクトに DamageFlash をアタッチし、flashColor を赤に設定
  2. 床がダメージ判定を出す直前(あるいは出した瞬間)に PlayFlash() を呼ぶ
  3. 床が赤く点滅してプレイヤーに危険を知らせるような演出が簡単に作れます

メリットと応用

DamageFlash を導入することで、以下のようなメリットがあります。

  • 責務が明確:ダメージ演出は DamageFlash に任せ、HP 計算や死亡処理は別コンポーネントに分離できます。
  • プレハブの再利用性アップ:プレイヤー、敵、ギミックなど、どのプレハブにも同じコンポーネントをポン付け可能。
  • レベルデザインが楽:シーン上で色や時間をいじるだけで、各オブジェクトの「痛そうさ」「危険さ」を簡単にチューニングできます。
  • アート変更に強い:スプライトが増えても、子階層に追加されるだけなら自動検出で勝手に対象になってくれます。

演出系はどうしても「とりあえずその場のスクリプトに書いてしまう」ことが多いですが、
こうして 「被弾時に色を変える」という機能だけを切り出したコンポーネント にしておくと、プロジェクト全体がかなりスッキリします。

改造案:徐々に元の色へ戻すフェードアウト演出

現在の実装では「パッと色が変わって、パッと戻る」点滅ですが、
「一瞬白くなって、徐々に元の色へフェードアウトしていく」ような演出もよく使われます。

例えば、以下のようなメソッドを DamageFlash に追加して、別の演出として使い分けることもできます。


/// <summary>
/// 一度だけ点滅色にして、徐々に元の色へフェードアウトする例。
/// 必要に応じて、PlayFlash() の代わりにこちらを呼ぶようにしてもOKです。
/// </summary>
public void PlayFadeFlash(float fadeTime)
{
    if (_flashCoroutine != null)
    {
        StopCoroutine(_flashCoroutine);
        RestoreOriginalColors();
    }
    _flashCoroutine = StartCoroutine(FadeFlashRoutine(fadeTime));
}

private IEnumerator FadeFlashRoutine(float fadeTime)
{
    // 現在の色を元色として記録
    foreach (var sr in _targetRenderers)
    {
        if (sr == null) continue;
        _originalColors[sr] = sr.color;
    }

    // 一旦全て点滅色にする
    SetAllSpriteColors(flashColor);

    float elapsed = 0f;
    while (elapsed < fadeTime)
    {
        elapsed += Time.deltaTime;
        float t = Mathf.Clamp01(elapsed / fadeTime);

        // t が 0 → 1 になるにつれて flashColor から originalColor へ補間
        foreach (var sr in _targetRenderers)
        {
            if (sr == null) continue;
            var from = flashColor;
            var to = _originalColors[sr];
            sr.color = Color.Lerp(from, to, t);
        }

        yield return null;
    }

    RestoreOriginalColors();
    _flashCoroutine = null;
}

このように、小さなコンポーネントをベースに「改造案」を積み重ねていくスタイルにしておくと、
プロジェクトが大きくなっても、演出のバリエーションを安全に増やしていけます。
ぜひ自分のゲームのテイストに合わせて、色や時間、フェードの仕方をどんどんカスタマイズしてみてください。