学習記事一覧 · Unity

【学習】Unity 6:DontDestroyOnLoad で残したオブジェクトは Destroy() で破棄できる(典型パターンと落とし穴)

前回:シーンが切り替わっても BGM を止めない(DontDestroyOnLoad とシングルトン)

DontDestroyOnLoad(gameObject) は「シーン切替で自動的には破棄されない」ようにする仕組みです。

一方で、通常の GameObject と同様に、コードから Destroy() すれば破棄できます。

今回は、いつ・どうやって片付けるか、初学者がつまずきやすい instance だけ null にする事故リセット用のシングルトン破棄パターンまで整理します。


今日のゴール

  • DontDestroyOnLoad オブジェクトは Destroy(gameObject) で消せると説明できる
  • 「シーンでは消えない」≠「永遠に消えない」 を区別できる
  • instance = null だけしてオブジェクトを残すことがどんな不具合を起こすか言える
  • ResetInstance() のような安全な破棄を書ける

結論:Destroy() は普通に効く

DontDestroyOnLoad() を呼ぶと、オブジェクトは 特殊な DontDestroyOnLoad シーン側に移ります。

それでも Destroy(gameObject); は有効です。

整理すると次の2段です。

  • シーンをまたいで残すDontDestroyOnLoad
  • 不要になったら消すDestroy

例:BGMManager に「停止して消す」を足す

using UnityEngine;
public class BGMManager : MonoBehaviour
{
    private static BGMManager instance;
    private void Awake()
    {
        if (instance != null)
        {
            Destroy(gameObject);
            return;
        }
        instance = this;
        DontDestroyOnLoad(gameObject);
    }
    public void StopAndDestroy()
    {
        Destroy(gameObject);
    }
}

別のスクリプトから呼ぶ例

var bgm = FindObjectOfType<BGMManager>();
if (bgm != null)
{
    bgm.StopAndDestroy();
}

補足(Unity 6 環境の実務)
環境によっては FindObjectOfType非推奨警告が出ることがあります。その場合は FindFirstObjectByType<BGMManager>()(または要件に応じて FindAnyObjectByType)へ置き換えるのが無難です。

または、BGMManager 側に public static BGMManager Instance { get; private set; } のような 静的参照を用意しておき、Find を避ける設計もよく使われます(どちらも方針は同じで「存在しないときの null チェック」が重要です)。

gameObject を直接破棄してもよい

Destroy(bgmManager.gameObject);

StopAndDestroy() のような窓口を作るか、直接 Destroy するかはチーム運用で揃えれば十分です。


よくある使い方:タイトルでは残し、終了時だけ片付ける

ゲーム終了時に明示的に破棄したい場合の例です。

private void OnApplicationQuit()
{
    Destroy(gameObject);
}

OnApplicationQuit は「アプリ終了の流れの中で呼ばれる」イメージで使います。
プレイを最初からやり直す」などのセッション内リセットは、次節の ResetInstance() の方が扱いやすいことが多いです。


初学者が混乱しやすいポイント

**「シーン切り替えで消えない」=「永遠に消えない」**ではありません。

正確には、

  • シーンロードでは自動削除されなくなる

だけです。

そのため、不要になったタイミングではコードから普通に削除できます。


シングルトンでよくある破棄パターン(リセット)

ゲームを最初からやり直すときなどは、次のように オブジェクト破棄と static の参照クリアをセットにします。

public static void ResetInstance()
{
    if (instance != null)
    {
        Destroy(instance.gameObject);
        instance = null;
    }
}

こうしておくと、次にシーン側で再生成しても、古いオブジェクトが残りっぱなしになりにくいです。


逆に危険なケース(典型:BGM 二重再生)

DontDestroyOnLoad() を使っているのに、

  • instance = null; だけやって
  • Destroy(gameObject); をしていない

この組み合わせは危険です。

すると、

  • 画面上(ヒエラルキー)にはオブジェクトが残っている
  • C# 側の instancenull 扱い

…という 状態のズレが起きやすく、次のシーンで もう一つ BGMManager が生きてしまい、結果として BGM が二重再生などの原因になります。

覚え方は単純で、シングルトンの寿命を切るなら「実体(GameObject)」と「参照(instance)」を同時にそろえて片付ける、です。


まとめ

  • DontDestroyOnLoad は「シーン自動破棄を止める」であって、Destroy 自体を無効にする魔法ではない
  • 不要になったら Destroy(gameObject) で普通に消せる
  • instance だけ null にしてオブジェクトを残すのは、重複生成・二重再生の温床になりやすい
  • セッションリセットなら Destroy + instance = null をセット(ResetInstance パターン)

前回の「残す」と今回の「片付ける」をセットで押さえると、DontDestroyOnLoad 周りの設計がかなり楽になります。