Unityを触り始めた頃、「とりあえず全部 Update() に書いておけば動くし楽!」となりがちですよね。プレイヤーの入力、移動、カメラ、HP管理、ゲームオーバー判定……すべてを1つの巨大スクリプトに詰め込んでしまうと、あとから「落下死ラインを追加したい」「敵にも同じ処理を使いたい」となったときに、どこを触ればいいか分からなくなってしまいます。

こういった「何でも屋スクリプト」を避けるには、機能ごとに小さなコンポーネントに分割していくのが効果的です。今回紹介する 「KillZ(落下死ライン)」コンポーネント は、Y座標が一定以下になったら、HPに関係なく即死処理を行う だけに責務を絞ったコンポーネントです。

プレイヤーでも敵でも、「落ちたら即アウト」 にしたいオブジェクトにこのコンポーネントをアタッチするだけで、シーン全体の落下死処理を統一できます。Updateにベタ書きしていた処理を切り出して、スッキリさせていきましょう。

【Unity】落下したら即アウト!「KillZ」コンポーネント

フルソースコード


using UnityEngine;

/// <summary> 
/// 「落下死ライン」を判定するコンポーネント。
/// - 一定のY座標より下に落ちたら即死処理を行う
/// - HPに関係なく強制的に「死」を通知する
/// - 「死に方」は別コンポーネントに任せる設計
/// 
/// 責務を「落下したかどうかの判定」に絞ることで、
/// プレイヤー・敵・オブジェクト問わず再利用しやすくしています。
/// </summary>
public class KillZ : MonoBehaviour
{
    [Header("落下死ライン設定")]
    [SerializeField]
    [Tooltip("このY座標より下に落ちたら即死扱いにします。")]
    private float killHeightY = -10f;

    [SerializeField]
    [Tooltip("判定を有効にするかどうか。デバッグ時に一時無効化したい場合に使います。")]
    private bool isEnabled = true;

    [Header("死亡処理のフック")]
    [SerializeField]
    [Tooltip("落下死したときに呼び出すコンポーネント。未指定の場合はGameObjectをDestroyします。")]
    private KillZDeathHandler deathHandler;

    [Header("リスポーン設定(任意)")]
    [SerializeField]
    [Tooltip("trueにすると、死亡前の位置を記録しておき、死亡後にそこへ戻す処理を呼び出せます。")]
    private bool recordSpawnPoint = false;

    // 最後に安全だった位置(recordSpawnPointがtrueのときだけ使用)
    private Vector3 lastSafePosition;

    private void Awake()
    {
        // deathHandler が未設定なら、同一オブジェクトから自動取得を試みる
        if (deathHandler == null)
        {
            deathHandler = GetComponent<KillZDeathHandler>();
        }

        // 初期位置を「安全な位置」として記録
        lastSafePosition = transform.position;
    }

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

        // 現在位置を監視し、一定Y以下なら即死処理
        if (transform.position.y <= killHeightY)
        {
            HandleDeathByFall();
        }
        else
        {
            // 安全圏内にいる間は、必要に応じて安全位置を更新
            if (recordSpawnPoint)
            {
                lastSafePosition = transform.position;
            }
        }
    }

    /// <summary>
    /// 落下による死亡処理を実行する。
    /// 具体的な「死に方」は KillZDeathHandler に委譲する。
    /// </summary>
    private void HandleDeathByFall()
    {
        // すでにDestroyされる途中などで二重呼び出しされないように、
        // isEnabled を false にしてガードするのもアリです。
        isEnabled = false;

        if (deathHandler != null)
        {
            // 具体的な死亡処理は別コンポーネントに任せる
            deathHandler.OnKilledByFall(this, lastSafePosition);
        }
        else
        {
            // DeathHandler が無い場合のデフォルト挙動:
            // ゲームオブジェクトを破壊してシンプルに消す
            Destroy(gameObject);
        }
    }

    /// <summary>
    /// 外部から KillZ の有効/無効を切り替えたい場合用のメソッド。
    /// 例:ゲーム開始演出中だけ落下死を無効にしたい、など。
    /// </summary>
    public void SetEnabled(bool enabled)
    {
        isEnabled = enabled;
    }

    /// <summary>
    /// 落下死ライン(Y座標)を動的に変更したい場合に使う。
    /// 例:ステージの高さが変わるギミックなど。
    /// </summary>
    public void SetKillHeight(float newHeightY)
    {
        killHeightY = newHeightY;
    }

    /// <summary>
    /// 現在の落下死ライン(Y座標)を取得。
    /// デバッグ表示などに使えます。
    /// </summary>
    public float GetKillHeight()
    {
        return killHeightY;
    }
}

/// <summary>
/// KillZ から呼び出される「落下死時の処理」をまとめたコンポーネント。
/// - プレイヤーならリスポーン
/// - 敵なら消滅+スコア加算
/// など、オブジェクトごとに自由に実装できます。
/// 
/// ここでは「プレイヤーを最後の安全位置に戻す」シンプルな例を実装しています。
/// </summary>
public class KillZDeathHandler : MonoBehaviour
{
    [Header("リスポーン設定")]
    [SerializeField]
    [Tooltip("落下死後に位置をリセットするかどうか。")]
    private bool respawnOnFall = true;

    [SerializeField]
    [Tooltip("リスポーン時に速度をリセットするかどうか(Rigidbodyがある場合)。")]
    private bool resetVelocityOnRespawn = true;

    [SerializeField]
    [Tooltip("リスポーン時に少しだけ上方向にオフセットして埋まり防止する距離。")]
    private float respawnYOffset = 0.5f;

    [Header("任意の演出")]
    [SerializeField]
    [Tooltip("落下死時に再生するパーティクル(任意)。指定しなければ何もしません。")]
    private ParticleSystem deathEffect;

    [SerializeField]
    [Tooltip("落下死時に再生するSE(任意)。指定しなければ何もしません。")]
    private AudioSource deathAudio;

    /// <summary>
    /// KillZ コンポーネントから呼ばれるコールバック。
    /// </summary>
    /// <param name="killZ">呼び出し元の KillZ コンポーネント</param>
    /// <param name="lastSafePosition">最後に安全だった位置</param>
    public void OnKilledByFall(KillZ killZ, Vector3 lastSafePosition)
    {
        // 演出(パーティクルやSE)の再生
        PlayEffects();

        if (respawnOnFall)
        {
            RespawnToLastSafePosition(lastSafePosition);
        }
        else
        {
            // リスポーンしない場合は、そのまま破壊してもよい
            Destroy(gameObject);
        }

        // KillZ を再度有効化しておく(リスポーン後も落下死が有効な場合)
        killZ.SetEnabled(true);
    }

    /// <summary>
    /// パーティクルとSEの再生をまとめた処理。
    /// </summary>
    private void PlayEffects()
    {
        if (deathEffect != null)
        {
            deathEffect.Play();
        }

        if (deathAudio != null)
        {
            deathAudio.Play();
        }
    }

    /// <summary>
    /// 最後の安全位置にリスポーンさせる処理。
    /// Rigidbody があれば速度もリセットします。
    /// </summary>
    /// <param name="lastSafePosition">最後に安全だった位置</param>
    private void RespawnToLastSafePosition(Vector3 lastSafePosition)
    {
        // 少しだけ上にオフセットして、地形にめり込まないようにする
        Vector3 respawnPosition = lastSafePosition + Vector3.up * respawnYOffset;
        transform.position = respawnPosition;

        // Rigidbody がアタッチされていれば、速度を0にする
        if (resetVelocityOnRespawn)
        {
            Rigidbody rb = GetComponent<Rigidbody>();
            if (rb != null)
            {
                rb.velocity = Vector3.zero;
                rb.angularVelocity = Vector3.zero;
            }
        }
    }
}

使い方の手順

  1. スクリプトファイルを作成する
    Unity の Project ウィンドウで KillZ.cs を作成し、上記のコードを丸ごとコピペして保存します。
    1つのファイル内に KillZKillZDeathHandler の2クラスが定義されていても問題ありません。
  2. プレイヤーにアタッチする
    例としてプレイヤーキャラクターに落下死を付けたい場合:
    • Hierarchy でプレイヤーの GameObject を選択
    • Add Component から KillZ を追加
    • 同じく KillZDeathHandler も追加
    • Kill Height Y に例えば -10 など、ステージの「奈落」より少し下の値を入れる
    • リスポーンさせたい場合は Record Spawn PointRespawn On Fall をオンにする

    これで、プレイヤーが Y = -10 以下まで落ちると、最後に安全だった位置に戻るようになります。

  3. 敵や動く床にも使う
    同じコンポーネントを敵キャラや動く足場にも使い回せます。
    • 敵キャラ: KillZ + KillZDeathHandler を敵のプレハブにアタッチし、Respawn On Fall をオフにすれば、「落ちたら消える敵」が簡単に作れます。
    • 動く床: エレベーターや動く足場に KillZ を付けておけば、何らかのバグで落下しても自動的に消えてくれるので、シーンがゴミだらけになるのを防げます。
  4. テストプレイで調整する
    Play モードで実際にプレイヤーを落下させてみて、Kill Height Y の値を調整しましょう。
    シーンビューのグリッドや地形の高さを見ながら、「ここまで落ちたら戻したい」というラインに合わせて数値を決めていくとやりやすいです。

メリットと応用

KillZ コンポーネントを用意しておくと、次のようなメリットがあります。

  • プレハブごとに挙動を変えやすい
    プレイヤー、敵、ギミックなど、同じ「落下死判定」を共有しつつ、「死んだときの挙動」だけを KillZDeathHandler 側で変えられます。
    例えばプレイヤーはリスポーン、敵は消滅+スコア加算、コインは単に消えるだけ、など。
  • レベルデザインがシンプルになる
    シーンに「巨大なトリガーコライダー」を敷き詰めて落下死を判定する方法もありますが、オブジェクトの数が増えると管理が大変です。
    KillZ なら「各オブジェクトが自分で落下を監視する」だけなので、シーン側はステージの高さを意識するだけで済みます。
  • 巨大な Update からの脱却
    「プレイヤー制御スクリプトの Update で、位置を見て落下死を判定する」といった処理を切り出せるので、プレイヤー制御スクリプトは「移動」「入力」「アニメーション」など、本来の責務に集中できます。

応用として、例えば「落下死したら残機を減らしてタイトルに戻す」といった処理も、KillZDeathHandler を差し替えるだけで実現できます。

最後に、改造案として「一定回数以上落下死したらゲームオーバーにする」ロジックの例を示します。
KillZDeathHandler に次のようなメソッドを追加してみましょう。


private int fallDeathCount = 0;
[SerializeField]
private int maxFallDeathCount = 3;

/// <summary>
/// 落下死回数をカウントし、一定回数を超えたらゲームオーバー処理を呼ぶ例。
/// 実際のシーン遷移やUI表示は、別のGameManagerコンポーネントに委譲するときれいです。
/// </summary>
private void CheckGameOverByFall()
{
    fallDeathCount++;

    if (fallDeathCount >= maxFallDeathCount)
    {
        // ここでゲームオーバー処理を呼び出す
        // 例: GameManager.Instance.GameOver();
        Debug.Log("落下死回数が上限に達しました。ゲームオーバー!");
    }
}

OnKilledByFall の最後で CheckGameOverByFall() を呼び出すようにすれば、「落下死が続いたらゲームオーバー」というルールを簡単に追加できます。
このように、小さなコンポーネントを組み合わせることで、柔軟かつ見通しの良いプロジェクト構成にしていきましょう。