單例模式是設(shè)計模式中很常用的一種模式齐佳,它的目的是讓一個類在程序運(yùn)行期間有且只有一個實(shí)例抓艳。關(guān)于 Unity 中如何實(shí)現(xiàn)單例模式其實(shí)有很多文章鬓椭,但是我找不到一篇能夠完整講述整個單例模式實(shí)現(xiàn)流程的文章盛嘿,大部分都是直接貼代碼昨寞,這對于我這種不喜歡知其然瞻惋,卻不知其所以然的人來說是遠(yuǎn)遠(yuǎn)不夠的,所以我翻閱了國外一些資料援岩,在這里寫下這篇文章歼狼,旨在通過完整的流程講述如何在 Unity 中實(shí)現(xiàn)單例模式,以及實(shí)現(xiàn)該模式時需要注意的一些事項享怀,希望能夠幫助初學(xué)者羽峰。另外,如有任何錯誤請盡管指出。
項目源碼倉庫:https://github.com/darylgo/UnitySingleton
1 單例模式的應(yīng)用場景
在使用 Unity 開發(fā)游戲的時候梅屉,經(jīng)常會需要各種 Manager 類用于管理一些全局的變量和方法值纱,例如最常見的 GameManager 用于記錄各種需要在整個游戲過程中持久化的數(shù)據(jù)。本文以 GameManager 為例坯汤,假設(shè)我們有以下幾個需求:
- 整個游戲過程中必須有且只有一個 GameManager
- 在 GameManager 里會記錄一個叫 Value 的整型變量
- 切換游戲場景的時候 GameManager 不能被銷毀
- 有兩個游戲場景分別叫 FirstScene 和 SecondScene
- 每一個場景中都會有一個按鈕计雌,點(diǎn)擊后會跳轉(zhuǎn)到另一場景,并且對 GameManager.Value 進(jìn)行 +1 操作
- 每一個場景中都會顯示當(dāng)前 GameManager.Value 的值
下面我們就來一步步實(shí)現(xiàn)單例模式下的 GameManager玫霎。
2 實(shí)現(xiàn)單例模式的 GameManager
首先凿滤,我們會定義一個類叫 GameManager,它繼承了 MonoBehaviour庶近,具體代碼如下所示:
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
public int Value { get; set; } = 0;
private void Awake()
{
Instance = this;
}
}
- 靜態(tài)的 GameManager 屬性 Instance 保證了它可以通過類訪問翁脆,而不是通過實(shí)例訪問。
- Instance 屬性私有的 set 保證了它只允許在 GameManager 內(nèi)部賦值鼻种,外部只能讀取反番。
- 繼承 MonoBehaviour 類的實(shí)例都是又 Unity 游戲引擎創(chuàng)建的,不能通過構(gòu)造函數(shù)創(chuàng)建叉钥,所以我們在 Awake() 方法里對 Instance 進(jìn)行賦值罢缸,保證了我們能夠在第一時間初始化。
創(chuàng)建完 GameManager 類之后投队,我們需要在游戲場景中創(chuàng)建一個也叫做 GameManager 的 GameObject枫疆,并且把 GameManager 類作為 Component 添加到 GameObject 上:
接下來,我們要處理實(shí)現(xiàn)單例模式時遇到的第一個問題敷鸦,就是 Unity 在切換游戲場景的時候息楔,默認(rèn)會消除上一個游戲場景里所有的 GameObject 對象,所以我們的 GameManager 對象也不可避免的會被銷毀扒披,這是我們不希望看到的值依,所以我們使用 DontDestroyOnLoad() 方法讓 GameManager 在切換游戲場景的時候不會被銷毀:
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
public int Value { get; set; } = 0;
private void Awake()
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
在處理完 GameManager 被銷毀的情況之后,我們要再處理另一個問題碟案,就是我們的 GameManager 是在第一個場景里創(chuàng)建的愿险,當(dāng)我們從第二個游戲場景切換回第一個游戲場景的時候,Unity 并不是恢復(fù)第一個游戲場景价说,而是會重新創(chuàng)建出一個新游戲場景辆亏,這就會導(dǎo)致一個新的 GameManager 對象被創(chuàng)建,這就不能保證 GameManager 對象的唯一性熔任,如下所示:
要解決上面的問題褒链,我們需要在 GameManager 類的 Awake() 方法里增加一些邏輯判斷,當(dāng)檢查到已經(jīng)有一個 GameManager 對象存在的時候疑苔,就把當(dāng)前的 GameManager 對象銷毀甫匹,如下所示:
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
public int Value { get; set; } = 0;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
}
3 通用單例模式
單例模式在開發(fā)過程中十分常見,所以我們經(jīng)常會使用泛型寫一個單例模式的基類,這樣我們就可以通過繼承該基類輕松實(shí)現(xiàn)單例模式兵迅,代碼如下所示:
public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
public static T Instance { get; private set; }
protected void Awake()
{
if (Instance == null)
{
Instance = (T) this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
}
我們使用 Singleton 基類實(shí)現(xiàn)我們的 GameManager抢韭,代碼如下所示:
public class GameManager : Singleton<GameManager>
{
public int Value { get; set; } = 0;
}