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;
}
}
}
}
使い方の手順
-
スクリプトファイルを作成する
Unity の Project ウィンドウでKillZ.csを作成し、上記のコードを丸ごとコピペして保存します。
1つのファイル内にKillZとKillZDeathHandlerの2クラスが定義されていても問題ありません。 -
プレイヤーにアタッチする
例としてプレイヤーキャラクターに落下死を付けたい場合:- Hierarchy でプレイヤーの GameObject を選択
Add ComponentからKillZを追加- 同じく
KillZDeathHandlerも追加 Kill Height Yに例えば-10など、ステージの「奈落」より少し下の値を入れる- リスポーンさせたい場合は
Record Spawn PointとRespawn On Fallをオンにする
これで、プレイヤーが Y = -10 以下まで落ちると、最後に安全だった位置に戻るようになります。
-
敵や動く床にも使う
同じコンポーネントを敵キャラや動く足場にも使い回せます。- 敵キャラ:
KillZ+KillZDeathHandlerを敵のプレハブにアタッチし、Respawn On Fallをオフにすれば、「落ちたら消える敵」が簡単に作れます。 - 動く床: エレベーターや動く足場に
KillZを付けておけば、何らかのバグで落下しても自動的に消えてくれるので、シーンがゴミだらけになるのを防げます。
- 敵キャラ:
-
テストプレイで調整する
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() を呼び出すようにすれば、「落下死が続いたらゲームオーバー」というルールを簡単に追加できます。
このように、小さなコンポーネントを組み合わせることで、柔軟かつ見通しの良いプロジェクト構成にしていきましょう。
