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

Unity Singleton Pattern 單例模式

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

沒有留言: