スポンサーリンク
エラー・トラブルシューティング

UnityでDontDestroyOnLoadが効かない原因と対処法【シーン管理の落とし穴】

エラー・トラブルシューティング

Unityでシーンを切り替えたときに、DontDestroyOnLoadを使ったはずのオブジェクトが消えたり、逆にManagerやBGMが増えたりすると、原因の切り分けがかなり難しく感じますよね。

特に多いのは、次のような症状です。

  • シーンを切り替えると、GameManagerやAudioManagerが消える
  • Inspectorで設定していたUIやCameraの参照がMissingになる
  • タイトル画面に戻るたびにBGMが二重、三重に鳴る
  • 前のシーンのCanvasやUIが残って、次のシーンに重なって表示される

こうしたトラブルは、DontDestroyOnLoadそのものが壊れているというより、保持する対象・初期化の順番・参照先の寿命・重複生成のどこかでズレが起きているケースが多いです。

たとえば、Managerだけを残したいのに、その子オブジェクトとしてタイトル画面用のCanvasまで置いていると、Canvasも一緒に次のシーンへ残ってしまいます。まるで「荷物だけ持っていくつもりが、部屋ごと引っ越してきた」みたいな状態です。

また、常駐するManagerが前のシーンにあるTextやButtonを直接参照していると、Managerは残っているのに参照先だけがシーン破棄で消えて、Missingになります。この場合は、DontDestroyOnLoadの書き方よりも、Managerに何を持たせるかを見直す必要があります。

まずは「消える」「Missingになる」「増える」「古いUIが残る」のどれに当てはまるかを見てください。症状を分けて考えると、原因はかなり絞りやすくなります。

シーン切り替え自体の基本がまだあいまいな場合は、先にコチラの記事を確認しておくと、以降の内容が理解しやすくなります。


  1. 結論:まず確認すべきポイントは4つです
  2. 原因1:保持する対象を間違えている
    1. 症状:シーン切り替え後にManagerやPlayerが消える
    2. 原因:ComponentとGameObjectを混同している
    3. 解決方法:残す対象を1つに絞る
    4. 注意点:子オブジェクトも一緒に残る
  3. 原因2:AwakeとStartの使い分けを間違えている
    1. 症状:GameManager.Instanceがnullになる
    2. 原因:Startで登録すると参照に間に合わない場合がある
    3. 解決方法:Instance登録と保持処理はAwakeにまとめる
    4. 注意点:UIやCameraの取得は分けて考える
  4. 原因3:常駐Managerがシーン内オブジェクトを直接参照している
    1. 症状:Inspectorの参照がMissingになる
    2. 原因:ManagerとUIの寿命が違う
    3. 解決方法:UI側からManagerへ接続する
    4. 注意点:Managerに何でも集めすぎない
  5. 原因4:シーン再読み込みでManagerが重複生成されている
    1. 症状:BGMが二重に鳴る・処理が複数回走る
    2. 原因:各シーンに同じManagerを配置している
    3. 解決方法:シングルトンで新しい方を破棄する
    4. 注意点:シングルトンは使いどころを絞る
  6. 原因5:イベント解除忘れで古い参照が残っている
    1. 症状:シーンをまたぐたびにイベント処理が増える
    2. 原因:常駐Managerに登録したイベントを解除していない
    3. 解決方法:OnEnableとOnDisableをセットで使う
    4. 注意点:OnDestroyだけに頼ると見落とす場合がある
  7. 3分で確認できる診断チェックリスト
  8. よくある誤解と注意点
    1. 誤解1:DontDestroyOnLoadを使えば何でも安全に残せる
    2. 誤解2:ManagerがUIを直接持つのが普通
    3. 誤解3:Startで初期化すれば十分
    4. 誤解4:シングルトンにすれば全部解決する
    5. 誤解5:オブジェクトが増えるのはUnityの不具合
  9. まとめ:DontDestroyOnLoadは残す対象と参照の寿命を分けて考える
  10. よくある質問(FAQ)
    1. 関連記事:

結論:まず確認すべきポイントは4つです

DontDestroyOnLoadが効かないように見えるときは、いきなりコード全体を疑うよりも、まず原因を4つに分けて確認するのがおすすめです。シーン跨ぎの不具合は、症状ごとに見る場所がかなり変わります。

症状まず疑う原因確認する場所
オブジェクトが消える保持する対象を間違えているDontDestroyOnLoad(gameObject)になっているか
Instanceがnullになる初期化の順番が遅いAwakeで登録しているか
参照がMissingになるシーン内オブジェクトを直接参照しているManagerがUIやCameraを持っていないか
BGMやManagerが増える重複生成を防げていないシングルトンで新しい方を破棄しているか

最初に見るべきなのは、DontDestroyOnLoadを呼んでいる対象です。基本は、保持したいオブジェクト自身に対してDontDestroyOnLoad(gameObject)を実行します。スクリプトだけを残す、という感覚ではなく「そのスクリプトが付いているGameObjectを残す」と考えると分かりやすいです。

次に確認したいのが、AwakeStartの使い分けです。シングルトンのInstance登録やDontDestroyOnLoadの呼び出しをStartで行うと、他のスクリプトが先にAwakeで参照してしまい、nullになる場合があります。

さらに、Managerが前のシーンのUIやCameraを直接持っている場合も要注意です。Managerは残っても、UIやCameraはシーン切り替えで破棄されるため、参照だけがMissingになります。これはかなりよくある「残したのに壊れる」パターンです。

最後に、シーンを戻ったときにManagerやBGMが増えるなら、重複生成を疑ってください。各シーンに同じManagerを置いていると、古いManagerが残ったまま新しいManagerが作られます。BGMが輪唱みたいになったら、だいたいこのパターンです。

判断の目安として、消えるなら対象、nullになるなら初期化順、Missingなら参照の寿命、増えるなら重複生成を疑うと、原因をかなり早く絞れます。




原因1:保持する対象を間違えている

DontDestroyOnLoadを書いたのにオブジェクトが消える場合、まず確認したいのは「何に対してDontDestroyOnLoadを呼んでいるか」です。ここがズレていると、処理を書いているつもりでも、残したいオブジェクトに正しく適用されていないことがあります。

症状:シーン切り替え後にManagerやPlayerが消える

よくある症状は、シーンを切り替えたあとにGameManagerPlayerがHierarchyから消えてしまうパターンです。ほかのスクリプトからGameManager.Instanceを呼んだときにnullになったり、シーン遷移後に処理が止まったりします。

この場合、コードの場所だけでなく、対象のオブジェクトを確認してください。Unityでは、スクリプトはComponent、それがアタッチされている本体はGameObjectです。この2つを混同すると、思った通りに保持できない原因になります。

原因:ComponentとGameObjectを混同している

初心者がつまずきやすいのは、thisを「このオブジェクト」と考えてしまうことです。MonoBehaviourの中で使うthisは、基本的にはそのスクリプト自身を指します。残したいのはスクリプト単体ではなく、スクリプトが付いているGameObjectです。

そのため、基本形は次のように書きます。

void Awake()
{
    DontDestroyOnLoad(gameObject);
}

gameObjectと書くことで、「このスクリプトが付いているGameObjectをシーン切り替え後も残す」という意味になります。まずはここが正しく書けているかを見てください。

解決方法:残す対象を1つに絞る

保持する対象は、できるだけ明確にしましょう。たとえば、ゲーム全体の状態を管理するGameManagerを残したいなら、GameManagerが付いているGameObjectだけを残すのが基本です。

一方で、タイトル画面用のCanvasや、ステージ専用の敵、弾、エフェクトまで一緒に残すと、次のシーンで不要なものが表示されたり、メモリ管理が難しくなったりします。

  • 残してよい例:GameManager、AudioManager、SaveManager
  • 注意が必要な例:Player、Camera、Canvas
  • 基本的に残さない例:敵、弾、エフェクト、ステージ専用UI

注意点:子オブジェクトも一緒に残る

DontDestroyOnLoadを使うと、そのGameObjectだけでなく、子オブジェクトも一緒に残ります。ここはかなり大事です。

たとえば、GameManagerの子にタイトル画面用のCanvasを置いていると、シーンを切り替えたあともCanvasが残ります。その結果、ゲーム画面にタイトルUIが重なって表示されることがあります。荷物だけ持っていくつもりが、棚やソファまで全部持ってきたような状態ですね。

判断基準としては、次のシーンでも本当に必要なものだけを親子ごと残すと考えてください。Managerの下に便利だから何でも入れるのではなく、シーン専用のUIやCameraは別の階層に置く方が安全です。




原因2:AwakeとStartの使い分けを間違えている

DontDestroyOnLoadを使っているのに、シーン開始直後だけInstancenullになる場合は、初期化の順番を疑ってください。特に、シングルトンの登録やDontDestroyOnLoadの呼び出しをStartに書いていると、他のスクリプトから参照されるタイミングに間に合わないことがあります。

症状:GameManager.Instanceがnullになる

よくあるのは、別のスクリプトのAwakeOnEnableGameManager.Instanceを使おうとして、NullReferenceExceptionが出るパターンです。何度か再生すると動くこともあるため、「あれ、さっきは動いたのに?」と迷いやすいところです。

この症状は、DontDestroyOnLoadが効いていないというより、Manager側の準備が終わる前に、他のスクリプトがManagerを見に行っている状態です。

原因:Startで登録すると参照に間に合わない場合がある

Unityでは、基本的にAwakeStartより先に呼ばれます。そのため、ManagerのStartInstanceを登録していると、他のオブジェクトのAwakeから参照されたときに、まだInstanceが入っていないことがあります。

ざっくり分けるなら、Awakeは「自分自身の準備」、Startは「他のオブジェクトとの連携」に使うと考えると整理しやすいです。

処理置き場所の目安
Instanceの登録Awake
DontDestroyOnLoadの実行Awake
重複チェックAwake
UIやCameraとの接続Start以降

解決方法:Instance登録と保持処理はAwakeにまとめる

シングルトンを使う場合は、まずAwakeInstance登録、重複チェック、DontDestroyOnLoadをまとめて行います。

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}

ポイントは、重複していた場合にDestroy(gameObject)したあと、必ずreturnで処理を止めることです。破棄予定のオブジェクトで初期化処理を続けてしまうと、思わぬ参照ミスにつながる場合があります。

注意点:UIやCameraの取得は分けて考える

Awakeで何でも済ませようとすると、今度はシーン内のUIやCameraがまだ準備できていないことがあります。Manager自身の準備はAwake、シーン内オブジェクトとの接続はStart以降、と分けるのが扱いやすいです。

実際の制作でも、私は「ManagerはAwakeで自分を登録するだけ」「UIはStartでManagerから値を読む」という形にすることが多いです。この分け方にしておくと、シーンを増やしたときにも原因を追いやすくなります。

実行タイミングの違いをもう少し深く知りたい場合は、コチラの記事も参考になります。




原因3:常駐Managerがシーン内オブジェクトを直接参照している

DontDestroyOnLoadでManager自体は残っているのに、UIやCameraの参照だけがMissingになる場合は、参照先の寿命を確認してください。ここはシーン跨ぎの不具合でかなりハマりやすいポイントです。

症状:Inspectorの参照がMissingになる

たとえば、GameManagerにスコア表示用のTextTextMeshProUGUI、ボタン、CameraなどをInspectorから直接入れているとします。

タイトルシーンでは問題なく動いていても、ゲームシーンへ移動した瞬間に、その参照がMissingになることがあります。Managerは残っているのに、参照していたUIやCameraだけが消えてしまう状態です。

public class GameManager : MonoBehaviour
{
    public TMPro.TextMeshProUGUI scoreText;
}

このように、常駐するManagerが「特定のシーンにだけ存在するUI」を直接持っていると、シーン切り替え時に参照切れが起きやすくなります。

原因:ManagerとUIの寿命が違う

DontDestroyOnLoadしたManagerは、シーンを切り替えても残ります。一方で、そのシーンに配置されていたCanvas、Text、Button、Cameraなどは、通常のシーン切り替えで破棄されます。

つまり、Managerは次のシーンへ引っ越しますが、参照していたUIは前のシーンに置いていかれます。その結果、Manager側には「もう存在しないUIへの参照」だけが残ります。

これはDontDestroyOnLoadが効いていないのではなく、残るものと消えるものを直接つないでしまっていることが原因です。

解決方法:UI側からManagerへ接続する

おすすめは、ManagerにUIを直接持たせるのではなく、新しいシーンにあるUI側からManagerへ接続する形です。Managerはスコアやゲーム状態などの「データ」を持ち、UIはそれを表示する役に分けます。

public class ScoreUI : MonoBehaviour
{
    [SerializeField] private TMPro.TextMeshProUGUI scoreText;

    private void Start()
    {
        if (GameManager.Instance != null)
        {
            scoreText.text = GameManager.Instance.Score.ToString();
        }
    }
}

この形なら、シーンが変わってUIが作り直されても、新しいUIがStartでManagerから値を読み直せます。Manager側が古いUIを抱え続けないので、Missingも起きにくくなります。

注意点:Managerに何でも集めすぎない

便利だからといって、GameManagerにUI、Camera、Audio、シーン遷移、セーブ処理を全部入れると、あとから原因を追うのが大変になります。最初は動いても、シーンが増えたあたりで「あれ、誰が何を持ってるの?」となりがちです。

判断基準としては、次のように分けると扱いやすいです。

  • Manager:スコア、進行状態、設定値など、シーンを跨いで残したい情報
  • UI:Text、Button、Panelなど、そのシーンで表示するもの
  • Camera:基本はシーンごとに管理し、必要なときだけ接続するもの

Managerが前のシーンのUIをInspectorで直接持っていたら、参照切れの黄色信号です。UI側からManagerを見に行く、またはイベントで通知する形に変えると、シーン跨ぎでも安定しやすくなります。

他のスクリプトから値を取得する基本を整理したい場合は、コチラの記事もあわせて確認しておくと理解しやすいです。




原因4:シーン再読み込みでManagerが重複生成されている

DontDestroyOnLoadでオブジェクトを残せるようになったあとに起きやすいのが、今度はManagerやBGMが増えていく問題です。消えないようにしたはずなのに、シーンを行き来するたびに増える。これはシーン跨ぎ管理でかなり定番の落とし穴です。

症状:BGMが二重に鳴る・処理が複数回走る

たとえば、タイトルシーンにAudioManagerを置き、ゲームシーンにも同じAudioManagerを置いているとします。最初は問題なく見えても、タイトルへ戻ったときに古いAudioManagerが残ったまま、新しいAudioManagerが作られることがあります。

その結果、BGMが二重に鳴ったり、スコア加算処理が2回走ったり、イベント通知が何度も呼ばれたりします。BGMが急に合唱を始めたら、だいたい重複生成を疑ってよいです。

原因:各シーンに同じManagerを配置している

DontDestroyOnLoadしたManagerは、シーンを切り替えても残ります。ところが、遷移先のシーンにも同じManagerが置かれていると、新しいManagerも生成されます。

つまり、古いManagerと新しいManagerが同時に存在する状態になります。Unityが勝手に増やしているというより、「前のManagerを残したまま、次のシーンでもう1個作っている」という状態です。

解決方法:シングルトンで新しい方を破棄する

ゲーム全体で1つだけあればよいManagerには、シングルトンを使って重複を防ぐ方法がよく使われます。考え方はシンプルで、「すでに本物がいるなら、新しく作られた自分を消す」という流れです。

public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}

ここで大事なのは、Destroy(gameObject)のあとにreturnすることです。破棄する予定のManagerで初期化処理やイベント登録を続けると、あとから原因不明の多重実行につながる場合があります。

注意点:シングルトンは使いどころを絞る

シングルトンは便利ですが、何でもシングルトンにすると依存関係が複雑になります。基本的には、ゲーム全体で本当に1つだけ必要なものに限定するのがおすすめです。

対象シングルトン向きか理由
GameManager向いているゲーム全体の状態を管理しやすい
AudioManager向いているBGMや音量設定をシーン間で共有しやすい
SaveManager向いているセーブ処理を一元管理しやすい
敵・弾・エフェクト向いていないシーンごと、または状況ごとに生成・破棄されるため
ステージ専用UI基本は向いていないシーンごとに表示内容や参照先が変わるため

判断に迷ったら、「ゲーム全体で1つだけあればよいか」を基準にしてください。ステージごとに違うもの、何度も生成されるもの、画面ごとに変わるものは、無理にDontDestroyOnLoadやシングルトンにしない方が安全です。

シングルトンの実装パターンをさらに整理したい場合は、コチラの記事も参考になります。

Unityゲーム プログラミング・バイブル 2nd Generation
✅ Amazonでチェックする✅ 楽天でチェックする




原因5:イベント解除忘れで古い参照が残っている

シーンを切り替えるたびに処理が増えたり、消したはずのUIに対して処理が走ったりする場合は、イベントの解除忘れを疑ってください。DontDestroyOnLoadしたManagerは残り続けるため、そこに登録されたイベントも残りやすくなります。

症状:シーンをまたぐたびにイベント処理が増える

たとえば、スコアが変わったときにUIを更新するため、シーン内のScoreUIがManagerのイベントへ登録しているとします。

private void OnEnable()
{
    GameManager.Instance.OnScoreChanged += UpdateScore;
}

この登録自体はよく使う形です。ただし、シーン切り替えでScoreUIが破棄される前に解除していないと、Manager側に古い参照が残る場合があります。その結果、次のシーンで新しいUIが登録されるたびに、処理が2回、3回と増えていくことがあります。

原因:常駐Managerに登録したイベントを解除していない

イベントは「この処理をあとで呼んでね」と予約する仕組みに近いです。便利なのですが、登録したまま放置すると、呼び出す側であるManagerが古い相手を覚え続けてしまうことがあります。

特に、ManagerはDontDestroyOnLoadで残り、UIやボタンはシーン切り替えで消える、という組み合わせでは注意が必要です。残る側に、消える側の処理を登録したままだと、参照の寿命がズレます。

解決方法:OnEnableとOnDisableをセットで使う

基本は、OnEnableで登録したらOnDisableで解除します。登録と解除をセットで書いておくと、シーン切り替えやオブジェクトの非表示にも対応しやすくなります。

private void OnEnable()
{
    if (GameManager.Instance != null)
    {
        GameManager.Instance.OnScoreChanged += UpdateScore;
    }
}

private void OnDisable()
{
    if (GameManager.Instance != null)
    {
        GameManager.Instance.OnScoreChanged -= UpdateScore;
    }
}

+=で登録したものは、-=で解除する。このセットを習慣にしておくと、イベント由来の多重実行をかなり防ぎやすくなります。

注意点:OnDestroyだけに頼ると見落とす場合がある

解除処理をOnDestroyに書くこともありますが、UIを一時的に非表示にするだけの場面ではOnDestroyが呼ばれないことがあります。そのため、表示・非表示に合わせてイベントを扱うなら、OnEnableOnDisableの組み合わせが分かりやすいです。

判断基準として、常駐Managerのイベントにシーン内オブジェクトを登録しているなら、解除処理までセットで確認するようにしてください。ここを忘れると、最初は正常でも、シーンを何度か行き来したあとにだけ不具合が出ることがあります。時間差で出るバグ、なかなか意地悪です。

イベントやメモリ管理についてさらに深く知りたい場合は、コチラの記事も参考になります。




3分で確認できる診断チェックリスト

DontDestroyOnLoadまわりの不具合は、やみくもにコードを書き換えるより、症状に近いところから順番に確認する方が早いです。まずは次のチェックリストを上から見ていきましょう。

  • DontDestroyOnLoad(gameObject)の形で呼んでいるか
  • Instance登録をAwakeで行っているか
  • □ 重複時にDestroy(gameObject)したあと、returnしているか
  • □ Managerの子にCanvas、Camera、タイトル専用UIを入れていないか
  • □ Managerが前のシーンのText、Button、Cameraを直接参照していないか
  • □ 各シーンに同じManagerを配置していないか
  • □ イベント登録した処理をOnDisableなどで解除しているか
  • □ HierarchyでManagerやAudioManagerが複数残っていないか

オブジェクトが消える場合は、まず1つ目のgameObject指定と、親子関係を見てください。特に、Managerの下に余計な子オブジェクトが入っていると、「消える」ではなく「不要なものまで残る」方向の不具合も起きます。

Instancenullになる場合は、AwakeStartの場所を確認します。Managerの登録がStartにあるなら、他のスクリプトが先に参照していないかを疑いましょう。

Missingが出る場合は、Managerがシーン内UIを直接持っていないかを見ます。常駐するものが、消えるものを握っていると参照切れになりやすいです。ここはコードの書き方というより、設計の問題として見ると原因を見つけやすくなります。

BGMや処理が増える場合は、重複生成を確認してください。再生中にHierarchyを見て、GameManagerAudioManagerが複数あるなら、シングルトンの重複チェックが足りない可能性があります。

当てはまる数見直しの目安
1〜2個局所的な修正で直る可能性が高い
3〜4個Managerの役割や参照先を見直した方がよい
5個以上シーン管理の設計を一度整理するのがおすすめ

体感として、消えるなら対象、nullなら初期化順、Missingなら参照の寿命、増えるなら重複生成です。この4分類で見ると、原因探しがかなり楽になります。




よくある誤解と注意点

DontDestroyOnLoadは便利ですが、「残せる」ことと「安全に管理できる」ことは別です。ここを混同すると、最初は動いていても、シーン数が増えたタイミングで急に不具合が出やすくなります。

誤解1:DontDestroyOnLoadを使えば何でも安全に残せる

DontDestroyOnLoadは、指定したGameObjectをシーン切り替え時の破棄対象から外すための機能です。ただし、残したオブジェクトが次のシーンでも正しく使えるかどうかは別問題です。

たとえば、ステージ専用の敵や弾、演出用エフェクトを残してしまうと、次のシーンで不要な処理が動き続ける場合があります。基本的には、ゲーム全体で必要なManagerや音量設定などに絞ると扱いやすいです。

誤解2:ManagerがUIを直接持つのが普通

小さなサンプルでは、GameManagerにTextやButtonを直接入れても動きます。ただ、シーンをまたぐ設計になると、UIはシーン切り替えで消え、Managerだけが残るため、参照切れが起きやすくなります。

迷ったら、Managerは「状態を持つ」、UIは「表示する」と分けて考えてください。スコアの数値はManager、スコアを表示するTextは各シーンのUIが担当する、という分け方です。

誤解3:Startで初期化すれば十分

Startは便利ですが、シングルトンのInstance登録には少し遅い場合があります。他のスクリプトがAwakeOnEnableでManagerを参照すると、Manager側のStartがまだ呼ばれていないことがあるためです。

そのため、Instance登録・重複チェック・DontDestroyOnLoadAwakeに置き、UIやCameraとの接続はStart以降に分けるのが安定しやすいです。

誤解4:シングルトンにすれば全部解決する

シングルトンは、Managerの重複生成を防ぐには役立ちます。ただし、何でもシングルトンにすると、どのスクリプトがどのManagerに依存しているのか見えにくくなります。

使う目安は「ゲーム全体で1つだけ必要か」です。GameManagerやAudioManagerのように全体で共有したいものは候補になりますが、敵、弾、ステージ専用UIなどは基本的に向いていません。

誤解5:オブジェクトが増えるのはUnityの不具合

シーンを戻るたびにManagerやBGMが増える場合、多くはUnityの不具合ではなく、古いManagerが残ったまま新しいManagerを生成している状態です。特に、複数のシーンに同じManagerを配置していると起きやすいです。

もちろん、Unityのバージョンやプロジェクト設定によって挙動確認が必要なケースもあります。ただ、まずはHierarchy上で同じManagerが複数存在していないか、重複時にDestroy(gameObject)しているかを確認すると切り分けやすいです。

設計パターンの使い分けをもう少し整理したい場合は、コチラの記事も参考になります。




まとめ:DontDestroyOnLoadは残す対象と参照の寿命を分けて考える

DontDestroyOnLoadが効かないように見えるときは、まず症状を分けて考えるのが近道です。オブジェクトが消えるのか、Missingになるのか、Managerが増えるのかで、確認する場所は変わります。

最初に確認したい優先順位は、次の通りです。

  1. DontDestroyOnLoad(gameObject)で、残したいGameObjectを対象にしているか
  2. Instance登録や重複チェックをAwakeで行っているか
  3. 常駐Managerが、前のシーンのUIやCameraを直接参照していないか
  4. シーン再読み込み時に、同じManagerが複数生成されていないか
  5. イベント登録した処理を、OnDisableなどで解除しているか

特に大切なのは、残すものと消えるものを直接つなぎすぎないことです。Managerはシーンをまたいで残る一方で、Canvas、Text、Button、Cameraなどはシーン切り替えで作り直されることが多いです。この寿命の違いを無視すると、DontDestroyOnLoad自体は動いていても、参照切れや多重実行が起きやすくなります。

私の感覚では、Missingが出たら「参照先の寿命」、BGMや処理が増えたら「重複生成」、シーン開始直後だけnullになるなら「初期化順」を疑うと、かなり原因を見つけやすいです。

また、DontDestroyOnLoadは便利な機能ですが、何でも残せばよいわけではありません。Managerにはスコア、進行状態、音量設定などの「残したい情報」を持たせ、UIやCameraなどの「そのシーンで表示・操作するもの」はシーン側に置くと、構造が安定しやすくなります。

Unityの基礎からシーン管理やスクリプトの考え方を整理したい場合は、書籍で一度まとまった流れを追うのも効果的です。

Unityの教科書 Unity 6完全対応版
✅ Amazonでチェックする✅ 楽天でチェックする

DontDestroyOnLoadまわりの不具合は、コード1行だけで解決することもありますが、多くの場合は「何を残すか」「何をシーンごとに作り直すか」の線引きがカギになります。ここを整理しておくと、シーンが増えても落ち着いて対応できます。


よくある質問(FAQ)

Q
DontDestroyOnLoadはPlayerにも使っていいですか?
A

使ってもよいですが、ゲームの作りによって向き・不向きがあります。たとえば、プレイヤーのHP、装備、所持アイテムなどをシーンをまたいで維持したい場合は、Playerを残す設計も候補になります。

ただし、ステージごとに初期位置が変わるゲームでは注意が必要です。Player本体をそのまま残すと、次のシーンで想定外の位置に出たり、前のシーン用のカメラ設定や当たり判定を引きずったりする場合があります。

迷ったときは、Player本体を残すのではなく、HPや所持アイテムなどのデータだけをManagerに残す方法も検討してみてください。シーンごとにPlayerを生成し直し、必要なデータだけ引き継ぐ形にすると、ステージ設計との相性がよいことがあります。

Q
DontDestroyOnLoadしたオブジェクトはどこに移動しますか?
A

DontDestroyOnLoadしたGameObjectは、通常のシーン切り替えで破棄されない特別な領域に移動したように扱われます。UnityのHierarchy上では、再生中に専用のシーンのような形で確認できる場合があります。

ここで大切なのは、「見た目上どこにあるか」よりも、通常のシーン破棄の対象から外れると理解することです。つまり、次のシーンへ移動しても、そのGameObjectと子オブジェクトは残ります。

もし残っているか分からない場合は、再生中にHierarchyを確認してみてください。GameManagerAudioManagerがシーン切り替え後も存在していれば、保持自体はできています。そのうえで、参照切れや重複生成が起きていないかを見ると、原因を切り分けやすいです。

Q
DontDestroyOnLoadとAdditiveロードはどちらを使うべきですか?
A

小規模なゲームや、Managerを数個だけ残したい場合は、DontDestroyOnLoadで十分なことが多いです。GameManager、AudioManager、SaveManagerのような常駐オブジェクトを扱うなら、実装も比較的シンプルです。

一方で、常駐させたい仕組みが増えてきた場合は、AdditiveロードやPreloadシーンを検討してもよいです。たとえば、共通UI、ロード管理、サウンド管理、通信管理などをまとめて常駐させたい場合、専用シーンとして管理した方が見通しがよくなることがあります。

方法向いているケース注意点
DontDestroyOnLoad少数のManagerを残したい対象が増えると管理が複雑になりやすい
Additiveロード常駐システム用のシーンを分けたいシーンの読み込み・破棄の管理が必要
Preloadシーン起動時に初期化処理をまとめたいプロジェクト全体の設計を決めてから使う方が安全

まずはDontDestroyOnLoadで「何を残すか」「何を残さないか」を整理し、それでも常駐オブジェクトが増えすぎる場合に、AdditiveロードやPreloadシーンへ広げていくのがおすすめです。

※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。

※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。

スポンサーリンク