關於 web service, unity, blogger 等軟體工程筆記

Global data using singlton pattern in Unity

Edit icon 沒有留言
Unity

看到社團有人在問,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)
{
var t = typeof(GameApplication);
new GameObject(t.Name, t);
}

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;
}
}
}

沒有留言: