はじめに
Unityにおいて、マネージャークラスなど、シーン内で唯一のインスタンスを保持する必要があるクラスを扱う場合があります。例えば、GameManagerやSoundManagerなどがこれに該当します。このようなオブジェクトに対してはSingletonMonobehaviour
パターンが有効な場面が多いです。
SingletonMonobehaviourの特徴
1. シーン内に唯一のインスタンスを保持する
2. staticなためインスタンス生成せず他のオブジェクトからアクセス可能で、新しいインスタンスを作成することができない、もしくは作成したら重複してるものを消す
3. 基本DontDestroyOnLoadに置くので、一度作成されたインスタンスは、シーンが切り替わっても保持される
SingletonMonobehaviourの実装
実装の紹介では、Unityが公式に提供しているUnityプログラミングパターンのサンプルリポジトリ*1のシングルトンの部分からコードを引用して紹介します。
1. 基底クラスのSingletonクラスを作成
using UnityEngine; namespace DesignPatterns.Singleton { public class Singleton<T> : MonoBehaviour where T : Component { private static T _instance; public static T Instance { get { if (_instance == null) { // アクセスされたらまずは、インスタンスがあるか調べる _instance = (T)FindObjectOfType(typeof(T)); if (_instance == null) { // なかったら作る SetupInstance(); } else { // 既に会った時のデバッグログ 特に意味はない string typeName = typeof(T).Name; Debug.Log("[Singleton] " + typeName + " instance already created: " + _instance.gameObject.name); } } return _instance; } } public virtual void Awake() { // 重複回避のためのチェック RemoveDuplicates(); } // シングルトン初期化 private static void SetupInstance() { _instance = (T)FindObjectOfType(typeof(T)); if (_instance == null) { GameObject gameObj = new GameObject(); gameObj.name = typeof(T).Name; _instance = gameObj.AddComponent<T>(); DontDestroyOnLoad(gameObj); } } private void RemoveDuplicates() { if (_instance == null) { _instance = this as T; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } } }
2. Singletonクラスを継承して、SingletonMonobehaviourを実装
今回は、AudioManagerを例に載せます。
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace DesignPatterns.Singleton { // This example shows a simple audio/sound manage as a Singleton. // Access the AudioManager.Instance to play an AudioClip from any GameObject. public class AudioManager : Singleton<AudioManager> { public AudioClip clip; public AudioSource audioSource; public Vector2 volume = new Vector2(0.5f, 0.9f); public Vector2 pitch = new Vector2(0.8f, 1.2f); // play a clip from a designated AudioSource public void PlaySoundEffect(AudioClip clip) { if (audioSource == null) return; audioSource.volume = Random.Range(volume.x, volume.y); audioSource.pitch = Random.Range(pitch.x, pitch.y); audioSource.clip = clip; audioSource.Stop(); audioSource.Play(); } } }
3. ゲーム起動時に呼び出したり、必要な場面で呼び出してインスタンスを作成
Instanceがstaticなので、クラス名.Instanceで目的の処理をさせることができます。
まとめ
以上がSingletonMonobehaviourの実装方法です。複数のシーンで同一のGameManagerオブジェクトを扱いたい場合などに利用すると便利です。