スポンサーリンク
設計・アーキテクチャ

【Unity】Singleton(シングルトン)の実装方法|DontDestroyOnLoad対応・注意点まで解説

設計・アーキテクチャ

UnityでGameManagerやAudioManagerを作っていると、「どこからでも呼び出せる管理用スクリプトを1つだけ置きたい」と感じる場面がありますよね。そこでよく使われるのがSingleton(シングルトン)です。

ただ、見よう見まねで実装すると、シーン切り替え後に同じManagerが増えたり、Instancenullになったりして、原因を探すのに時間がかかることがあります。

この記事では、UnityでSingletonを使うべき場面を整理しながら、実装方法と失敗しやすいポイントを順番に見ていきます。


Singletonを使うべき場面

Singletonは便利な設計ですが、すべてのスクリプトに使うものではありません。

判断に迷ったら、「ゲーム全体で1つだけあれば十分な役割かどうか」を基準に考えると分かりやすくなります。

1つだけで十分な管理役に使う

Unityでは、次のような「ゲーム全体を管理する役割」のスクリプトでSingletonがよく使われます。

  • GameManager(ゲーム全体の進行管理)
  • AudioManager(BGM・効果音の管理)
  • SaveManager(セーブデータの管理)
  • 設定情報やゲームオプションの管理

これらは複数存在すると、設定が食い違ったり、BGMが二重に再生されたりと、不具合の原因になりやすい管理役です。そのため、「常に1つだけ存在する」というSingletonの考え方と相性がよくなります。

反対に、敵キャラクターや弾、アイテムのように複数生成されるオブジェクトには、Singletonは基本的に向いていません。

Singletonを採用するか迷ったら、次のチェックリストを確認してみてください。

  • シーンをまたいで同じ情報を使いたい
  • ゲーム内に1つだけ存在すれば十分な役割である
  • 複数のスクリプトから共通で参照したい

この3つすべてに当てはまるなら、Singletonを採用する価値があります。

逆に、どれか1つでも当てはまらない場合は、通常のMonoBehaviourやScriptableObjectなど、別の方法のほうがシンプルに実装できることもあります。

まずは「本当に1つだけ必要なオブジェクトか」を判断し、そのうえで実装を始めることが、あとから設計を見直す手間を減らすポイントです。




UnityのSingleton基本実装

Singletonを使う場面が決まったら、まずは基本形を作ってみましょう。ここでは、UnityのMonoBehaviourを使った管理用スクリプトを想定します。

まずはこの形で作る

例として、ゲーム全体を管理するGameManagerをSingletonにするコードです。

using UnityEngine;

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

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

        Instance = this;
    }
}

外部のスクリプトから使うときは、次のように呼び出せます。

GameManager.Instance

Instanceには、現在使うべきGameManager自身が入ります。private setにしているので、ほかのスクリプトから勝手に中身を書き換えにくい形です。

Awakeでは、すでに別のGameManagerが存在しているかを確認しています。もし存在していれば、新しく作られたほうをDestroy(gameObject)で削除します。

このコードで防げる失敗

この基本形で防げる代表的な失敗は、同じManagerが複数存在してしまうことです。

  • シーン内に同じManagerを2つ置いてしまった
  • PrefabからManagerを重複生成してしまった
  • 別シーンに同じManagerを配置していた

このような状態になると、スコアが別々に管理されたり、同じ処理が2回実行されたりすることがあります。見た目では気づきにくいので、最初から重複チェックを入れておくと安心です。

ただし、このコードだけではシーンを切り替えたときにオブジェクトは破棄されます。シーンをまたいで同じManagerを使いたい場合は、次のセクションで扱うDontDestroyOnLoad対応が必要になります。




シーンをまたぐなら永続化する

タイトル画面からゲーム画面へ移動したあとも、同じManagerを使い続けたい場合は、DontDestroyOnLoadを使います。

たとえば、BGMをシーン切り替えで止めたくないときや、ゲーム設定・スコア情報を次のシーンに引き継ぎたいときです。

DontDestroyOnLoadを追加する

基本実装にDontDestroyOnLoad(gameObject)を追加すると、シーンが切り替わってもそのオブジェクトが破棄されにくくなります。

using UnityEngine;

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);
    }
}

この形にすると、最初に作られたGameManagerが残り続けます。ゲーム全体で共通して使う管理役には便利です。

重複生成防止も必ず入れる

DontDestroyOnLoadを使うときに忘れやすいのが、重複生成の対策です。

たとえば、タイトルシーンにもゲームシーンにも同じGameManagerを置いていると、シーン移動時に新しいGameManagerが作られることがあります。

そのまま放置すると、BGMが二重に流れたり、スコア管理がずれたりする原因になります。そこで、すでにInstanceが存在する場合は、あとから作られたほうを削除します。

  • BGMをシーン移動後も流し続けたい
  • タイトル、ゲーム、リザルトで同じ設定を使いたい
  • プレイヤーデータやスコアを一時的に保持したい

このような場合は、DontDestroyOnLoad対応のSingletonを使うと管理しやすくなります。

一方で、シーンごとに別々の状態を持たせたいManagerには向きません。毎回リセットしたい情報まで残してしまうと、前のシーンの値が残って予想外の動きになることがあります。

BGMを止めずにシーンを移動する具体例は、こちらの記事でも解説しています。




Singletonでエラーになる原因

Singletonの実装でつまずきやすいのは、コードの書き方そのものよりも「呼び出すタイミング」と「破棄された後の参照」です。

とくに多いのは、Instancenullになるケースと、削除済みのオブジェクトを参照してしまうケースです。

Awakeの順番でnullになる

Unityでは、複数のスクリプトのAwakeがどの順番で実行されるかに注意が必要です。

たとえば、あるスクリプトのAwakeで次のように呼び出すとします。

private void Awake()
{
    GameManager.Instance.StartGame();
}

このとき、GameManager側のAwakeがまだ実行されていないと、Instanceが設定されておらず、nullになることがあります。

判断基準としては、次のように考えると安全です。

  • Awakeでは、自分自身の初期化を中心にする
  • ほかのSingletonを使う処理は、できればStart以降に回す
  • どうしても順番が必要な場合だけ、Script Execution Orderを検討する

最初のうちは、Awakeで別のManagerを呼び出しすぎないようにすると、原因不明のnullをかなり減らせます。

Destroy後の参照が残る

もう1つ注意したいのが、破棄されたオブジェクトへの参照です。

Singletonのオブジェクトを削除したあとも、Instanceに古い参照が残っていると、存在しないオブジェクトを使おうとしてエラーになることがあります。

その対策として、OnDestroyで自分自身がInstanceならnullに戻しておきます。

private void OnDestroy()
{
    if (Instance == this)
    {
        Instance = null;
    }
}

ただし、DontDestroyOnLoadでゲーム中ずっと残すManagerの場合、通常プレイ中に何度も破棄することは少ないです。それでも、シーンの作り直しやテスト中には役立つことがあります。

次のような症状が出る場合は、破棄後の参照を疑ってみてください。

  • シーンを戻ったあとにエラーが出る
  • Play Modeを何度か試すと挙動が変わる
  • 削除したはずのManagerを別スクリプトが呼んでいる

削除済みオブジェクト参照の詳しい考え方は、こちらの記事でも解説しています。




Singletonを使わない判断基準

Singletonは便利ですが、「どこからでも使えるから」という理由だけで増やしていくと、あとから管理が難しくなることがあります。

使うか迷ったときは、「本当に1つだけ存在する管理役なのか」をもう一度確認してみましょう。

データだけならScriptableObjectを検討する

アイテム情報や敵のステータス、ゲーム設定のように「値を持っておきたいだけ」の場合は、SingletonよりもScriptableObjectのほうが扱いやすいことがあります。

やりたいこと向いている方法
ゲーム中の進行状態を管理したいSingleton
アイテムや敵の固定データを管理したいScriptableObject
シーンをまたいでBGMを管理したいSingleton + DontDestroyOnLoad

たとえば「ポーションの回復量」「敵の最大HP」「武器の攻撃力」のような固定データは、Singletonで管理するより、データとして分けたほうが見通しがよくなります。

判断基準はシンプルです。ゲーム中に状態が変わる管理役ならSingleton、あらかじめ決まったデータを参照したいだけならScriptableObjectを検討するとよいです。

大規模化したらDIも候補にする

プロジェクトが大きくなると、Singleton同士が互いに呼び合って、どこで何が動いているのか追いにくくなることがあります。

次のような状態になってきたら、Singletonを増やす前に設計を見直すサインです。

  • Managerという名前のスクリプトが増えすぎている
  • 1つのManagerが音・UI・セーブなど複数の役割を持っている
  • 修正した場所とは別の機能まで壊れやすい
  • どのスクリプトがどのManagerを使っているか追いにくい

この場合は、依存性注入(DI)や責務分離を検討すると、コードのつながりを整理しやすくなります。

ただし、小さな個人制作や学習用プロジェクトで、GameManagerやAudioManagerを1つだけ使う程度なら、無理に難しい設計へ進む必要はありません。

DIについて詳しく知りたい場合は、こちらの記事で基本を解説しています。




まとめ:まずは基本形で十分

UnityでSingletonを使うなら、まずは「本当に1つだけ必要な管理役か」を確認してから実装すると失敗しにくくなります。

GameManagerやAudioManagerのように、ゲーム全体で1つだけあればよいスクリプトなら、重複生成を防ぐ基本形から始めて大丈夫です。シーンをまたいで残したい場合だけ、DontDestroyOnLoadを追加しましょう。

一方で、固定データの管理や大規模な設計までSingletonでまとめようとすると、あとから修正しづらくなることがあります。迷ったときは「管理役ならSingleton、データだけなら別管理」と考えると、次に取る行動を決めやすくなります。


よくある質問(FAQ)

Q
Singletonとstaticは同じですか?
A

同じではありません。Singletonは「インスタンスを1つだけ使うための設計」、staticは「インスタンスを作らずに使う仕組み」です。

Unityでは、MonoBehaviourの機能を使いたい場合はSingleton、単純な計算処理や定数だけを扱う場合はstaticが向いています。

Q
Singletonは使わないほうがいいですか?
A

使いすぎには注意が必要ですが、目的がはっきりしていれば使っても大丈夫です。

たとえば、GameManagerやAudioManagerのように「ゲーム全体で1つだけあればよい管理役」なら、Singletonは分かりやすい選択肢になります。

ただし、敵・アイテム・UIパーツのように複数存在するものや、固定データを持つだけのものまでSingletonにすると、後から整理しづらくなります。迷ったときは「1つだけ必要な管理役かどうか」で判断してみてください。

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

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

スポンサーリンク