1. 程式人生 > >Unity單例模式(普通單例和繼承MonoBehaviour的單例)

Unity單例模式(普通單例和繼承MonoBehaviour的單例)

  單例類是開發過程中必不可少的東西。介紹完兩個常用單例型別後會介紹一個簡單卻很實用的MonoEvent。

  關於單例的寫法網上有很多,這篇文章主要想介紹的是MonoEvent以及為之後的計時器做準備。

1.普通單例

  最普通的寫法,加了個執行緒鎖,通過屬性訪問,不光是Unity,所有的c#程式都能用。

using UnityEngine;
/// <summary>
/// 不繼承mono的單例基類,如果需要Update,可以將方法註冊進MonoEvent的事件中
/// </summary>
/// <typeparam name="T"></typeparam>
public class Singleton<T> where T : new()
{
    private static T _instance;
    private static readonly object objlock = new object();

    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (objlock)
                {
                    if (_instance == null)
                    {
                        _instance = new T();
                    }
                }
            }
            return _instance;
        }
    }
}

 普通單例類的構造有兩種方式,看習慣,個人比較喜歡第一種,呼叫的時候可以少寫一個單詞一對括號

1.通過繼承單例基類

 構造:  public class MyClass : Singleton<MyClass>{ }

 呼叫:  MyClass.Instance.MethodName

2.不繼承單例基類

 構造:  public class MyClass { }

 呼叫:  Singleton<MyClass>.Instance.MethodName

2.繼承MonoBehaviour的單例

 不建議手動在Scene中的某個GameObject上掛載繼承Mono的單例類,呼叫的時候程式碼會自動建立

using UnityEngine;

/// <summary>
/// 此單例繼承於Mono,絕大多情況下,都不需要使用此單例型別。請使用Singleton
/// 不需要手動掛載
/// </summary>
public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;
    /// <summary>
    /// 執行緒鎖
    /// </summary>
    private static readonly object _lock = new object();
    /// <summary>
    /// 程式是否正在退出
    /// </summary>
    protected static bool ApplicationIsQuitting { get; private set; }
    /// <summary>
    /// 是否為全域性單例
    /// </summary>
    protected static bool isGolbal = true;

    static MonoSingleton()
    {
        ApplicationIsQuitting = false;
    }

    public static T Instance
    {
        get
        {
            if (ApplicationIsQuitting)
            {
                if (Debug.isDebugBuild)
                {
                    Debug.LogWarning("[Singleton] " + typeof(T) +
                                            " already destroyed on application quit." +
                                            " Won't create again - returning null.");
                }

                return null;
            }

            lock (_lock)
            {
                if (_instance == null)
                {
                    // 先在場景中找尋
                    _instance = (T)FindObjectOfType(typeof(T));

                    if (FindObjectsOfType(typeof(T)).Length > 1)
                    {
                        if (Debug.isDebugBuild)
                        {
                            Debug.LogWarning("[Singleton] " + typeof(T).Name +" should never be more than 1 in scene!");
                        }

                        return _instance;
                    }

                    // 場景中找不到就建立新物體掛載
                    if (_instance == null)
                    {
                        GameObject singletonObj = new GameObject();
                        _instance = singletonObj.AddComponent<T>();
                        singletonObj.name = "(singleton) " + typeof(T);

                        if (isGolbal && Application.isPlaying)
                        {
                            DontDestroyOnLoad(singletonObj);
                        }

                        return _instance;
                    }
                }

                return _instance;
            }
        }
    }

    /// <summary>
    /// 當工程執行結束,在退出時,不允許訪問單例
    /// </summary>
    public void OnApplicationQuit()
    {
        ApplicationIsQuitting = true;
    }
}

3.MonoEvent

  很多時候,我們寫的類並不像繼承MonoBehaviour,但是我們又想這個類能夠在Mono的生命週期裡做一些事。比如Update,那麼我們可以這樣寫

MonoEvent.Instance.UPDATE += MethodName

using System;

/// <summary>
/// Mono生命週期事件
/// 一些不繼承Mono的類如果想在Mono生命週期做一些事,可以往這裡新增
/// </summary>
public class MonoEvent : MonoSingleton<MonoEvent>
{
    public event Action UPDATE;
    public event Action FIXEDUPDATE;
    public event Action ONGUI;
    public event Action LATEUPDATE;

    private void Update()
    {
        UPDATE?.Invoke();
    }

    private void FixedUpdate()
    {
        FIXEDUPDATE?.Invoke();
    }

    private void OnGUI()
    {
        ONGUI?.Invoke();
    }

    private void LateUpdate()
    {
        LATEUPDATE?.Invoke();
    }
}