ジェネリックなシングルトン
CoddyのUnityジャーニー「Unityのゲームアーキテクチャ」セクションの一部。レッスン 43/64。
Section 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;
}チャレンジ
中級両方のマネージャーに、シーンの読み込み後も存続する基盤を用意しましょう。
PersistentSingletonに、protected virtual void Awake()を記述します。最初のインスタンスはInstanceになって存続し(DontDestroyOnLoad)、後から来たインスタンスは自分自身を破棄します。WalletとMusicPlayerはこれを継承します。それぞれのInstanceと、WalletのAwakeを削除します。MusicPlayerでAwakeをオーバーライドします。まずbase.Awake()を呼び出し、次にそれがInstanceであれば、Now playing: Adventureをログに出力します。
「プレイ」を押して、森に行ってから戻ってきましょう。コインは1枚です。その後、「確認」を押します。
自分で試してみよう
using UnityEngine;
public abstract class PersistentSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
public static T Instance { get; private set; }
// Awake: シーンの読み込み後も最初のインスタンスを維持し、後から生成されたインスタンスはすべて破棄する
}
Unityのゲームアーキテクチャのすべてのレッスン
自分で練習してみよう: Unityプレイグラウンド