Menu
CoddyTech

ジェネリックなシングルトン

CoddyのUnityジャーニー「Unityにおけるゲームアーキテクチャ」セクションの一部。レッスン 34/50。

セクション3のシングルトンは、各マネージャーが繰り返し記述する5行で構成されています。staticなInstance、これを設定するAwake、重複チェックです。ジェネリックな基底クラスなら、これらを一度だけ記述できます。各マネージャーは自分自身の型を指定して、そこから派生します。

public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance { get; private set; }

    protected virtual void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this as T;
    }
}

public class ScoreManager : Singleton<ScoreManager>
{
    public int score;
    public void Add(int points) { score += points; }
}

public class SoundManager : Singleton<SoundManager>
{
    public void Play(string clip) { Debug.Log("Playing " + clip); }
}
// どのスクリプト内でも
ScoreManager.Instance.Add(10);
SoundManager.Instance.Play("coin");

ジェネリッククラスのstaticメンバーは、そのクラスから作られた各型に属します。Singleton<ScoreManager>とSingleton<SoundManager>はそれぞれInstanceを持つ別々の型なので、マネージャー同士で共有されることはありません。ScoreManager.InstanceはすでにScoreManager型です。これがTを使う理由です。基底クラス内では、thisはSingleton<T>としてしか認識されないため、this as Tによって、本来のマネージャー型として渡します。

基底クラスはabstractです。Unityがオブジェクトにアタッチするのはマネージャーだけです。独自のAwakeが必要なマネージャーは、通常のvoid Awake()を書いてはいけません。これは基底クラスのメソッドを隠してしまい(コンパイラーが警告します)、Unityはマネージャー側のものだけを呼び出すため、Instanceが設定されません。overrideして、最初に基底クラスのメソッドを呼び出します。

protected override void Awake()
{
    base.Awake();
    score = 0;
}
challenge icon

チャレンジ

中級

町と森が紫色のポータルでつながっています。各シーンにはそれぞれ独自の Wallet と MusicPlayer オブジェクトがあるため、どちらも単独で再生され、コインは Wallet.Instance.Add(1) を呼び出します。現在、それぞれのマネージャーはセクション 3 のシングルトンなので、シーンを読み込むたびにウォレットが空のものに置き換わります。シーンの読み込みをまたいで保持される汎用基底クラスを、両方のマネージャーに与えましょう。

  • PersistentSingleton.cs で、任意の MonoBehaviour 型 T に対応する MonoBehaviour である public abstract class PersistentSingleton<T> を記述します。型 T の public static な Instance を持ち、その設定はクラスだけが行えるようにし、さらに protected virtual void Awake() を用意します。
  • その Awake は同種のうち最初のものを維持します。それが Instance になり、シーンの読み込み後も残ります。後から現れたものは自身のオブジェクトを破棄し、何も変更しません。
  • Wallet と MusicPlayer はこのクラスを継承し、それぞれにあった Instance フィールドを削除します。
  • MusicPlayer は独自の Awake を持ち続け、Now playing: Adventure をログに出力します。基底クラスの Awake の後に実行し、残るインスタンスだけで実行されるようにして、ゲームごとに音楽が一度だけ開始されるようにします。

矢印キーで Player を移動します。Play を押してコインを拾い、森へ行って戻ってから、Check を押してください。

自分で試してみよう

using UnityEngine;

// PersistentSingleton<T>: シーンの読み込み後も存続する単一のインスタンス
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Unityにおけるゲームアーキテクチャのすべてのレッスン