Unity 單例模式

單例模式是設(shè)計(jì)模式中很常用的一種模式,它的目的是讓一個類在程序運(yùn)行期間有且只有一個實(shí)例。關(guān)于 Unity 中如何實(shí)現(xiàn)單例模式其實(shí)有很多文章,但是我找不到一篇能夠完整講述整個單例模式實(shí)現(xiàn)流程的文章,大部分都是直接貼代碼,這對于我這種不喜歡知其然,卻不知其所以然的人來說是遠(yuǎn)遠(yuǎn)不夠的,所以我翻閱了國外一些資料,在這里寫下這篇文章,旨在通過完整的流程講述如何在 Unity 中實(shí)現(xiàn)單例模式,以及實(shí)現(xiàn)該模式時需要注意的一些事項(xiàng),希望能夠幫助初學(xué)者。另外,如有任何錯誤請盡管指出。

項(xiàng)目源碼倉庫:https://github.com/darylgo/UnitySingleton

1 單例模式的應(yīng)用場景

在使用 Unity 開發(fā)游戲的時候,經(jīng)常會需要各種 Manager 類用于管理一些全局的變量和方法,例如最常見的 GameManager 用于記錄各種需要在整個游戲過程中持久化的數(shù)據(jù)。本文以 GameManager 為例,假設(shè)我們有以下幾個需求:

  1. 整個游戲過程中必須有且只有一個 GameManager
  2. 在 GameManager 里會記錄一個叫 Value 的整型變量
  3. 切換游戲場景的時候 GameManager 不能被銷毀
  4. 有兩個游戲場景分別叫 FirstScene 和 SecondScene
  5. 每一個場景中都會有一個按鈕,點(diǎn)擊后會跳轉(zhuǎn)到另一場景,并且對 GameManager.Value 進(jìn)行 +1 操作
  6. 每一個場景中都會顯示當(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;
    }
}
  1. 靜態(tài)的 GameManager 屬性 Instance 保證了它可以通過類訪問,而不是通過實(shí)例訪問。
  2. Instance 屬性私有的 set 保證了它只允許在 GameManager 內(nèi)部賦值,外部只能讀取。
  3. 繼承 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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。