B L O G

ブログ記事

thumbnail
it知識

【Unity】シングルトンとは

シングルトンとは

インスタンスが1つしか存在しないようにする仕組みのことです。

インスタンスというのは、実体(実際に動くもの)のことです。
よくある例えで、「クラス」は設計図、「インスタンス」はその設計図から作った本物の車(=実物)というのがあります。

なぜシングルトンが必要なのか?

ゲームを作るとき、「全体で一つだけ存在してほしい」ものがあると思います。
例えば、ゲーム内の設定管理や音量管理、セーブ管理などです。

これらが複数存在すると、設定が上書きされたり、バラバラのセーブデータが作られたり、音量の調整が片方にだけ反映されたりと、予期しないバグの原因になります。

スクリプトの参照は GetComponentFindAnyObjectByType などでもできますが、
シングルトンを使うと「1つだけ存在する」ことが保証されているため、どこからでも簡単にアクセスできてとても便利です。

Unityでのシングルトンの基本的な書き方・使い方

書き方


public class GameManager : MonoBehaviour
{

  public static GameManager instance; // インスタンスの定義

  private void Awake(){
      if (instance == null)
      {
        instance = this; // 自分をインスタンスに設定
      }
      else
      {
        Destroy(gameObject); // 複数存在していた場合、自身を破棄
      }
    }

}

public static GameManager instance;の意味

  • public : どこからでもアクセスできる
  • static : インスタンスではなくクラスに属する
  • GameManager : 型名
  • instance : 変数名

publicでGameManager型のinstanceという名前の変数を定義してます。
ここに static(静的)をつけることで、この変数はインスタンスに属さず、クラスそのものに属するようになります。

これの何がありがたいかというと、この GameManager スクリプトにアクセスするときに、GetComponentnew を使わずに直接アクセスできるようになるという点です。

また、その後はGameManager型の変数を宣言したあと、Awake時に instance が null なら、自分自身を代入しています。
こうして、自分をシングルトンのインスタンスとして設定できたわけですね!

もしすでに instance に別のインスタンスが入っていた場合は、重複を避けるためにDestroyで自分を破棄しています。

使い方

ここではコインを取得した時に、GameManagerscore の値を操作する例を見てみましょう。
例えば、以下のような GameManagerCoin のスクリプトがあったとします。

GameManagerスクリプト


using UnityEngine;

public class GameManager : MonoBehaviour
{
    public int score = 0;

    public static GameManager instance; // インスタンスの定義

    private void Awake()
    {
        if (instance == null)
        {
            instance = this; // 自分をインスタンスに設定
        }
        else
        {
            Destroy(gameObject); // 複数存在していた場合、自身を破棄
        }
    }
}

Coinスクリプト


using UnityEngine;

public class Coin : MonoBehaviour
{
  public void GetCoin()
  {
    GameManager.instance.score++;
    Debug.Log("スコア: " + GameManager.instance.score);
  }
}

この時、Coinスクリプト内で GameManager.instance.score++;GameManagerにアクセスして、scoreの値を+1してます。

このように、他のスクリプトから GameManager.instance.score++ のように GameManager.instance を通じて、score などの変数にアクセス・操作できます。

補足

通常、シーンをまたぐと、そのインスタンスが破棄されてしまいますが、Awake 内に DontDestroyOnLoad(gameObject); を追加することで、シーンをまたいでも同じ GameManager インスタンスを使い続けることができます。

ジェネリックを使った汎用的なシングルトン

先ほどの方法でもシングルトンは作れますが、シングルトンにしたいスクリプトごとに同じコードを書くのは少し面倒です。
そこで、ジェネリックな基底クラスというのを作成することで、コードの重複を減らし、使いやすくできます。

まずは、下記のような Singleton クラスを作成します。


using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{

  public static T instance; // インスタンスの定義

  protected virtual void Awake()
  {
      if (instance == null)
      {
  		  instance = (T)FindObjectOfType(typeof(T)); //探したコンポーネントをインスタンスに設定
  		}
      else
      {
        Destroy(gameObject); // 複数存在していた場合、自身を破棄
      }
  }

}

public class Singleton<T> : MonoBehaviour where T : MonoBehaviourの意味

  • public : どこからでもアクセスできる
  • Singleton : クラス名
  • <T> : あとで決める「型の名前」(ジェネリック型パラメータ)
  • MonoBehaviour : Unityの基本クラス MonoBehaviour を継承
  • where T : MonoBehaviour : Tは MonoBehaviour またはその派生クラスでなければならないという型の制限

ジェネリック(generics:総称型)とは

ジェネリックはさまざまな型に対応するために、型をパラメータとして受け取る仕組みのことです。
Singleton<T> は「Tという型のシングルトンを作るためのテンプレート」になっています。

where T : MonoBehaviourの意味

where T : MonoBehaviourと指定することで、Tintstring のような型が入るのを防ぎます。

これにより、Unity のコンポーネント(MonoBehaviour 派生クラス)だけを対象にできます。
間違った型を指定するとコンパイルエラーになるので、安全です。

instance = (T)FindObjectOfType(typeof(T));の意味

  • instance : シングルトンのインスタンスを格納する変数
  • FindObjectOfType(typeof(T)) : シーン内にある型 T のコンポーネントを探すUnityの関数
  • (T) : FindObjectOfTypeの返り値は Object 型なので、型 T に変換(キャスト)する

まとめると、シーン内に既にある T 型のコンポーネントを探して、正しく型変換し、それを instance に代入するという処理を行っています。

この Singleton クラスがあれば、たとえば GameManager をシングルトンにしたいときは、以下のように書くだけでOKです。


using UnityEngine;
public class GameManager : Singleton
{
  // GameManager に関する処理
}

普段 MonoBehaviour と書いていたところを Singleton<GameManager> にすることで、

  • GameManager クラスは Singleton<GameManager>親クラス(=継承先)として使う
  • Singleton クラスに書かれた「シングルトンの仕組み」をそのまま使える

という形になります。
これで毎回シングルトンのコードを書かなくて良くなりました!

まとめ

今回は、シングルトンの概念と使い方について解説しました。

シングルトンとは、インスタンス(実体)が常に1つだけしか存在しないようにする仕組みだということが分かりましたね。

ちなみに、シングルトンはデザインパターンと呼ばれる設計手法の1つなので、
たとえばファクトリーパターン(Factory Pattern)、オブジェクトプール(Object Pool)など、他にもさまざまな種類があります。

これらのパターンについても、今後少しずつ紹介していけたらと思います!

参考

関連する記事