Unity チュートリアルのタワーディフェンステンプレートを触ってみる(46)
Unity チュートリアルのタワーディフェンステンプレートを触ってみる(45)では Wave を登録し管理する WaveManager について解説を行った。今回は Prefab から追加した GameManager 継承関係にある Singleton と PersistentSingleton、GameManagerBase の解説を行っていく。
1.タワーディフェンステンプレートのステージの設定編 – Singleton.cs
Singleton.cs について稚拙ながら解説
「Singleton.cs」は「Assets/Scripts/Core/Utilities/Singleton.cs」の指しておりスクリプトについては以下の通り。内容としては Singleton 処理を行っている。
[cce_csharp]using UnityEngine; namespace Core.Utilities { /// <summary> /// Singleton class /// </summary> /// <typeparam name="T">Type of the singleton</typeparam> public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T> { /// <summary> /// The static reference to the instance /// </summary> public static T instance { get; protected set; } /// <summary> /// Gets whether an instance of this singleton exists /// </summary> public static bool instanceExists { get { return instance != null; } } /// <summary> /// Awake method to associate singleton with instance /// </summary> protected virtual void Awake() { if (instanceExists) { Destroy(gameObject); } else { instance = (T) this; } } /// <summary> /// OnDestroy method to clear singleton association /// </summary> protected virtual void OnDestroy() { if (instance == this) { instance = null; } } } }[/cce_csharp]
9行目 : 「public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>」は総称型もしくはジェネリックスと呼ばれるスクリプトの作成手法である。『C#(およびその他の.NET言語)のジェネリクスは.NET Framework 2.0の一部として2005年11月に追加された。Javaと似てはいるが、.NETのジェネリクスは、コンパイラによるジェネリクス型から非ジェネリクス型へのコンバートとしてではなく、実行時に実装される。このことにより、ジェネリクス型に関するあらゆる情報はメタデータとして保存される。』(Wikipedia より引用)
Wikipedia についてはこちらを参照してほしい。
分かりづらいと思うため、簡単に説明すると T という部分に好きな型 GameObject や GameManager など自由に定義できるものという認識で良いだろう。「where T : Singleton<T>」という部分でこの T の型を指定している。ここでは T には Singleton<T> という型を設定する必要があることを指している。
19行目 : 「public static bool instanceExists」はインスタンスが存在しているかの判定を行っている。
27行目 : 「protected virtual void Awake()」は Unity 固有の起動処理を行っている。内容としてはインスタンスが存在していれば、自身の GameObject を破棄し、存在していなければグローバル変数の instance に設定している。
42行目 : 「protected virtual void OnDestroy()」は Unity 固有の破棄処理を行っている。内容としてはグローバル変数に設定されている値が自身と一致していればグローバル変数に対して null を設定している。
また、上記の処理は Singleton パターンと呼ばれ『Singleton パターン(シングルトン・パターン)とは、オブジェクト指向のコンピュータプログラムにおける、デザインパターンの1つである。GoF (Gang of Four; 4人のギャングたち) によって定義された。Singleton パターンとは、そのクラスのインスタンスが1つしか生成されないことを保証するデザインパターンのことである。ロケールやルック・アンド・フィールなど、絶対にアプリケーション全体で統一しなければならない仕組みの実装に使用される[1]。 』(Wikipedia より引用)ということだが、要約すると実行中のシステム(ゲーム)上に一個しかインスタンスを保持できないということを指している。
Wikipedia についてはこちらを参照してほしい。
2.タワーディフェンステンプレートのステージの設定編 – PersistentSingleton.cs
PersistentSingleton.cs について稚拙ながら解説
「PersistentSingleton.cs」は「Assets/Scripts/Core/Utilities/PersistentSingleton.cs」の指しておりスクリプトについては以下の通り。内容としては永続的な Singleton の設定を行っている。
[cce_csharp]namespace Core.Utilities { /// <summary> /// Singleton that persists across multiple scenes /// </summary> public class PersistentSingleton<T> : Singleton<T> where T : Singleton<T> { protected override void Awake() { base.Awake(); DontDestroyOnLoad(gameObject); } } }[/cce_csharp]
6行目 : 「public class PersistentSingleton<T> : Singleton<T> where T : Singleton<T>」は 1. で解説した通り総称型であることを指している。ここでは 1. と同様に T には Singleton<T> という型を設定する必要があることを指している。
8行目 : 「protected override void Awake()」は Unity 固有の起動処理を行っている。内容としては 1. で解説した Singleton の Awake 処理後、DontDestroyOnLoad でシーンを切り替えてもこのゲームオブジェクトの削除が行われないようにしている。
3.タワーディフェンステンプレートのステージの設定編 – GameManagerBase.cs
GameManagerBase.cs について稚拙ながら解説
「GameManagerBase.cs」は「Assets/Scripts/Core/Data/GameManagerBase.cs」の指しておりスクリプトについては以下の通り。内容としては音量の設定・保存を行っている。
[cce_csharp]using System; using Core.Utilities; using UnityEngine; using UnityEngine.Audio; namespace Core.Data { /// <summary> /// Base game manager /// </summary> public abstract class GameManagerBase<TGameManager, TDataStore> : PersistentSingleton<TGameManager> where TDataStore : GameDataStoreBase, new() where TGameManager : GameManagerBase<TGameManager, TDataStore> { /// <summary> /// File name of saved game /// </summary> const string k_SavedGameFile = "save"; /// <summary> /// Reference to audio mixer for volume changing /// </summary> public AudioMixer gameMixer; /// <summary> /// Master volume parameter on the mixer /// </summary> public string masterVolumeParameter; /// <summary> /// SFX volume parameter on the mixer /// </summary> public string sfxVolumeParameter; /// <summary> /// Music volume parameter on the mixer /// </summary> public string musicVolumeParameter; /// <summary> /// The serialization implementation for persistence /// </summary> protected JsonSaver<TDataStore> m_DataSaver; /// <summary> /// The object used for persistence /// </summary> protected TDataStore m_DataStore; /// <summary> /// Retrieve volumes from data store /// </summary> public virtual void GetVolumes(out float master, out float sfx, out float music) { master = m_DataStore.masterVolume; sfx = m_DataStore.sfxVolume; music = m_DataStore.musicVolume; } /// <summary> /// Set and persist game volumes /// </summary> public virtual void SetVolumes(float master, float sfx, float music, bool save) { // Early out if no mixer set if (gameMixer == null) { return; } // Transform 0-1 into logarithmic -80-0 if (masterVolumeParameter != null) { gameMixer.SetFloat(masterVolumeParameter, LogarithmicDbTransform(Mathf.Clamp01(master))); } if (sfxVolumeParameter != null) { gameMixer.SetFloat(sfxVolumeParameter, LogarithmicDbTransform(Mathf.Clamp01(sfx))); } if (musicVolumeParameter != null) { gameMixer.SetFloat(musicVolumeParameter, LogarithmicDbTransform(Mathf.Clamp01(music))); } if (save) { // Apply to save data too m_DataStore.masterVolume = master; m_DataStore.sfxVolume = sfx; m_DataStore.musicVolume = music; SaveData(); } } /// <summary> /// Load data /// </summary> protected override void Awake() { base.Awake(); LoadData(); } /// <summary> /// Initialize volumes. We cannot change mixer params on awake /// </summary> protected virtual void Start() { SetVolumes(m_DataStore.masterVolume, m_DataStore.sfxVolume, m_DataStore.musicVolume, false); } /// <summary> /// Set up persistence /// </summary> protected void LoadData() { // If it is in Unity Editor use the standard JSON (human readable for debugging) otherwise encrypt it for deployed version #if UNITY_EDITOR m_DataSaver = new JsonSaver<TDataStore>(k_SavedGameFile); #else m_DataSaver = new EncryptedJsonSaver<TDataStore>(k_SavedGameFile); #endif try { if (!m_DataSaver.Load(out m_DataStore)) { m_DataStore = new TDataStore(); SaveData(); } } catch (Exception) { Debug.Log("Failed to load data, resetting"); m_DataStore = new TDataStore(); SaveData(); } } /// <summary> /// Saves the gamme /// </summary> protected virtual void SaveData() { m_DataSaver.Save(m_DataStore); } /// <summary> /// Transform volume from linear to logarithmic /// </summary> protected static float LogarithmicDbTransform(float volume) { volume = (Mathf.Log(89 * volume + 1) / Mathf.Log(90)) * 80; return volume - 80; } } }[/cce_csharp]
11行目 : 「 public abstract class GameManagerBase<TGameManager, TDataStore> : PersistentSingleton<TGameManager> where TDataStore : GameDataStoreBase, new() where TGameManager : GameManagerBase<TGameManager, TDataStore>」は 1. で解説した通り、総称型であることを指しているここでは TDataStore には GameDataStoreBase<T> とし、TGameManager には GameManagerBase<TGameManager, TDataStore> という型を設定する必要があることを指している。
53行目 : 「public virtual void GetVolumes(out float master, out float sfx, out float music)」は音量の取得を行っている。m_DataStore から各音量の取得を行っている。
63行目 : 「public virtual void SetVolumes(float master, float sfx, float music, bool save)」は音量の設定を行っている。gameMixer に対して音量を設定後、save フラグが True であれば m_DataStore にパラメータを設定し SaveData 処理を行っている。
98行目 : 「protected override void Awake()」は Unity 固有の起動処理を行っている。内容としては 2. PersistentSingleton の Awake 処理後、 LoadData を行っている。
107行目 : 「protected virtual void Start()」は Unity 固有の開始処理を行っている。内容としては SetVolumes の処理を行っている。
115行目 : 「protected void LoadData()」は k_SavedGameFile からデータを取得後、SaveData の処理を行っている。
143行目 : 「protected virtual void SaveData()」は m_DataSaver の Save 処理を行っている。
151行目 : 「protected static float LogarithmicDbTransform(float volume)」では音量を指数関数として計算を行っている。