【Unity】RespawnAnchor (復活地点) コンポーネントの作り方

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

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 を付ける

  1. プレイヤーの GameObject(例: Player)を選択します。
  2. Add Component から RespawnableCharacter を追加します。
  3. インスペクターで以下を設定します。
    • Initial Respawn Point:空欄でもOK(その場合、ゲーム開始位置が初期復活地点になります)。
      もしくは、空のGameObjectを作って「スタート地点」的に配置し、それを指定してもOKです。
    • Reset Velocity On Respawn:Rigidbodyで動かしている場合は On 推奨。
    • Respawn Delay Seconds:死亡から復活までの待ち時間。とりあえず 0 で大丈夫です。

プレイヤーにHP管理がある場合は、HPが0になったときに RespawnableCharacter.Kill() を呼ぶようにしておきましょう。
(この記事内のコードだけで完結させたい場合、簡易的に「落下死判定」を追加する例は後述の改造案で示します。)

手順②:RespawnAnchor を配置する(チェックポイントの設置)

  1. シーン上でチェックポイントにしたい位置に Empty Object を作成し、名前を RespawnAnchor_X などにします。
  2. そのオブジェクトに Collider を追加します(例:BoxCollider)。
  3. Collider の Is Trigger にチェックを入れます。
  4. Add Component から RespawnAnchor を追加します。
  5. インスペクターで以下を確認・設定します。
    • Target Tag:デフォルトは Player。プレイヤーのTagもPlayerに合わせておきましょう。
    • Activate Only Once:一度踏んだら二度と使えなくしたい場合は On
      何度でも「最後に踏んだ場所」が更新されてほしいなら Off のままでOKです。

これで、プレイヤーがそのアンカーに触れると、自動的に RespawnableCharacter.SetRespawnPoint() が呼ばれ、
以降の死亡時にはその地点から復活するようになります。

手順③:死亡時に Kill() を呼ぶ

死亡トリガーはプロジェクトによって様々ですが、ここでは例として「落下死エリア」を作る方法を説明します。

  1. ステージの一番下に大きな BoxCollider を配置し、Is Trigger をオンにします。
  2. 以下のような簡易スクリプトを作成し、その落下死エリアにアタッチします。

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 は復活地点の管理だけ」「見た目変更は別メソッド」というように役割を分けていく意識を持つと、プロジェクト全体がスッキリしてきますね。

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

URLをコピーしました!