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