DontDestroyOnLoadでプレイヤーをシーン間に保持する

広告

はじめに

Unityでゲームを作っていると、シーンを切り替えるたびにキャラクターが消えてしまうことがあります。

たとえば、タイトル画面からゲーム画面に移ったとき、プレイヤーが最初から作り直されてしまう——そんな問題を解決するのが DontDestroyOnLoad です。

このチュートリアルでは、2つのシーン(TitleScene と GameScene) の間でプレイヤーを維持する方法を、スクリプト1本だけでシンプルに学びます。


対象者

  • Unity を使いはじめたばかりの方
  • シーン切り替えを初めて試す方
  • スクリプトの基本(MonoBehaviour、Awake)を少し知っている方

完成イメージ

  1. TitleScene を再生する
  2. 「スタート」ボタンを押すと GameScene に切り替わる
  3. プレイヤーは消えずに GameScene にも表示されている

プロジェクトの準備

シーンを2つ用意する

  • TitleScene(タイトル画面)
  • GameScene(ゲーム画面)

File > Build Settings を開き、この2つのシーンを登録してください。

TitleScene を 0 番目に登録しておくと、再生したときに最初に表示されます。


プレイヤーを作る

  1. TitleScene を開く
  2. Hierarchy で 右クリック > 3D Object > Capsule を作成する
  3. 名前を Player に変更する
  4. Player を Assets フォルダにドラッグ して Prefab にする

スクリプトを書く

PlayerController.cs という名前でスクリプトを作成し、以下のコードを書きます。

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    static PlayerController instance;

    void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}

書いたら、Prefab の Player にアタッチしてください。

コードの意味

コード意味
void Awake()オブジェクトが生まれた瞬間に呼ばれる処理
static PlayerController instance「いま存在しているプレイヤー」を記憶しておく場所
if (instance == null)まだ誰もいなければ、自分を登録してシーンをまたいで残す
Destroy(gameObject)すでに1体いれば、新しく生成されたほうを削除する

DontDestroyOnLoad と組み合わせることで、常に1体だけがシーンをまたいで生き続ける仕組みになります。


スタートボタンを作る

TitleScene にボタンを置いて、ゲームシーンへ移動できるようにします。

手順

  1. Hierarchy で 右クリック > UI > Button – TextMeshPro を作成する
  2. Button の文字を「スタート」に変更する
  3. TitleScene にある 空のGameObject(例:SceneChanger)に、以下のスクリプトをアタッチする
using UnityEngine;
using UnityEngine.SceneManagement;

public class TitleSceneController : MonoBehaviour
{
    public void OnStartButtonClicked()
    {
        SceneManager.LoadScene("GameScene");
    }
}
  1. Button の OnClick() に SceneChanger オブジェクトをドラッグし、TitleSceneController > OnStartButtonClicked を選択する

プレイヤーを TitleScene に置く

  1. TitleScene の Hierarchy に Player Prefab をドラッグして配置する
  2. 再生ボタンを押し、スタートボタンをクリックする
  3. GameScene に切り替わった後も Player が Hierarchy に残っていれば成功です

Hierarchy の Player オブジェクトが DontDestroyOnLoad というグループに移動して表示されていたら、正しく機能しています。


よくある疑問

Q. シーンを切り替えるたびにプレイヤーが増えてしまいます

TitleScene に戻ったとき、すでに存在するプレイヤーに加えて新しいプレイヤーが生成されると起きます。

PlayerController.cs を以下のように書き換えると、2体目以降は自動で削除されます。

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // 今存在しているプレイヤーを記憶しておく場所
    static PlayerController instance;

    void Awake()
    {
        if (instance == null)
        {
            // はじめて生成されたときだけ、自分を記憶してシーンをまたいで残す
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            // すでに1体いる場合は、新しく生成されたほうを即座に削除する
            Destroy(gameObject);
        }
    }
}

流れのイメージ

  1. TitleScene でプレイヤーが生成される → instance に記憶 → シーンをまたいで維持
  2. TitleScene に戻り、再びプレイヤーが生成される → instance がすでにいる → 新しいほうを削除

これにより、常に1体だけが存在し続けます。

Q. GameScene でプレイヤーの位置がズレます

シーンをまたいでオブジェクトを維持すると、前のシーンにいた座標のまま引き継がれます。GameScene の正しい位置にプレイヤーを移動させるには、出現地点(SpawnPoint) を使います。

SpawnPoint を用意する

  1. GameScene を開く
  2. Hierarchy で 右クリック > Create Empty を作成し、名前を SpawnPoint にする
  3. プレイヤーを出現させたい位置に移動する
  4. Inspector で Tag を SpawnPoint に設定する

Tag の設定:Inspector 上部の「Tag」ドロップダウン → Add Tag → + で SpawnPoint を追加 → 再度 SpawnPoint オブジェクトを選んで Tag を設定

PlayerController.cs に位置移動の処理を追加する

using UnityEngine;
using UnityEngine.SceneManagement;

public class PlayerController : MonoBehaviour
{
    static PlayerController instance;

    void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);

            // シーンが切り替わったときに呼ばれるイベントを登録する
            SceneManager.sceneLoaded += OnSceneLoaded;
        }
        else
        {
            Destroy(gameObject);
        }
    }

    // シーンが読み込まれるたびに自動で呼ばれる
    void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        // SpawnPoint タグのついたオブジェクトを探す
        GameObject spawnPoint = GameObject.FindWithTag("SpawnPoint");

        if (spawnPoint != null)
        {
            // 見つかったら、その位置にプレイヤーを移動する
            transform.position = spawnPoint.transform.position;
        }
    }
}

コードの意味

コード意味
SceneManager.sceneLoaded += OnSceneLoadedシーンが読み込まれたとき、OnSceneLoaded を呼ぶよう登録する
OnSceneLoaded(Scene scene, LoadSceneMode mode)シーンが切り替わるたびに自動で実行される処理
GameObject.FindWithTag("SpawnPoint")SpawnPoint タグのついたオブジェクトを探す
transform.position = spawnPoint.transform.positionプレイヤーの位置を SpawnPoint の位置に合わせる

流れのイメージ

  1. スタートボタンを押す → GameScene に切り替わる
  2. OnSceneLoaded が自動で呼ばれる
  3. GameScene 内の SpawnPoint を探して、その位置にプレイヤーが移動する

SpawnPoint が見つからない場合(タグの設定ミスなど)は、移動せずにそのままの位置に留まります。まず SpawnPoint のタグが正しく設定されているか確認してください。


シーン間のアクセスについて

DontDestroyOnLoad で維持されているオブジェクトと、通常のシーンにあるオブジェクトはお互いに普通にアクセスできます

DontDestroy側 → 通常シーン側

今回の OnSceneLoaded 内の処理がまさにこれです。プレイヤー(DontDestroy)側から、GameScene にある SpawnPoint を探しています。

// PlayerController(DontDestroy)から、GameScene の SpawnPoint を探す
GameObject spawnPoint = GameObject.FindWithTag("SpawnPoint");

通常シーン側 → DontDestroy側

GameScene 側のスクリプトからプレイヤーにアクセスする場合、instance を public static にすることで直接参照できます。

まず PlayerController.cs の instance を public に変更します。

public static PlayerController instance; // public を追加

これで、GameScene 側のスクリプトからこのように書けます。

// タグで探す方法
GameObject player = GameObject.FindWithTag("Player");

// instance 経由で直接アクセスする方法(public にした場合)
PlayerController.instance.gameObject.SetActive(false);

instance を public にするかどうかは用途次第です。他のスクリプトから触る必要がなければ、private(何も書かない)のままで問題ありません。


まとめ

やったこと方法
プレイヤーをシーン間で維持するDontDestroyOnLoad(gameObject) を Awake で呼ぶ
重複生成を防ぐstatic 変数で1体だけを管理し、2体目は Destroy する
シーンを切り替えるSceneManager.LoadScene("シーン名")
シーン切り替え後に正しい位置へ移動するSceneManager.sceneLoaded イベントで SpawnPoint の位置に合わせる
DontDestroy → 通常シーンへアクセスGameObject.FindWithTag などでそのまま検索できる
通常シーン → DontDestroyへアクセスinstance を public static にして PlayerController.instance で参照する

DontDestroyOnLoad は、「このオブジェクトはシーンが変わっても消さないでください」 とUnityに伝える命令です。

たった1行で、プレイヤーがゲーム全体を通じて生き続けるようになります。まずはこれだけ覚えておけば十分です。

訪問数 8 回, 今日の訪問数 8回

広告

Unity

Posted by hidepon