我们都知道可以使用协程这样制作定时器
using System.Collections;
using UnityEngine;
using System;
public class QWidget : MonoBehaviour
{
private void Awake()
{
StartCoroutine(UpdateTimer(1.5f,()=> {
Debug.Log(123);
}));
}
IEnumerator UpdateTimer(float timer, Action callBack)
{
var wait = new WaitForSeconds(timer);
while (true)
{
yield return wait;
callBack();
}
}
}
但是,如果使用上面的代码,场景内协程多了将会出现明显的卡顿
所以有了下面的这个定时器:
如果场景内存在定时器时,仅开启一个协程。通过计算增量时间执行对应的事件
如果没有定时器在运行就关闭协程!
栗子:
using UnityEngine;
public class QWidget : MonoBehaviour
{
private void Awake()
{
QTimer timer = new QTimer();
timer.timerout = () => { Debug.Log("ee"); };
timer.Start(1);
QTimer timer2 = new QTimer();
timer2.timerout = () => { Debug.Log("qq"); };
timer2.Start(1);
QTimer.SingleShot(3, () =>
{
timer.Stop();
timer2.Stop();
Debug.Log(222222);
});
}
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class QTimer
{
private static int coroutineID;
private static int idCount = 0;
private static Stack<int> removeStack = new Stack<int>();
private static IDictionary<int, TimerModel> timerEventDic = new Dictionary<int, TimerModel>();
private int id;
private bool isNotify = false;
public Action timerout;
/// <summary>
/// 定时器开始
/// </summary>
/// <param name="timer"></param>
/// <param name="isDisposable">是否一次性</param>
public void Start(float timer, bool isDisposable=false)
{
id = idCount++;
timerEventDic.Add(id, new TimerModel(timer,timerout, isDisposable));
if (!isNotify)
{
isNotify = true;
coroutineID = QCoroutine.Start(Notify);
}
}
/// <summary>
/// 一次性定时器
/// </summary>
/// <param name="timer"></param>
/// <param name="callBack"></param>
public static void SingleShot(float timer, Action callBack)
{
QTimer qTimer = new QTimer();
qTimer.timerout = callBack;
qTimer.Start(timer, true);
}
public void Stop()
{
removeStack.Push(id);
}
IEnumerator Notify()
{
int i, count;
while (true)
{
foreach (var timerEvent in timerEventDic)
{
if (timerEvent.Value.Update())
{
removeStack.Push(timerEvent.Key);
}
}
if (removeStack.Count > 0)
{
count = removeStack.Count;
for (i = 0; i < count; i++)
{
timerEventDic.Remove(removeStack.Pop());
}
if (timerEventDic.Count <= 0)
{
isNotify = false;
QCoroutine.Stop(coroutineID);
}
}
yield return null;
}
}
private class TimerModel
{
private bool isDisposable;
private Action timerout;
public float time;
public float timer;
public TimerModel(float timer, Action function, bool isDisposable=false)
{
time = timer;
this.timer = timer;
timerout = function;
this.isDisposable = isDisposable;
}
~TimerModel()
{
timerout = null;
}
public bool Update()
{
if (timerout == null) return true;
time -= Time.deltaTime;
if (time <= 0)
{
time = timer;
timerout();
if (isDisposable) return true;
}
return false;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class QCoroutine : MonoBehaviour
{
private static int idCount = 0;
private static QCoroutine instance;
private static QCoroutine Instance { get { return instance ?? (instance=new GameObject("Coroutine").AddComponent<QCoroutine>()); } }
private static IDictionary<int, Coroutine> dic = new Dictionary<int,Coroutine>();
public static int Start(Func<IEnumerator> callBack)
{
var id = idCount++;
dic.Add(id, Instance.StartCoroutine(callBack()));
return id;
}
public static void Stop(int coroutineId)
{
Instance.StopCoroutine(dic[coroutineId]);
}
}