ジェネリックなシングルトン
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;
}チャレンジ
中級町と森が紫色のポータルでつながっています。各シーンにはそれぞれ独自の 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>: シーンの読み込み後も存続する単一のインスタンス
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。