【Unity3D基礎教程】給初學者看的Unity教程(七):在Unity中構建健壯的單例模式(Singleton)


作者:王選易,出處:http://www.cnblogs.com/neverdie/ 歡迎轉載,也請保留這段聲明。如果你喜歡這篇文章,請點推薦。謝謝!

hh

該博客中的代碼均出自我的開源項目 : 迷你微信

為什么需要單例模式

游戲中需要單例有以下幾個原因:

  • 我們需要在游戲開始前和結束前做一些操作,比如網絡的鏈接和斷開,資源的加載和卸載,我們一般會把這部分邏輯放在單例里。
  • 單例可以控制初始化和銷毀順序,而靜態變量和場景中的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>方法即可。具體的使用可以參考該類代碼鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM