天天看點

Unity的MonoBehaviour單例設定

Unity的MonoBehaviour單例設定

    • MonoBehaviour的基本單例模式
    • MonoBehaviour單例的泛型基類
    • MonoBehaviour單例腳本的問題
    • 解決方案
      • 互斥鎖Mutex
      • 使用Editor.OnEnable()監測
        • 想法
        • 實作
    • 參考連結

MonoBehaviour的基本單例模式

根據Unity的

MonoBehaviour

腳本建立單例模式,我們一般的建立方式是:

using UnityEngine;

public class MonoSingleton : MonoBehaviour
{
    public static MonoSingleton Instance = null;

    protected virtual void Awake()
    {
        Instance = this;
    }
}
           

MonoBehaviour單例的泛型基類

使用泛型之後是:

using UnityEngine;

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
    public static T Instance = null;

    protected virtual void Awake()
    {
        Instance = this as T;
    }
}
           

MonoBehaviour單例腳本的問題

但這樣是有問題的單例模式,在其他的文章中提到單例模式的原則(目的)是“保證一個類隻有一個執行個體,并提供全局通路點”。

根據上面所述,對比我們這裡建立的

MonoBehaviour

類的單例并沒有對

MonoBehaviour

類的構造函數進行非公有化,我們依然可以自由的建立該類的執行個體對象。因為

MonoBehaviour

類有可視化操作的特點(手動拖拽到場景

GameObject

上),這部分是Unity内部封裝的,為了不出現不必要的錯誤,我們不能從根本上(即Unity的内部)改變這點。

解決方案

互斥鎖Mutex

經過查找資料,找到不少方法,但似乎效果都不是很理想,或者說不是我想要的效果。但這裡我還是分享一個看起來不錯的方式:就是使用互斥類

Mutex

(互斥鎖),代碼如下,

/// <summary>
/// Unity的Mono單例基類
/// </summary>
/// <typeparam name="T">對應的子類</typeparam>
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
    private static T m_Instance = null;
    private new static string name;
    private static Mutex mutex;//互斥類Mutex
    public static T Instance
    {
        get
        {
            if (m_Instance == null)
            {
                if (IsSingle())
                {
                    m_Instance = new GameObject(name, typeof(T)).GetComponent<T>();
                    m_Instance.Init();
                }
            }
            return m_Instance;
        }
    }
    private static bool IsSingle()
    {
        name = "Singleton of " + typeof(T).ToString();
        mutex = new Mutex(false, name, out bool createdNew);
        mutex.WaitOne();
        return createdNew;
    }

    private bool m_isDestroy = true;

    protected virtual void Awake()
    {
        m_isDestroy = true;
        if (m_Instance == null)
        {
            if (IsSingle())
            {
                m_Instance = this as T;
                m_Instance.Init();
            }
        }
        else
        {
            Debug.LogError("[Singleton] " + typeof(T).Name + " should never be more than 1 in scene!Owner is “" + transform.name + "”.");
            m_isDestroy = false;
            Destroy(this);
        }
    }
    protected abstract void Init();
    protected abstract void DisInit();
    private void OnDestroy()
    {
        if (m_Instance != null)
        {
            if (m_isDestroy)
            {
                if (null != mutex)
                {
                    mutex.ReleaseMutex();
                }
                m_Instance = null;
            }
            DisInit();
        }
    }
    private void OnApplicationQuit()
    {
        if (null != mutex)
        {
            mutex.ReleaseMutex();
            mutex.Close();
            mutex = null;
        }
    }
}
           

根據互斥鎖

Mutex

的特點,使将本在程式運作之初,隻承認第一個建立的

MonoBehaviour

單例類,并将多餘的的

Destroy

銷毀掉,隻留下一個。

這不失為一種好辦法。

使用Editor.OnEnable()監測

想法

但我想的是

MonoBehaviour

單例類腳本,在編輯器狀态下,被手動拖拽到

GameObject

上之時,便能識别,場景中是否已經存在該單例類;如果已存在則不會再将該腳本建立(拖拽)到任何

GameObject

上。

而這種操作就隻能在

UnityEditor

庫下,每個

MonoBehaviour

腳本對應的

Editor

中才能實作。

實作

我經過多次嘗試,終于實作了以上描述的效果,即在

Editor

OnEnable()

函數中實作,代碼如下:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(*****), true), CanEditMultipleObjects]
public class MonoSingletonEditor : Editor
{
    private static ***** tInstance = null;
    protected ***** sMono;

    protected virtual void OnEnable()
    {
        sMono = ((*****)target);
        if (null == tInstance)
        {
            tInstance = sMono;
        }
        else if (sMono != tInstance)
        {
            DestroyImmediate(target);
            Debug.LogError("There can only be one in this script scenario!");
            return;
        }
    }
}
           

代碼中的 “ ***** ” 是對應的

MonoBehaviour

單例腳本名,而且這也用到了Unity内部封裝的一些函數,故這種方式是無法泛型的。每一個

MonoBehaviour

單例就要有一個相對的該類。

但經過我測試,也确實是實作了我上面描述的效果,再結合互斥鎖

Mutex

的泛型基類,就可以很好的防止該類被多次建立。

參考連結

https://www.cnblogs.com/fastcam/p/5924036.html