Unityを触り始めた頃って、つい「とりあえず Update() に全部書いてしまう」ことが多いですよね。アニメーションの制御、入力処理、エフェクトの点滅演出…全部ひとつのスクリプトに押し込んでしまうと、

  • どこで何が起きているのか追いづらい
  • 一部だけ別のシーン・別のキャラで再利用したくても切り出しにくい
  • ちょっとした演出変更でも巨大なスクリプトを開いて修正する必要がある

といった「Godクラス」状態になりがちです。

そこでこの記事では、「一定間隔でキャラやオブジェクトを点滅させる」というよくある演出を、ひとつの小さなコンポーネントに切り出してみましょう。イベントシーンの演出や、ダメージを受けた時の点滅、警告ランプなどにそのまま使えるようにします。

今回作るのは、親オブジェクトの「見える/見えない(visible)」を一定間隔で切り替える BlinkEffect コンポーネントです。

【Unity】演出用の点滅処理を1コンポーネントに分離!「BlinkEffect」コンポーネント

以下が、Unity6(C#)で動く BlinkEffect のフルコードです。
「親の visible を一定間隔で切り替える」という要件に合わせて、親の GameObject 全体の表示/非表示を切り替える 実装にしています。


using UnityEngine;

/// <summary> 
/// 一定間隔で親オブジェクトを点滅させるコンポーネント。
/// - 親の GameObject の active 状態を ON/OFF することで「visible」を切り替える。
/// - イベント中の演出、ダメージ演出、警告ランプなどに利用可能。
/// - 自分自身ではなく「親」を点滅させる点に注意。
/// </summary>
[DisallowMultipleComponent]
public class BlinkEffect : MonoBehaviour
{
    // --- 設定項目(インスペクターで調整) ---

    [SerializeField]
    [Tooltip("点滅を開始したときの親オブジェクトの初期表示状態(true なら最初は表示)。")]
    private bool startVisible = true;

    [SerializeField]
    [Tooltip("1回の ON/OFF にかかる時間(秒)。例: 0.5 なら 0.5秒ごとに切り替え。")]
    private float interval = 0.5f;

    [SerializeField]
    [Tooltip("自動で再生を開始するかどうか。false の場合はスクリプトから StartBlink() を呼び出す。")]
    private bool playOnStart = true;

    [SerializeField]
    [Tooltip("点滅をループさせるかどうか。false の場合は totalDuration 経過後に自動停止。")]
    private bool loop = true;

    [SerializeField]
    [Tooltip("ループしない場合の総再生時間(秒)。loop が false のときのみ使用。")]
    private float totalDuration = 2f;

    [SerializeField]
    [Tooltip("親ではなく、このオブジェクト自身を点滅対象にしたい場合は true。")]
    private bool blinkSelfInsteadOfParent = false;

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

    // 点滅対象となる Transform(親 or 自身)
    private Transform targetTransform;

    // 点滅対象となる GameObject(キャッシュしておくと毎フレームの GetComponent を避けられる)
    private GameObject targetObject;

    // 現在点滅中かどうか
    private bool isBlinking = false;

    // 経過時間の蓄積(ループしないときの終了判定に使う)
    private float elapsedTime = 0f;

    // 次に切り替えを行うまでの残り時間
    private float remainTimeToToggle = 0f;

    // 現在の表示状態
    private bool currentVisible;

    // --- Unity ライフサイクル ---

    private void Awake()
    {
        // 点滅対象を決定
        if (blinkSelfInsteadOfParent)
        {
            targetTransform = transform;
        }
        else
        {
            // 親が存在しない場合は、自身を対象にフォールバックする
            targetTransform = transform.parent != null ? transform.parent : transform;
        }

        targetObject = targetTransform.gameObject;
    }

    private void Start()
    {
        // 初期表示状態を反映
        SetVisible(startVisible);

        // 自動再生が有効なら点滅を開始
        if (playOnStart)
        {
            StartBlink();
        }
    }

    private void Update()
    {
        if (!isBlinking)
        {
            return;
        }

        // 経過時間を更新
        float deltaTime = Time.deltaTime;
        elapsedTime += deltaTime;
        remainTimeToToggle -= deltaTime;

        // ループしない場合の終了判定
        if (!loop && elapsedTime >= totalDuration)
        {
            // 最後は表示状態を startVisible に戻して終了させる
            SetVisible(startVisible);
            StopBlinkInternal();
            return;
        }

        // 一定間隔ごとに ON/OFF を切り替え
        if (remainTimeToToggle <= 0f)
        {
            ToggleVisible();
            remainTimeToToggle = interval;
        }
    }

    private void OnDisable()
    {
        // オブジェクトが無効化されたら点滅も止めておく
        StopBlinkInternal();
    }

    // --- 公開 API ---

    /// <summary>
    /// 点滅を開始する。
    /// playOnStart を false にしている場合は、外部スクリプトからこのメソッドを呼ぶ。
    /// </summary>
    public void StartBlink()
    {
        // すでに点滅中ならリセットして再スタート
        isBlinking = true;
        elapsedTime = 0f;
        remainTimeToToggle = interval;

        // 開始時の表示状態を再設定
        SetVisible(startVisible);
    }

    /// <summary>
    /// 点滅を停止する。
    /// </summary>
    public void StopBlink()
    {
        StopBlinkInternal();
    }

    /// <summary>
    /// 現在点滅中かどうかを返す。
    /// </summary>
    public bool IsBlinking()
    {
        return isBlinking;
    }

    // --- 内部メソッド ---

    /// <summary>
    /// 実際に停止処理を行う内部メソッド。
    /// </summary>
    private void StopBlinkInternal()
    {
        isBlinking = false;
        elapsedTime = 0f;
        remainTimeToToggle = 0f;
    }

    /// <summary>
    /// 表示状態を切り替える(ON/OFF)。
    /// </summary>
    private void ToggleVisible()
    {
        SetVisible(!currentVisible);
    }

    /// <summary>
    /// 表示状態を直接設定する。
    /// </summary>
    /// <param name="visible">true なら表示、false なら非表示</param>
    private void SetVisible(bool visible)
    {
        currentVisible = visible;

        // GameObject の active 状態を切り替えることで visible を制御
        // 非表示にすると Update なども止まるため、その点を理解した上で使う。
        if (targetObject != null)
        {
            targetObject.SetActive(visible);
        }
    }
}

使い方の手順

ここでは、具体的な使用例として

  • イベント中にプレイヤーを点滅させる
  • 敵の警告ランプを点滅させる
  • 動く床の「出現/消失」演出に使う

といったシチュエーションをイメージしながら手順を説明します。

  1. コンポーネントを作成する
    • Unity の Project ビューで Scripts フォルダを作成(任意)。
    • 右クリック → Create > C# Script → 名前を BlinkEffect にする。
    • 自動生成された中身を、上記のコード全文で置き換える。
  2. 点滅させたいオブジェクトの子にアタッチする
    「親の visible を切り替える」仕様なので、点滅させたいオブジェクトの子にアタッチするのがポイントです。
    • 例1: プレイヤーをイベント中に点滅させたい場合
      • PlayerRoot(プレイヤー本体の GameObject)を用意。
      • PlayerRoot の子として空の GameObject BlinkController を作成。
      • BlinkControllerBlinkEffect コンポーネントを追加。
      • この状態で blinkSelfInsteadOfParentOFF にしておけば、PlayerRoot 全体が点滅します。
    • 例2: 敵の頭上にある警告ランプだけを点滅させたい場合
      • EnemyRoot の子に WarningLamp(ランプの見た目)を配置。
      • WarningLamp 自体に BlinkEffect をアタッチ。
      • この場合は blinkSelfInsteadOfParentON にします(自分自身を点滅)。
  3. インスペクターでパラメータを設定する
    • Start Visible: 点滅開始時に表示状態から始めるかどうか。通常は true でOK。
    • Interval: 点滅の間隔。0.20.5 あたりが分かりやすいです。
    • Play On Start: シーン開始と同時に点滅させたい場合は ON。
    • Loop: 永続的に点滅させたい場合は ON、一時的な演出なら OFF。
    • Total Duration: Loop が OFF のときにだけ使われる総再生時間(秒)。
    • Blink Self Instead Of Parent:
      • ON: このコンポーネントが付いたオブジェクト自身を点滅。
      • OFF: 親オブジェクト(存在しない場合は自分)を点滅。
  4. スクリプトから制御したい場合(任意)
    例えば「プレイヤーがダメージを受けたときだけ点滅させる」といった制御を行いたい場合は、
    playOnStartOFF にして、外部スクリプトから StartBlink() / StopBlink() を呼び出します。
    
    using UnityEngine;
    
    public class PlayerDamageExample : MonoBehaviour
    {
        [SerializeField]
        private BlinkEffect blinkEffect;
    
        public void OnDamaged()
        {
            // すでに点滅中なら一度止めてから再スタートしてもよい
            if (blinkEffect.IsBlinking())
            {
                blinkEffect.StopBlink();
            }
    
            blinkEffect.StartBlink();
        }
    }
    

    PlayerDamageExample をプレイヤーにアタッチし、インスペクターで BlinkEffect への参照をドラッグ&ドロップしておきましょう。

メリットと応用

BlinkEffect をコンポーネントとして切り出しておくと、

  • 「点滅」という演出ロジックをどこからでも再利用できる
  • プレハブの段階で点滅設定を仕込んでおけば、シーン配置するだけで演出が完成する
  • プレイヤー、敵、ギミック(動く床、トラップなど)で同じ仕組みを共有できる
  • 巨大な Update() から「点滅処理」を完全に追い出せる

といったメリットがあります。レベルデザインの観点でも、

  • 「このトラップは 1 秒間だけ点滅して消える」
  • 「この足場は 0.3 秒間隔で点滅し続ける」

といった違いをインスペクターの数値だけで調整できるので、デザイナーや自分自身の後作業がかなり楽になります。

また、「親の visible を切り替える」という設計にしているため、子オブジェクトがいくつあってもまとめて消えるのも扱いやすいポイントです。キャラ本体・武器・エフェクト・UI などをひとつのルートオブジェクトの下にぶら下げておけば、BlinkEffect ひとつで全部を点滅させられます。

最後に、簡単な「改造案」として、一定時間後に自動で点滅を開始する機能を追加する例を示します。
以下のメソッドを BlinkEffect クラスの中に追記すれば、外部から「2秒後に点滅開始」などの予約ができます。


    /// <summary>
    /// 指定した秒数後に点滅を開始する。
    /// 例: StartBlinkDelayed(2f); // 2秒後に点滅開始
    /// </summary>
    /// <param name="delaySeconds">遅延時間(秒)</param>
    public void StartBlinkDelayed(float delaySeconds)
    {
        // すでに予約されているコルーチンがあれば止めたい場合は、
        // StopCoroutine を使って管理するとさらに堅牢になる。
        StartCoroutine(StartBlinkDelayedRoutine(delaySeconds));
    }

    private System.Collections.IEnumerator StartBlinkDelayedRoutine(float delaySeconds)
    {
        yield return new WaitForSeconds(delaySeconds);
        StartBlink();
    }

このように、小さなコンポーネントに分割しておくと、「あとからちょっとした機能を足す」こともやりやすくなります。
巨大な Update() に書き足していくのではなく、演出ごとにコンポーネントを分けていく設計を意識していきましょう。