作者:王選易,出處:http://www.cnblogs.com/neverdie/ 歡迎轉載,也請保留這段聲明。如果你喜歡這篇文章,請點推薦。謝謝!
該博客中的代碼均出自我的開源項目 : 迷你微信
為什么需要單例模式
游戲中需要單例有以下幾個原因:
- 我們需要在游戲開始前和結束前做一些操作,比如網絡的鏈接和斷開,資源的加載和卸載,我們一般會把這部分邏輯放在單例里。
- 單例可以控制初始化和銷毀順序,而靜態變量和場景中的GameObject都無法控制自己的創建和銷毀順序,這樣就會造成很多潛在的問題。
- Unity3D的GameObject需要動態創建。而不是固定在場景里,我們需要使用單例來創建GameObject。
- Unity3D的場景中的各個GameObject需要從單例中存取數據。
單例的設計原則
在設計單例的時候,我並不建議采取延遲初始化的方案,正如雲風所說:
對於單件的處理,采用靜態對象和惰性初始化的方案,簡直就是 C++ 程序員的陋習。Double Checked Locking is broken,相信很多人都讀過了。過於依賴語法糖,通常就會造成這種結果。其實讓程序有明顯的初始化和退出階段,是很容易被規划出來的。把單件(singleton) 的處理放在正確的時機,以正確的次序來處理並非難事。
我們應該在程序某處明確定義單例是否被初始化,在初始化執行完畢后再執行正常的游戲邏輯
- 盡量避免多線程創建單例帶來的復雜性
- 在某處定義了一定的初始化順序后,可以在游戲結束的時候按照相反的順序銷毀這些單例
設計單例的基類
在Unity中,我們需要一個基類來為所有單例的操作提供統一的接口,同時,我們還要讓所有單例繼承MonoBehaviour,只有這樣才能讓單例自由使用協程這一特性。
基類設計如下,代碼鏈接
using System;
using UnityEngine;
namespace MiniWeChat
{
[RequireComponent(typeof(GameRoot))]
public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static T _instance;
public static T GetInstance()
{
return _instance;
}
public void SetInstance(T t)
{
if (_instance == null)
{
_instance = t;
}
}
public virtual void Init()
{
return;
}
public virtual void Release()
{
return;
}
}
}
設計單例的管理類
除了設計基類之外, 還需要設計一個讓所有基類初始化和銷毀的類,我們把這個類叫做GameRoot,並且把它綁定在一個名為GameRoot的GameObject上,並且把這個GameObject放在游戲進入的Main場景中。
GameRoot類設計如下,代碼鏈接
namespace MiniWeChat
{
public class GameRoot : MonoBehaviour
{
private static GameObject _rootObj;
private static List<Action> _singletonReleaseList = new List<Action>();
public void Awake()
{
_rootObj = gameObject;
GameObject.DontDestroyOnLoad(_rootObj);
StartCoroutine(InitSingletons());
}
/// <summary>
/// 在這里進行所有單例的銷毀
/// </summary>
public void OnApplicationQuit()
{
for (int i = _singletonReleaseList.Count - 1; i >= 0; i--)
{
_singletonReleaseList[i]();
}
}
/// <summary>
/// 在這里進行所有單例的初始化
/// </summary>
/// <returns></returns>
private IEnumerator InitSingletons()
{
yield return null;
// Init Singletons
}
private static void AddSingleton<T>() where T : Singleton<T>
{
if (_rootObj.GetComponent<T>() == null)
{
T t = _rootObj.AddComponent<T>();
t.SetInstance(t);
t.Init();
_singletonReleaseList.Add(delegate()
{
t.Release();
});
}
}
public static T GetSingleton<T>() where T : Singleton<T>
{
T t = _rootObj.GetComponent<T>();
if (t == null)
{
AddSingleton<T>();
}
return t;
}
}
}
如何拓展新的單例
有了以上兩個類之后,當我們需要新創建一個類的時候,就可以繼承Singleton<T>
來創建新的單例,重寫Init和Release方法,同時在GameRoot的InitSingleton方法的適當順序執行AddSingleton<T>
方法即可。具體的使用可以參考該類代碼鏈接