单例类是开发过程中必不可少的东西。介绍完两个常用单例类型后会介绍一个简单却很实用的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();
}
}