Unity Singleton Pattern 單例模式
看到社團有人在問,Unity 場景切換後怎麼拿到上個場景的資料?嗯,全域變數即可以處理這件事情,把資料放在全域變數中,到下個場景再去讀取。
關於全域變數,便直接想到 Singleton 這設計模式,建立一個唯一存在的物件實體 (Instance),又可以讓全部物件看到操作。
整理一下範例,下次要用直接複製貼上,不需要重新寫。(這模式太常使用到了,直接寫比找到範例複製貼上還要快)
Pure data class
只是要單純存放資料:
public class DataManager
{
private static DataManager singleton;
public static DataManager Singleton
{
get
{
if (singleton == null)
{
singleton = new DataManager();
}
return singleton;
}
}
}
使用上的範例 :
var value = DataManager.Singleton.PropertyName;
Singleton 沒有資料會自動建立,如果已經建立回傳唯一實體資料。
MonoBehaviour class
如果該 Singleton 繼承 MonoBehaviour,需要擁有 MonoBehaviour 的行為,例如 Update,Coroutine 等等,則需要修改一下初始化。原因在於 MonoBehaviour 不能夠使用 new 方式產生,必須使用 UnityEngine.GameObject.AddComponent 方式建立:
using UnityEngine;
public class GameApplication : MonoBehaviour
{
private static GameApplication singleton = null;
public static GameApplication Singleton
{
get
{
// TODO: Automatic creation
if (singleton == null)
{
singleton = new GameObject(t.Name, typeof(GameApplication));
}
return singleton;
}
}
private void Awake()
{
if (singleton != null)
{
Debug.LogErrorFormat(this.gameObject, "Multiple instances of {0} is not allow", GetType().Name);
return;
}
singleton = this;
GameObject.DontDestroyOnLoad(this.gameObject);
}
}
當然這自動初始化建立流程可以不要有,強迫場景必須先行建立,否則後續程式會產生 NullException 的錯誤。或者是初始化建立流程可以改成從 Resource 建立,從 AssetBundle 建立,載入新場景建立等等…。看應用程式需求。
使用方法差不多:
GameApplication.Singleton.StartCoroutine(MyRoutine());
討論
也許有人會認為在第一個範例中,為什麼需要額外用 Singleton 來包裝,而不直接使用直接存取 DataManager.PropertyName
這一個參數?例如宣告:
public class DataManager
{
public static string PropertyName
{
get;
set;
}
}
最主要的是因為 Singleton Getter 可以保留加上一些前置處理的彈性,可以根據不同狀況使用不同的初始建立流程,或者回傳不同的物件實體,例如範例:
public static DataManager Singleton
{
get
{
#if UNITY_IOS
return iosSingleton;
#elif UNITY_ANDROID
return androidSingleton;
#else
throw new System.NotImplementedException();
#endif
}
}
結語
Singleton,保留未來修改的彈性,在需要時時修改的遊戲程式中,這樣的好東西不用嗎?
另外沒有提到的是跟介面 (Interface) 的結合,例如以下範例,不同的條件回傳不同的實體:
using UnityEngine;
public interface IGameApplication
{
void SaveGame();
}
class IOSGameApplication : IGameApplication
{
public void SaveGame()
{
...
}
}
class AndroidGameApplication : IGameApplication
{
public void SaveGame()
{
...
}
}
public static class GameApplication
{
private static IGameApplication singleton = null;
public static IGameApplication Singleton
{
get
{
if (singleton == null)
{
#if UNITY_IOS
singleton = new IOSGameApplication();
#elif UNITY_ANDROID
singleton = new AndroidGameApplication();
#else
throw new System.NotImplementedException();
#endif
}
return singleton;
}
}
}
沒有留言: