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