单例模式(Singleton Pattern)【创建型】__MonoSingleton,及 相关应用(TimerManager)
如果项目中有很多个单例,那么我们就必须每次都写这些代码,有什么办法可以省去这些不必要的代码呢?
using UnityEngine;
/// <summary>
/// Be aware this will not prevent a non singleton constructor
/// such as `T myT = new T();`
/// To prevent that, add `protected T () {}` to your singleton class.
///
/// As a note, this is made as MonoBehaviour because we need Coroutines.
/// </summary>
public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
private static object _lock = new object();
public static T Instance
{
get
{
if (applicationIsQuitting)
{
Debug.LogWarning("[Singleton] Instance '" + typeof(T) +
"' already destroyed on application quit." +
" Won't create again - returning null.");
return null;
}
lock (_lock)
{
if (_instance == null)
{
_instance = FindObjectOfType<T>();
if (FindObjectsOfType<T>().Length > 1)
{
Debug.LogError("[Singleton] Something went really wrong " +
" - there should never be more than 1 singleton!" +
" Reopenning the scene might fix it.");
return _instance;
}
if (_instance == null)
{
GameObject singleton = new GameObject();
_instance = singleton.AddComponent<T>();
singleton.name = "(singleton) " + typeof(T).ToString();
DontDestroyOnLoad(singleton);
Debug.Log("[Singleton] An instance of " + typeof(T) +
" is needed in the scene, so '" + singleton +
"' was created with DontDestroyOnLoad.");
}
else
{
//Debug.Log("[Singleton] Using instance already created: " + _instance.gameObject.name);
}
}
return _instance;
}
}
}
private static bool applicationIsQuitting = false;
/// <summary>
/// When Unity quits, it destroys objects in a random order.
/// In principle, a Singleton is only destroyed when application quits.
/// If any script calls Instance after it have been destroyed,
/// it will create a buggy ghost object that will stay on the Editor scene
/// even after stopping playing the Application. Really bad!
/// So, this was made to be sure we're not creating that buggy ghost object.
/// </summary>
protected virtual void OnDestroy()
{
applicationIsQuitting = true;
}
public virtual void Awake()
{
if (gameObject.transform.parent == null)
DontDestroyOnLoad(gameObject);
}
}
应用: TimerManager——单例协同
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
using UnityEngine.Events;
public class TimerManager : MonoSingleton<TimerManager> {
public delegate void TimeoutEventDelegate(params object[] arr);
public class Timer
{
CrudeElapsedTimer _TimerEntity;
TimeoutEventDelegate _Callback;
object[] _Params;
public Timer(float timesec, TimeoutEventDelegate cb, params object[] arr)
{
_Params = arr;
_Callback = cb;
_TimerEntity = new CrudeElapsedTimer(timesec);
}
public bool Update()
{
if (_TimerEntity != null)
{
_TimerEntity.Advance(Time.deltaTime);
if (_TimerEntity.TimeOutCount > 0)
{
if (_Callback != null)
{
_Callback(_Params);
}
return true;
}
}
return false;
}
}
Dictionary<Type, Timer> _CallbackDic = new Dictionary<Type, Timer>();
public TimerUpdateEvent _TimerUpdateEvent = new TimerUpdateEvent();
List<Type> _WillRemove = new List<Type>();
// Update is called once per frame
void Update() {
ClearWillRemove();
foreach (KeyValuePair<Type, Timer> kv in _CallbackDic)
{
bool b = kv.Value.Update();
if (b)
{
_WillRemove.Add(kv.Key);
}
}
ClearWillRemove();
if (_TimerUpdateEvent != null)
{
_TimerUpdateEvent.Invoke();
}
}
void ClearWillRemove()
{
foreach (Type t in _WillRemove)
{
if (_CallbackDic.ContainsKey(t))
{
_CallbackDic.Remove(t);
}
}
_WillRemove.Clear();
}
public void AddTimer<T>(float timesec, TimeoutEventDelegate cb, params object[] arr)
{
StartCoroutine(AddTimerCoroutine<T>(timesec, cb, arr));
}
IEnumerator AddTimerCoroutine<T>(float timesec, TimeoutEventDelegate cb, params object[] arr)
{
yield return 0;
if (!_CallbackDic.ContainsKey(typeof(T)))
{
_CallbackDic.Add(typeof(T), new Timer(timesec, cb, arr));
}
}
public void CancelTimer<T>()
{
StartCoroutine(CancelTimerCoroutine<T>());
}
IEnumerator CancelTimerCoroutine<T>()
{
yield return 0;
if (!_WillRemove.Contains(typeof(T)))
{
_WillRemove.Add(typeof(T));
}
}
#region calculate elapsed time tool
private TimeSpan startTime;
public void StartTime()
{
startTime = DateTime.Now.TimeOfDay;
}
/// <summary>
/// 开始时间
/// </summary>
/// <returns>开始时间</returns>
public TimeSpan StartTimeFlag()
{
TimeSpan stf = DateTime.Now.TimeOfDay;
return stf;
}
/// <summary>
/// 输出经过时间
/// </summary>
/// <param name="st">相对应的开始时间</param>
/// <param name="timeFlag">需要计算的时间标志</param>
public void LogElapsedTimeFlag(TimeSpan st,string timeFlag = "")
{
string te = (DateTime.Now.TimeOfDay - st).ToString();
Debug.LogFormat("{0} elapse: {1}", timeFlag, te);
}
public TimeSpan EndTime(string entFlag = "")
{
TimeSpan endTime = DateTime.Now.TimeOfDay;
return endTime;
}
public void LogTimeElapsed(string tag = "")
{
string timeElapsed = (DateTime.Now.TimeOfDay - startTime).ToString();
Debug.LogFormat("{0} !!!!!elapse: {1}", tag, timeElapsed);
}
#endregion
#region Call Example
//void OnTimeout(params object[] arr)
//{
// //do something
//}
//TimerManager.Instance.AddTimer<T>(5.0f, OnTimeout);
//TimerManager.Instance.CancelTimer<T>();
#endregion
}
public class TimerUpdateEvent : UnityEvent
{
}
应用:
TimerManager.Instance.StartCoroutine(Test());
PS:MonoBehavior单例
今天我们就来看看在unity中如何使用单例模式,在unity中,我们分两种单例,一种是继承monobehavior的单例,一种是普通单例。
其实在unity中,如果脚本是继承monobehavior,那么使用起单例来更加简单。
只需要在Awake()里面,添加一句instance = this;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
2.普通类的单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|