DontDestroyOnLoadでプレイヤーをシーン間に保持する
はじめに
Unityでゲームを作っていると、シーンを切り替えるたびにキャラクターが消えてしまうことがあります。
たとえば、タイトル画面からゲーム画面に移ったとき、プレイヤーが最初から作り直されてしまう——そんな問題を解決するのが DontDestroyOnLoad です。
このチュートリアルでは、2つのシーン(TitleScene と GameScene) の間でプレイヤーを維持する方法を、スクリプト1本だけでシンプルに学びます。
対象者
- Unity を使いはじめたばかりの方
- シーン切り替えを初めて試す方
- スクリプトの基本(MonoBehaviour、Awake)を少し知っている方
完成イメージ
- TitleScene を再生する
- 「スタート」ボタンを押すと GameScene に切り替わる
- プレイヤーは消えずに GameScene にも表示されている
プロジェクトの準備
シーンを2つ用意する
TitleScene(タイトル画面)GameScene(ゲーム画面)
File > Build Settings を開き、この2つのシーンを登録してください。
TitleScene を 0 番目に登録しておくと、再生したときに最初に表示されます。
プレイヤーを作る
- TitleScene を開く
- Hierarchy で 右クリック > 3D Object > Capsule を作成する
- 名前を
Playerに変更する 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 にボタンを置いて、ゲームシーンへ移動できるようにします。
手順
- Hierarchy で 右クリック > UI > Button – TextMeshPro を作成する
- Button の文字を「スタート」に変更する
TitleSceneにある 空のGameObject(例:SceneChanger)に、以下のスクリプトをアタッチする
using UnityEngine;
using UnityEngine.SceneManagement;
public class TitleSceneController : MonoBehaviour
{
public void OnStartButtonClicked()
{
SceneManager.LoadScene("GameScene");
}
}
- Button の OnClick() に
SceneChangerオブジェクトをドラッグし、TitleSceneController > OnStartButtonClickedを選択する
プレイヤーを TitleScene に置く
- TitleScene の Hierarchy に
PlayerPrefab をドラッグして配置する - 再生ボタンを押し、スタートボタンをクリックする
- 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);
}
}
}
流れのイメージ
- TitleScene でプレイヤーが生成される →
instanceに記憶 → シーンをまたいで維持 - TitleScene に戻り、再びプレイヤーが生成される →
instanceがすでにいる → 新しいほうを削除
これにより、常に1体だけが存在し続けます。
Q. GameScene でプレイヤーの位置がズレます
シーンをまたいでオブジェクトを維持すると、前のシーンにいた座標のまま引き継がれます。GameScene の正しい位置にプレイヤーを移動させるには、出現地点(SpawnPoint) を使います。
SpawnPoint を用意する
- GameScene を開く
- Hierarchy で 右クリック > Create Empty を作成し、名前を
SpawnPointにする - プレイヤーを出現させたい位置に移動する
- 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 の位置に合わせる |
流れのイメージ
- スタートボタンを押す → GameScene に切り替わる
OnSceneLoadedが自動で呼ばれる- 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行で、プレイヤーがゲーム全体を通じて生き続けるようになります。まずはこれだけ覚えておけば十分です。



ディスカッション
コメント一覧
まだ、コメントがありません