Unityを触り始めた頃は、つい「とりあえず全部Updateに書いてしまう」ことが多いですよね。
プレイヤーの入力処理、移動、HP管理、リスポーン処理、UI更新……全部ひとつのスクリプトに詰め込んでしまうと、次のような問題が出てきます。
- ちょっと仕様を変えたいだけなのに、巨大なスクリプトの一部をいじる必要がある
- プレイヤー以外のオブジェクト(敵やトラップ)でも同じ処理を使いたいのに流用しづらい
- バグが出たときに原因箇所を特定しづらい
そこでおすすめなのが、「機能ごとに小さなコンポーネントとして分割する」考え方です。
今回は「プレイヤーが触れると現在位置を保存し、死亡時にその地点から復活する」という機能を、RespawnAnchor(復活地点)コンポーネントとして切り出してみましょう。
【Unity】どこからでもスマート復活!「RespawnAnchor」コンポーネント
この記事では、以下のような構成を目指します。
- RespawnAnchor:触れると「ここを復活地点にしてね」とプレイヤーに教えるコンポーネント
- RespawnableCharacter:死亡&リスポーン処理を担当するコンポーネント(プレイヤーや敵に付ける)
この2つを組み合わせることで、巨大なGodクラスを作らずに、
「復活地点の管理」と「実際の復活処理」をきれいに分離できます。
フルコード:RespawnAnchor & RespawnableCharacter
using UnityEngine;
namespace Sample.RespawnSystem
{
/// <summary>
/// 死亡とリスポーンを管理するコンポーネント。
/// プレイヤーや敵キャラなど、「復活」させたいオブジェクトにアタッチします。
///
/// - 現在のリスポーン地点(Transform)を保持
/// - ダメージや死亡判定は別コンポーネントから呼び出す想定
/// - RespawnAnchor からリスポーン地点を更新される
/// </summary>
[RequireComponent(typeof(Transform))]
public class RespawnableCharacter : MonoBehaviour
{
[Header("初期リスポーン位置設定")]
[SerializeField]
private Transform initialRespawnPoint;
// シーン開始時に使う初期リスポーン地点。
// 未設定なら「今いる位置」が初期地点になります。
[Header("リスポーン時の調整")]
[SerializeField]
private bool resetVelocityOnRespawn = true;
[SerializeField]
private float respawnDelaySeconds = 0.0f;
// 0 のときは即座にリスポーン
/// <summary>現在有効なリスポーン地点</summary>
private Transform currentRespawnPoint;
/// <summary>死亡中フラグ。連続で Kill() されるのを防ぐ用途などに。</summary>
public bool IsDead { get; private set; }
/// <summary>
/// 現在のリスポーン地点を外部から参照したい場合用のプロパティ
/// </summary>
public Transform CurrentRespawnPoint => currentRespawnPoint;
private Rigidbody rb; // 物理挙動をリセットしたい場合に利用
private void Awake()
{
// Rigidbody があればキャッシュしておく(任意)
rb = GetComponent<Rigidbody>();
// 初期リスポーン地点が指定されていなければ、現在位置を使う
if (initialRespawnPoint == null)
{
// 仮の空オブジェクトを生成して初期地点として扱う
GameObject point = new GameObject($"{name}_InitialRespawnPoint");
point.transform.position = transform.position;
point.transform.rotation = transform.rotation;
currentRespawnPoint = point.transform;
}
else
{
currentRespawnPoint = initialRespawnPoint;
}
}
/// <summary>
/// 外部(RespawnAnchor など)からリスポーン地点を更新するためのメソッド。
/// </summary>
/// <param name="newPoint">新しいリスポーン地点の Transform</param>
public void SetRespawnPoint(Transform newPoint)
{
if (newPoint == null) return;
currentRespawnPoint = newPoint;
}
/// <summary>
/// 「死亡した」ことを通知するメソッド。
/// HP管理コンポーネントなどから呼び出す想定です。
/// </summary>
public void Kill()
{
if (IsDead) return; // 二重呼び出し防止
IsDead = true;
// ここでアニメーションやエフェクト再生などを挟んでもよい
// 例: Animator に "Die" トリガーを送る など
if (respawnDelaySeconds > 0f)
{
// 指定秒数待ってからリスポーン
Invoke(nameof(Respawn), respawnDelaySeconds);
}
else
{
// 即座にリスポーン
Respawn();
}
}
/// <summary>
/// 実際にキャラクターをリスポーンさせる処理。
/// </summary>
public void Respawn()
{
if (currentRespawnPoint == null)
{
Debug.LogWarning(
$"[{nameof(RespawnableCharacter)}] currentRespawnPoint が設定されていません。" +
"Awake で初期化されているはずですが、何かの拍子に Destroy された可能性があります。",
this);
return;
}
// 位置と向きをリスポーン地点に戻す
transform.position = currentRespawnPoint.position;
transform.rotation = currentRespawnPoint.rotation;
// 物理挙動をリセットしたい場合
if (resetVelocityOnRespawn && rb != null)
{
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
}
// 死亡フラグ解除
IsDead = false;
}
}
/// <summary>
/// プレイヤーなどが触れると「ここをリスポーン地点にする」ためのアンカー。
///
/// - Trigger コライダーを持つオブジェクトにアタッチして使う
/// - RespawnableCharacter を持つオブジェクトが触れると、自身の Transform をリスポーン地点として登録する
/// - 一度踏んだら見た目を変える(ライトの色を変えるなど)用途にも拡張しやすい
/// </summary>
[RequireComponent(typeof(Collider))]
public class RespawnAnchor : MonoBehaviour
{
[Header("どのタグを持つオブジェクトを対象とするか")]
[SerializeField]
private string targetTag = "Player";
// 例: プレイヤーのみを対象にしたい場合は "Player" にする
[Header("一度だけ有効にするか")]
[SerializeField]
private bool activateOnlyOnce = false;
// true の場合、一度踏まれたら無効化される
[Header("デバッグ用表示")]
[SerializeField]
private Color gizmoColor = Color.green;
private bool isActivated = false;
private Collider anchorCollider;
private void Reset()
{
// コンポーネントを追加したタイミングで、Collider を Trigger にしておく
anchorCollider = GetComponent<Collider>();
if (anchorCollider != null)
{
anchorCollider.isTrigger = true;
}
// デフォルトで "Player" タグを対象にする
targetTag = "Player";
}
private void Awake()
{
anchorCollider = GetComponent<Collider>();
if (anchorCollider != null && !anchorCollider.isTrigger)
{
Debug.LogWarning(
$"[{nameof(RespawnAnchor)}] Collider は isTrigger = true にすることを推奨します。",
this);
}
}
private void OnTriggerEnter(Collider other)
{
// すでに一度有効化されていて、再利用不可設定なら何もしない
if (activateOnlyOnce && isActivated) return;
// タグで対象を絞り込む
if (!string.IsNullOrEmpty(targetTag) && !other.CompareTag(targetTag))
{
return;
}
// 触れてきたオブジェクトに RespawnableCharacter が付いているか調べる
RespawnableCharacter respawnable = other.GetComponent<RespawnableCharacter>();
if (respawnable == null)
{
// 子オブジェクトなどに付いている可能性もあるので、GetComponentInParent も試す
respawnable = other.GetComponentInParent<RespawnableCharacter>();
}
if (respawnable == null)
{
// 対象オブジェクトに RespawnableCharacter が付いていなければ何もしない
return;
}
// 自分自身の Transform をリスポーン地点として登録
respawnable.SetRespawnPoint(transform);
isActivated = true;
// ここで見た目の変更などを行うと、「踏んだ」ことがわかりやすくなります。
// 例:
// ChangeVisualOnActivate();
}
/// <summary>
/// シーンビュー上でアンカー位置をわかりやすくするためのギズモ描画。
/// ゲームプレイには影響しません。
/// </summary>
private void OnDrawGizmos()
{
Gizmos.color = gizmoColor;
Gizmos.DrawSphere(transform.position, 0.3f);
// 上方向に小さな矢印を描く
Vector3 up = transform.up * 0.7f;
Gizmos.DrawLine(transform.position, transform.position + up);
}
/// <summary>
/// 外部から「このアンカーをリセット(再び有効化)したい」場合に呼び出すメソッド。
/// </summary>
public void ResetActivation()
{
isActivated = false;
}
}
}
使い方の手順
ここでは「プレイヤーが落下死したら、最後に触れたRespawnAnchorから復活する」例で説明します。
手順①:プレイヤーに RespawnableCharacter を付ける
- プレイヤーの GameObject(例:
Player)を選択します。 - Add Component から
RespawnableCharacterを追加します。 -
インスペクターで以下を設定します。
- Initial Respawn Point:空欄でもOK(その場合、ゲーム開始位置が初期復活地点になります)。
もしくは、空のGameObjectを作って「スタート地点」的に配置し、それを指定してもOKです。 - Reset Velocity On Respawn:Rigidbodyで動かしている場合は
On推奨。 - Respawn Delay Seconds:死亡から復活までの待ち時間。とりあえず
0で大丈夫です。
- Initial Respawn Point:空欄でもOK(その場合、ゲーム開始位置が初期復活地点になります)。
プレイヤーにHP管理がある場合は、HPが0になったときに RespawnableCharacter.Kill() を呼ぶようにしておきましょう。
(この記事内のコードだけで完結させたい場合、簡易的に「落下死判定」を追加する例は後述の改造案で示します。)
手順②:RespawnAnchor を配置する(チェックポイントの設置)
- シーン上でチェックポイントにしたい位置に Empty Object を作成し、名前を
RespawnAnchor_Xなどにします。 - そのオブジェクトに Collider を追加します(例:
BoxCollider)。 - Collider の Is Trigger にチェックを入れます。
- Add Component から
RespawnAnchorを追加します。 -
インスペクターで以下を確認・設定します。
- Target Tag:デフォルトは
Player。プレイヤーのTagもPlayerに合わせておきましょう。 - Activate Only Once:一度踏んだら二度と使えなくしたい場合は
On。
何度でも「最後に踏んだ場所」が更新されてほしいならOffのままでOKです。
- Target Tag:デフォルトは
これで、プレイヤーがそのアンカーに触れると、自動的に RespawnableCharacter.SetRespawnPoint() が呼ばれ、
以降の死亡時にはその地点から復活するようになります。
手順③:死亡時に Kill() を呼ぶ
死亡トリガーはプロジェクトによって様々ですが、ここでは例として「落下死エリア」を作る方法を説明します。
- ステージの一番下に大きな BoxCollider を配置し、Is Trigger をオンにします。
- 以下のような簡易スクリプトを作成し、その落下死エリアにアタッチします。
using UnityEngine;
using Sample.RespawnSystem;
public class KillZone : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
// 触れてきたオブジェクトに RespawnableCharacter が付いていれば Kill する
RespawnableCharacter respawnable = other.GetComponent<RespawnableCharacter>();
if (respawnable == null)
{
respawnable = other.GetComponentInParent<RespawnableCharacter>();
}
if (respawnable != null)
{
respawnable.Kill();
}
}
}
これで、プレイヤーが落下して KillZone に触れると、
最後に触れた RespawnAnchor の位置から復活するようになります。
手順④:敵や動く床にも応用してみる
- 敵キャラ:敵にも
RespawnableCharacterを付けておけば、
「倒された敵が一定時間後にどこから復活するか」を同じ仕組みで管理できます。 - 動く床:動く足場に
RespawnableCharacterを付けておけば、
「落ちたら足場のスタート位置に戻る」ようなギミックも作れます。
足場自身が特定のRespawnAnchorを踏みに行くスクリプトを組み合わせると、
レベルデザインの自由度が上がります。
メリットと応用
この構成のポイントは、「復活地点の保存」と「復活処理」を完全に分離していることです。
- プレハブ管理が楽になる
プレイヤーのプレハブにRespawnableCharacterを一度設定しておけば、
どのシーンに持っていっても同じインターフェースで復活させられます。
シーンごとに「復活ロジック」を書き直す必要がありません。 - レベルデザインが直感的になる
RespawnAnchor は「ここから復活させたい場所」にポンポン置くだけでOKです。
プログラマーがいちいち座標をコードに書かなくても、レベルデザイナーがシーン上で配置していくだけでチェックポイントを増やせます。 - Godクラスを避けられる
プレイヤーのスクリプトに「HP管理・入力・移動・アニメーション・リスポーン」などを全部詰め込まず、
それぞれを小さなコンポーネントに分けることで、テストや差し替えがしやすくなります。 - 敵やギミックにも水平展開しやすい
RespawnableCharacterは「リスポーンできるもの」という抽象的な役割なので、
プレイヤーだけでなく、敵、オブジェクト、ギミックなどにも簡単に流用できます。
改造案:RespawnAnchor を踏んだら見た目を変える
RespawnAnchor を踏んだときに、たとえばマテリアルの色を変えて「アクティブ化された」ことを視覚的に示したい場合、
以下のようなメソッドを RespawnAnchor に追加してみましょう。
/// <summary>
/// アンカーが有効化されたときに見た目を変える簡易例。
/// Renderer のマテリアルカラーを変えるだけのシンプルな実装です。
/// </summary>
private void ChangeVisualOnActivate()
{
Renderer renderer = GetComponent<Renderer>();
if (renderer == null) return;
// すでに複製済みのマテリアルを使っている前提。
// 共有マテリアルを直接書き換えないように注意しましょう。
renderer.material.color = Color.yellow;
}
そして OnTriggerEnter の最後にあるコメント部分を、次のように差し替えます。
// 見た目の変更で「踏んだ」ことをわかりやすくする
ChangeVisualOnActivate();
このように、小さなコンポーネント+小さなメソッド単位で機能を追加していくと、
後からの改造や差し替えがとても楽になります。
「RespawnAnchor は復活地点の管理だけ」「見た目変更は別メソッド」というように役割を分けていく意識を持つと、プロジェクト全体がスッキリしてきますね。




