【Unity】GodMode (無敵モード) コンポーネントの作り方

Unityを触り始めたころ、なんでもかんでも Update() に書いてしまいがちですよね。
ダメージ処理も、入力処理も、エフェクトも、デバッグ用の無敵モードも、全部ひとつの巨大スクリプトに詰め込んでしまうと、少し仕様を変えるだけであちこちのコードを触る必要が出てきて、とてもつらくなります。

とくに「デバッグ用の無敵モード」をプレイヤーのスクリプトに直書きしてしまうと、本番ビルドで消し忘れたり、別キャラにも同じ機能を持たせたくなったときにコピペ地獄になったりしがちです。

そこで今回は、「GodMode(無敵モード)」だけを担当する小さなコンポーネントとして切り出してみましょう。
HPを減らさず、あらゆる攻撃判定を無視する、デバッグ用の無敵モードを、どのキャラにもポン付けできる形で実装していきます。

【Unity】一瞬でデバッグ無敵化!「GodMode」コンポーネント

ここでは、以下のような構成を想定しています。

  • Health コンポーネント:HP管理(ダメージ・回復など)
  • Damageable コンポーネント:攻撃を受ける対象であることを表す
  • GodMode コンポーネント:無敵モードのON/OFF管理(今回の主役)

「GodMode」は、Health に「ダメージしないで」と伝える役に徹します。
実際のゲームロジック(弾が当たったらダメージ、など)は Health / Damageable に任せることで、責務を分離します。

フルコード


using UnityEngine;
using UnityEngine.InputSystem; // 新Input Systemを使う場合

/// <summary>HPを管理するシンプルなコンポーネント</summary>
public class Health : MonoBehaviour
{
    [SerializeField] private int maxHealth = 100;
    [SerializeField] private int currentHealth;

    // 無敵状態かどうか(GodMode などから書き換えられる前提)
    [SerializeField] private bool isInvincible = false;

    // HPが0になったときに何かしたい場合に使う(任意)
    public System.Action OnDied;

    public int CurrentHealth => currentHealth;
    public int MaxHealth => maxHealth;

    private void Awake()
    {
        // 開始時に現在HPを最大値にリセット
        currentHealth = maxHealth;
    }

    /// <summary>外部から無敵状態を設定するためのメソッド</summary>
    public void SetInvincible(bool value)
    {
        isInvincible = value;
    }

    /// <summary>ダメージを受ける処理</summary>
    public void ApplyDamage(int amount)
    {
        // 無敵ならダメージを無視
        if (isInvincible) return;

        if (amount <= 0) return; // マイナスや0ダメージは無視

        currentHealth -= amount;
        currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth);

        if (currentHealth == 0)
        {
            HandleDeath();
        }
    }

    /// <summary>回復処理</summary>
    public void Heal(int amount)
    {
        if (amount <= 0) return;

        currentHealth += amount;
        currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth);
    }

    /// <summary>死亡時の処理をまとめておく</summary>
    private void HandleDeath()
    {
        // イベントを発火(他コンポーネントに任せる)
        OnDied?.Invoke();

        // ここではとりあえずログだけ
        Debug.Log($"{gameObject.name} は倒れました。");
    }
}

/// <summary>
/// 「攻撃を受ける対象」であることを表すコンポーネント。
/// 弾やトラップなどから、このコンポーネントを経由してダメージを与える。
/// </summary>
[RequireComponent(typeof(Health))]
public class Damageable : MonoBehaviour
{
    [SerializeField] private Health health;

    private void Reset()
    {
        // インスペクターで自動参照
        health = GetComponent<Health>();
    }

    private void Awake()
    {
        if (health == null)
        {
            health = GetComponent<Health>();
        }
    }

    /// <summary>外部(弾・敵など)から呼ばれる想定のダメージ受付口</summary>
    public void TakeDamage(int damageAmount)
    {
        if (health == null) return;
        health.ApplyDamage(damageAmount);
    }
}

/// <summary>
/// デバッグ用の無敵モードを管理するコンポーネント。
/// ・HPを減らさない(Health に無敵フラグを渡す)
/// ・攻撃判定を無視する(Damageable へのダメージをブロック)
/// </summary>
[DisallowMultipleComponent]
[RequireComponent(typeof(Health))]
[RequireComponent(typeof(Damageable))]
public class GodMode : MonoBehaviour
{
    [Header("GodMode 設定")]
    [Tooltip("ゲーム開始時から無敵にするかどうか")]
    [SerializeField] private bool startAsGodMode = true;

    [Tooltip("デバッグ用:このキーで無敵の ON/OFF を切り替える(新Input System を使わない場合)")]
    [SerializeField] private Key toggleKey = Key.F1;

    [Tooltip("新Input System を使わず、古い Input.GetKey を使う場合は true")]
    [SerializeField] private bool useLegacyInput = false;

    [Header("参照コンポーネント")]
    [SerializeField] private Health health;
    [SerializeField] private Damageable damageable;

    // 現在の無敵状態
    [SerializeField] private bool isGodMode = false;

    // 新Input System用の入力アクション(任意)
    [Tooltip("新Input Systemで無敵切り替えを行いたい場合に割り当てる")]
    [SerializeField] private InputActionReference toggleActionReference;

    private void Reset()
    {
        // コンポーネント追加時に自動で参照をセット
        health = GetComponent<Health>();
        damageable = GetComponent<Damageable>();
    }

    private void Awake()
    {
        if (health == null) health = GetComponent<Health>();
        if (damageable == null) damageable = GetComponent<Damageable>();

        // 開始時の無敵状態を適用
        SetGodMode(startAsGodMode);
    }

    private void OnEnable()
    {
        // 新Input Systemを使う場合は、Action にコールバックを登録
        if (toggleActionReference != null && toggleActionReference.action != null)
        {
            toggleActionReference.action.performed += OnToggleActionPerformed;
            toggleActionReference.action.Enable();
        }

        // Damageable のダメージ受付をフックするため、イベントや委譲があればそちらを使うが、
        // ここではシンプルに「GodMode 側からダメージを監視してブロックする」想定で、
        // 弾側から GodMode をチェックしてもらう設計にします。
        // (具体的な弾の実装例は後述のチュートリアルで解説)
    }

    private void OnDisable()
    {
        if (toggleActionReference != null && toggleActionReference.action != null)
        {
            toggleActionReference.action.performed -= OnToggleActionPerformed;
            toggleActionReference.action.Disable();
        }
    }

    private void Update()
    {
        // 旧Input Systemを使う簡易デバッグ用トグル
        if (useLegacyInput)
        {
            // 注意:旧Input Systemを使う場合は Project Settings で有効化しておきましょう
            if (Keyboard.current != null && Keyboard.current[toggleKey].wasPressedThisFrame)
            {
                ToggleGodMode();
            }
        }
    }

    /// <summary>新Input Systemから呼ばれるトグル処理</summary>
    private void OnToggleActionPerformed(InputAction.CallbackContext context)
    {
        ToggleGodMode();
    }

    /// <summary>無敵モードを反転する</summary>
    public void ToggleGodMode()
    {
        SetGodMode(!isGodMode);
    }

    /// <summary>無敵モードを明示的に設定する</summary>
    public void SetGodMode(bool enable)
    {
        isGodMode = enable;

        // Health に無敵フラグを渡す
        if (health != null)
        {
            health.SetInvincible(isGodMode);
        }

        Debug.Log($"GodMode: {(isGodMode ? "ON" : "OFF")} - {gameObject.name}");
    }

    /// <summary>
    /// 外部(弾やトラップ)から「このオブジェクトにダメージを与えてよいか?」を問い合わせるためのメソッド。
    /// GodMode が ON のときは false を返すことで、攻撃判定自体を無視させることができる。
    /// </summary>
    public bool CanReceiveDamage()
    {
        return !isGodMode;
    }
}

/// <summary>
/// シンプルな弾の例。Damageable と GodMode を参照してダメージを与える。
/// </summary>
public class SimpleBullet : MonoBehaviour
{
    [SerializeField] private int damage = 10;
    [SerializeField] private float lifeTime = 5f;

    private void Start()
    {
        // 一定時間後に自動で消える
        Destroy(gameObject, lifeTime);
    }

    private void OnTriggerEnter(Collider other)
    {
        // 当たった相手が Damageable を持っているかチェック
        if (other.TryGetComponent(out Damageable damageable))
        {
            // もし GodMode を持っていたら、ダメージを与えてよいか確認
            if (other.TryGetComponent(out GodMode godMode))
            {
                if (!godMode.CanReceiveDamage())
                {
                    // 無敵中ならダメージを与えず、そのまま弾だけ消す
                    Destroy(gameObject);
                    return;
                }
            }

            // ダメージ許可ならダメージを与える
            damageable.TakeDamage(damage);
            Destroy(gameObject);
        }
    }
}

使い方の手順

  1. プレイヤーに Health / Damageable / GodMode を付ける
    プレイヤーの GameObject を選択し、HealthDamageableGodMode の3つのコンポーネントを追加します。
    GodMode には [RequireComponent] が付いているので、足りないコンポーネントは自動で追加されます。
  2. 初期設定を行う
    インスペクターで以下を設定します。
    • HealthMax Health を好きな値(例: 100)に設定。
    • GodMode
      • Start As God Mode … デバッグ中は ON にしておくと便利です。
      • Use Legacy Input … 旧Input SystemでF1トグルを使いたい場合は ON。
      • Toggle Key … 好きなキー(例: F1, F2)を設定。
      • Toggle Action Reference … 新Input Systemなら、InputActionAssetからトグル用Actionを割り当て。
  3. 弾(または敵の攻撃)に SimpleBullet を付ける
    弾用のプレハブを作成し、SimpleBullet を追加します。
    弾の Collider を Is Trigger にして、Rigidbody(KinematicでもOK)を付けておきましょう。
    敵やトラップも同じ考え方で、攻撃が当たるときに Damageable を探して TakeDamage() を呼ぶようにします。
  4. プレイして無敵モードを確認する
    再生して、プレイヤーに弾を当ててみます。
    • GodMode が ON:HP が減らず、ログに「GodMode: ON」と表示されます。
    • GodMode を OFF:弾が当たるたびに HP が減っていきます。

    開発中は無敵 ON、本番テストでは OFF、という切り替えがとても簡単になります。

このように、「無敵モード」はプレイヤー本体のスクリプトに直書きせず、GodMode コンポーネントとして分離しておくことで、敵キャラや動く床など、他のオブジェクトにも同じ仕組みを簡単に使い回せます。

メリットと応用

この GodMode コンポーネントを導入するメリットはかなり多いです。

  • プレハブ単位で無敵状態を管理できる
    プレイヤー、ボス、テスト用ダミーなど、どのプレハブに無敵を付けるかをインスペクターで完結できます。
    「このボスはフェーズ2だけ無敵」などの演出も、GodMode を一時的に ON にするだけで表現できます。
  • 本番とデバッグの切り替えが楽になる
    デバッグビルドでは Start As God Mode を ON、本番ビルドでは OFF、のようにビルドごとに設定を変えるだけで対応できます。
    スクリプト内に「#if UNITY_EDITOR」だらけのデバッグコードを書かなくて済みます。
  • レベルデザインの検証が高速化する
    新しいステージを作るとき、まずはプレイヤーを無敵にしてマップの動線やギミックだけ確認したいことが多いです。
    そんなとき、GodMode コンポーネントを ON にしておけば、被弾を気にせず動き回れるので、検証がかなり楽になります。
  • 責務分離でコードが見通しやすくなる
    HP管理は Health、ダメージ受付は Damageable、無敵管理は GodMode と役割を分けることで、
    「どのスクリプトが何をしているのか」が明確になります。巨大な God クラスを避けられますね。

改造案:時間制限付きの一時的な無敵

例えば「アイテムを取ったら5秒だけ無敵」みたいな仕様を足したい場合は、GodMode にこんなメソッドを追加できます。


public void ActivateTemporaryGodMode(float duration)
{
    // すでに無敵中でも、時間を上書きしたいなら Coroutine で管理するのが安全
    StartCoroutine(TemporaryGodModeRoutine(duration));
}

private System.Collections.IEnumerator TemporaryGodModeRoutine(float duration)
{
    SetGodMode(true);
    yield return new WaitForSeconds(duration);
    SetGodMode(false);
}

あとは、無敵アイテムのスクリプトから playerGodMode.ActivateTemporaryGodMode(5f); のように呼ぶだけで、5秒間だけ無敵を簡単に実現できます。
このように、小さなコンポーネントに分けておくと、「ちょっとした仕様追加」がとてもやりやすくなりますね。

URLをコピーしました!