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(互斥锁),代码如下,

/// 
/// Unity的Mono单例基类
/// 
/// 对应的子类
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中才能实现。

实现

我经过多次尝试,终于实现了以上描述的效果,即在EditorOnEnable()函数中实现,代码如下:

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

你可能感兴趣的:(基础,单例,Unity基础小框架,unity,设计模式,c#,mono)