using System;
using System.Collections;
using System.Collections.Generic;
namespace MYTOOL
{
public class TimerManager : MonoSingletonTemplate<TimerManager>
{
// 单例模式
private readonly BinaryHeapMin<Timer> timers = new BinaryHeapMin<Timer>();
// 最小二叉堆
private static float fRealTime = 0;
// set start time
private System.DateTime startTime;
protected override void OnInit()
{
startTime = System.DateTime.Now;
}
// 添加定时器
internal static void SetTimer(Timer tr)
{
GetInstance().SetTimer(tr, fRealTime);
}
// 添加定时器
private void SetTimer(Timer tr, float fTime)
{
#if UNITY_EDITOR
if (timers.Contains(tr))
{
UnityEngine.Debug.LogError("重复添加已存在的定时器到最小堆!");
return;
}
#endif
tr.fTimer += fTime + tr.Interval;
timers.Enqueue(tr);
}
private void CheckTimer(float fTime)
{
// 更新时间线
fRealTime = fTime;
int nLen = 0;
while (true)
{
if (nLen >= timers.Size)
break;
nLen++;
// 最近的定时器
Timer tr = timers.FindMin();
if (tr == null)
{
timers.DequeueMin();
break;
}
if (fTime <= tr.fTimer)
{
break;
}
tr.Run();
if (tr.Killed)
{
timers.DequeueMin();
}
else
{
tr.fTimer += tr.Interval;
timers.Enqueue(timers.DequeueMin());
}
}
}
private void Update()
{
//CheckTimer(UnityEngine.Time.realtimeSinceStartup);
CheckTimer((float)(DateTime.Now - startTime).TotalSeconds);
}
}
public class Timer : IComparable
{
public delegate void OnTimer(Timer sender);
public readonly float Interval;
private readonly OnTimer onTimer;
private readonly bool RunOnce;
internal bool Killed = false;
internal float fTimer = 0;
public Timer(float interval, OnTimer onTimer, bool autoRun, bool runOnce = false)
{
this.onTimer = onTimer;
Interval = interval;
RunOnce = runOnce;
if (autoRun)
{
TimerManager.SetTimer(this);
}
}
/// <summary>
/// 开始运行
/// </summary>
public void Start()
{
TimerManager.SetTimer(this);
}
/// <summary>
/// 停止运行
/// </summary>
public void Stop()
{
Killed = true;
}
public int CompareTo(object obj)
{
return CompareTo(((Timer)obj));
}
protected int CompareTo(Timer other)
{
return fTimer.CompareTo(other.fTimer);
}
/// <summary>
/// 定时器执行一次
/// </summary>
internal void Run()
{
if (Killed)
return;
if (RunOnce)
Killed = true;
onTimer?.Invoke(this);
}
}
/// <summary>
/// 最小二叉堆,T必须继承IComparable
/// </summary>
/// <typeparam name="T"></typeparam>
public class BinaryHeapMin<T>
{
private readonly List<T> list = new List<T>();
public int Size => list.Count;
/// <summary>
/// 入堆
/// </summary>
/// <param name="obj"></param>
public void Enqueue(T obj)
{
list.Add(obj);
int i = list.Count;
while (i > 1 && Comparer.Default.Compare(list[i / 2 - 1], obj) > 0)
{
list[i - 1] = list[i / 2 - 1];
i /= 2;
}
list[i - 1] = obj;
}
/// <summary>
/// 取最小值,堆空的时候返回空
/// </summary>
/// <returns></returns>
public T FindMin()
{
if (list.Count == 0)
{
return default;
}
return list[0];
}
/// <summary>
/// 出堆
/// </summary>
/// <returns></returns>
public T DequeueMin()
{
int nCount = list.Count;
if (nCount == 0)
{
return default;
}
T tmpObj = FindMin();
int i = 1;
while ((2 * i + 1) <= nCount)
{
if (Comparer.Default.Compare(list[2 * i - 1], list[2 * i]) <= 0)
{
list[i - 1] = list[2 * i - 1];
list[2 * i - 1] = tmpObj;
i = 2 * i;
}
else
{
list[i - 1] = list[2 * i];
list[2 * i] = tmpObj;
i = 2 * i + 1;
}
}
T delObj = list[i - 1];
if (i != nCount)
{
//如果搜索到的对象就是数组的最后一个对象,则什么都不要做
list[i - 1] = list[nCount - 1];
}
//将最后一个对象删除
list.RemoveAt(nCount - 1);
return delObj;
}
public bool Contains(T obj)
{
return list.Contains(obj);
}
}
}
2025.3.22修改,有些情况下期望定时器运行指定次数。所以对上述脚本做了简单调整。
至于为什么使用short,而不是int,主要是为了节省几个字节。 大部分情况下,short类型足够。
using System;
using System.Collections;
using System.Collections.Generic;
namespace MYTOOL
{
public class TimerManager : MonoSingletonTemplate<TimerManager>
{
// 单例模式
private readonly BinaryHeapMin<Timer> timers = new BinaryHeapMin<Timer>();
// 最小二叉堆
private static float fRealTime = 0;
// set start time
private System.DateTime startTime;
protected override void OnInit()
{
startTime = System.DateTime.Now;
}
// 添加定时器
internal static void SetTimer(Timer tr)
{
GetInstance().SetTimer(tr, fRealTime);
}
// 添加定时器
private void SetTimer(Timer tr, float fTime)
{
#if UNITY_EDITOR
if (timers.Contains(tr))
{
UnityEngine.Debug.LogError("重复添加已存在的定时器到最小堆!");
return;
}
#endif
tr.fTimer += fTime + tr.fInterval;
timers.Enqueue(tr);
}
private void CheckTimer(float fTime)
{
// 更新时间线
fRealTime = fTime;
int nLen = 0;
while (true)
{
if (nLen >= timers.Size)
break;
nLen++;
// 最近的定时器
Timer tr = timers.FindMin();
if (tr == null)
{
timers.DequeueMin();
break;
}
if (fTime < tr.fTimer)
{
break;
}
tr.Run();
if (tr.Killed)
{
timers.DequeueMin();
}
else
{
tr.fTimer += tr.fInterval;
timers.Enqueue(timers.DequeueMin());
}
}
}
private void Update()
{
//CheckTimer(UnityEngine.Time.realtimeSinceStartup);
CheckTimer((float)(DateTime.Now - startTime).TotalSeconds);
}
}
public class Timer : IComparable
{
public delegate void OnTimer(Timer sender);
internal readonly float fInterval;
private readonly OnTimer _timer;
private readonly short _maxRunCount;
private short _currentRunCount;
internal bool Killed = false;
internal float fTimer = 0;
/// <summary>
/// 轻量化计时器
/// </summary>
/// <param name="interval">执行间隔(秒)</param>
/// <param name="timer">回调函数</param>
/// <param name="autoRun">是否自动启动</param>
/// <param name="maxRunCount">最大执行次数(0和负数表示无限)</param>
public Timer(float interval, OnTimer timer, bool autoRun, short maxRunCount = -1)
{
fInterval = interval;
_timer = timer;
_maxRunCount = maxRunCount;
_currentRunCount = 0;
if (autoRun)
{
TimerManager.SetTimer(this);
}
}
/// <summary>
/// 开始运行
/// </summary>
public void Start()
{
TimerManager.SetTimer(this);
}
/// <summary>
/// 停止运行
/// </summary>
public void Stop()
{
Killed = true;
}
public int CompareTo(object obj)
{
return CompareTo(((Timer)obj));
}
protected int CompareTo(Timer other)
{
return fTimer.CompareTo(other.fTimer);
}
/// <summary>
/// 定时器执行一次
/// </summary>
internal void Run()
{
if (Killed)
{
return;
}
_timer?.Invoke(this);
if (_maxRunCount > 0)
{
_currentRunCount++;
Killed = _currentRunCount >= _maxRunCount;
}
}
}
/// <summary>
/// 最小二叉堆,T必须继承IComparable
/// </summary>
/// <typeparam name="T"></typeparam>
public class BinaryHeapMin<T>
{
private readonly List<T> list = new List<T>();
public int Size => list.Count;
/// <summary>
/// 入堆
/// </summary>
/// <param name="obj"></param>
public void Enqueue(T obj)
{
list.Add(obj);
int i = list.Count;
while (i > 1 && Comparer.Default.Compare(list[i / 2 - 1], obj) > 0)
{
list[i - 1] = list[i / 2 - 1];
i /= 2;
}
list[i - 1] = obj;
}
/// <summary>
/// 取最小值,堆空的时候返回空
/// </summary>
/// <returns></returns>
public T FindMin()
{
if (list.Count == 0)
{
return default;
}
return list[0];
}
/// <summary>
/// 出堆
/// </summary>
/// <returns></returns>
public T DequeueMin()
{
int nCount = list.Count;
if (nCount == 0)
{
return default;
}
T tmpObj = FindMin();
int i = 1;
while ((2 * i + 1) <= nCount)
{
if (Comparer.Default.Compare(list[2 * i - 1], list[2 * i]) <= 0)
{
list[i - 1] = list[2 * i - 1];
list[2 * i - 1] = tmpObj;
i = 2 * i;
}
else
{
list[i - 1] = list[2 * i];
list[2 * i] = tmpObj;
i = 2 * i + 1;
}
}
T delObj = list[i - 1];
if (i != nCount)
{
//如果搜索到的对象就是数组的最后一个对象,则什么都不要做
list[i - 1] = list[nCount - 1];
}
//将最后一个对象删除
list.RemoveAt(nCount - 1);
return delObj;
}
public bool Contains(T obj)
{
return list.Contains(obj);
}
}
}
2025.3.24
修改命名,CheckTimer中判断IsCanceled属性,尽早发现已取消的计时器并移除(可能是外部已取消)。
using System;
using System.Collections;
using System.Collections.Generic;
namespace MYTOOL
{
public class TimerManager : MonoSingletonTemplate<TimerManager>
{
// 单例模式
private readonly BinaryHeapMin<Timer> timers = new BinaryHeapMin<Timer>();
// 最小二叉堆
private static float fRealTime = 0;
// set start time
private System.DateTime startTime;
protected override void OnInit()
{
startTime = System.DateTime.Now;
}
// 添加定时器
internal static void SetTimer(Timer tr)
{
GetInstance().SetTimer(tr, fRealTime);
}
// 添加定时器
private void SetTimer(Timer tr, float fTime)
{
#if UNITY_EDITOR
if (tr == null || tr.IsCanceled)
{
UnityEngine.Debug.LogWarning("无法添加计时器到最小堆!");
return;
}
if (timers.Contains(tr))
{
UnityEngine.Debug.LogWarning("重复添加已存在的计时器到最小堆!");
return;
}
#endif
tr.SetEndTime(fTime);
timers.Enqueue(tr);
}
private void CheckTimer(float fTime)
{
// 更新时间线
fRealTime = fTime;
int nLen = 0;
while (true)
{
if (nLen >= timers.Count)
break;
nLen++;
// 最近的计时器
Timer tr = timers.FindMin();
if (tr == null)
{
timers.DequeueMin();
break;
}
// 计时器已取消
if (tr.IsCanceled)
{
timers.DequeueMin();
continue;
}
if (fTime < tr.EndTime)
{
break;
}
tr.TriggerCallback();
if (tr.IsCanceled)
{
timers.DequeueMin();
}
else
{
tr.AppendIntervalTime();
timers.Enqueue(timers.DequeueMin());
}
}
}
private void Update()
{
//CheckTimer(UnityEngine.Time.realtimeSinceStartup);
CheckTimer((float)(DateTime.Now - startTime).TotalSeconds);
}
}
public class Timer : IComparable
{
public delegate void OnTimer(Timer sender);
private readonly float _interval;
private readonly OnTimer _onTimerCallback;
private readonly short _maxExecutionCount;
private short _currentExecutionCount;
private float _endTime;
public float EndTime => _endTime;
private bool _isCanceled;
public bool IsCanceled => _isCanceled;
/// <summary>
/// 轻量化计时器
/// </summary>
/// <param name="interval">执行间隔(秒)</param>
/// <param name="callback">回调函数</param>
/// <param name="autoRun">是否自动启动</param>
/// <param name="maxExecutionCount">最大执行次数(0和负数表示无限)</param>
public Timer(float interval, OnTimer callback, bool autoRun, short maxExecutionCount = -1)
{
_interval = interval;
_onTimerCallback = callback;
_maxExecutionCount = maxExecutionCount;
_currentExecutionCount = 0;
if (autoRun)
TimerManager.SetTimer(this);
}
/// <summary>
/// 启动计时器
/// </summary>
public void Start()
{
TimerManager.SetTimer(this);
}
/// <summary>
/// 取消计时器
/// </summary>
public void Cancel()
{
_isCanceled = true;
}
public int CompareTo(object obj)
{
return CompareTo(((Timer)obj));
}
protected int CompareTo(Timer other)
{
return _endTime.CompareTo(other._endTime);
}
internal void SetEndTime(float startTime)
{
_endTime = startTime + _interval;
}
internal void AppendIntervalTime()
{
_endTime += _interval;
}
/// <summary>
/// 计时器执行一次
/// </summary>
internal void TriggerCallback()
{
if (_isCanceled)
{
return;
}
if (_maxExecutionCount > 0)
{
_currentExecutionCount++;
_isCanceled = _currentExecutionCount >= _maxExecutionCount;
}
_onTimerCallback?.Invoke(this);
}
}
/// <summary>
/// 最小二叉堆,T必须继承IComparable
/// </summary>
/// <typeparam name="T"></typeparam>
public class BinaryHeapMin<T>
{
private readonly List<T> list = new List<T>();
public int Count => list.Count;
/// <summary>
/// 入堆
/// </summary>
/// <param name="obj"></param>
public void Enqueue(T obj)
{
list.Add(obj);
int i = list.Count;
while (i > 1 && Comparer.Default.Compare(list[i / 2 - 1], obj) > 0)
{
list[i - 1] = list[i / 2 - 1];
i /= 2;
}
list[i - 1] = obj;
}
/// <summary>
/// 取最小值,堆空的时候返回空
/// </summary>
/// <returns></returns>
public T FindMin()
{
if (list.Count == 0)
{
return default;
}
return list[0];
}
/// <summary>
/// 出堆
/// </summary>
/// <returns></returns>
public T DequeueMin()
{
int nCount = list.Count;
if (nCount == 0)
{
return default;
}
T tmpObj = FindMin();
int i = 1;
while ((2 * i + 1) <= nCount)
{
if (Comparer.Default.Compare(list[2 * i - 1], list[2 * i]) <= 0)
{
list[i - 1] = list[2 * i - 1];
list[2 * i - 1] = tmpObj;
i = 2 * i;
}
else
{
list[i - 1] = list[2 * i];
list[2 * i] = tmpObj;
i = 2 * i + 1;
}
}
T delObj = list[i - 1];
if (i != nCount)
{
//如果搜索到的对象就是数组的最后一个对象,则什么都不要做
list[i - 1] = list[nCount - 1];
}
//将最后一个对象删除
list.RemoveAt(nCount - 1);
return delObj;
}
public bool Contains(T obj)
{
return list.Contains(obj);
}
}
}
2025.4.2
修改nLen计数问题
using System;
using System.Collections;
using System.Collections.Generic;
namespace MYTOOL
{
public class TimerManager : MonoSingletonTemplate<TimerManager>
{
// 单例模式
private readonly BinaryHeapMin<Timer> timers = new BinaryHeapMin<Timer>();
// 最小二叉堆
private static float fRealTime = 0;
// set start time
private System.DateTime startTime;
protected override void OnInit()
{
startTime = System.DateTime.Now;
}
// 添加定时器
internal static void SetTimer(Timer tr)
{
GetInstance().SetTimer(tr, fRealTime);
}
// 添加定时器
private void SetTimer(Timer tr, float fTime)
{
#if UNITY_EDITOR
if (tr == null || tr.IsCanceled)
{
UnityEngine.Debug.LogWarning("无法添加计时器到最小堆!");
return;
}
if (timers.Contains(tr))
{
UnityEngine.Debug.LogWarning("重复添加已存在的计时器到最小堆!");
return;
}
#endif
tr.SetEndTime(fTime);
timers.Enqueue(tr);
}
private void CheckTimer(float fTime)
{
// 更新时间线
fRealTime = fTime;
int nLen = 0;
while (true)
{
if (nLen >= timers.Count)
break;
// 最近的计时器
Timer tr = timers.FindMin();
if (tr == null)
{
timers.DequeueMin();
break;
}
// 计时器已取消
if (tr.IsCanceled)
{
timers.DequeueMin();
continue;
}
if (fTime < tr.EndTime)
{
break;
}
tr.TriggerCallback();
if (tr.IsCanceled)
{
timers.DequeueMin();
}
else
{
tr.AppendIntervalTime();
timers.Enqueue(timers.DequeueMin());
nLen++;
}
}
}
private void Update()
{
//CheckTimer(UnityEngine.Time.realtimeSinceStartup);
CheckTimer((float)(DateTime.Now - startTime).TotalSeconds);
}
}
public class Timer : IComparable
{
public delegate void OnTimer(Timer sender);
private readonly float _interval;
private readonly OnTimer _onTimerCallback;
private readonly short _maxExecutionCount;
private short _currentExecutionCount;
private float _endTime;
public float EndTime => _endTime;
private bool _isCanceled;
public bool IsCanceled => _isCanceled;
/// <summary>
/// 轻量化计时器
/// </summary>
/// <param name="interval">执行间隔(秒)</param>
/// <param name="callback">回调函数</param>
/// <param name="autoRun">是否自动启动</param>
/// <param name="maxExecutionCount">最大执行次数(0和负数表示无限)</param>
public Timer(float interval, OnTimer callback, bool autoRun, short maxExecutionCount = -1)
{
_interval = interval;
_onTimerCallback = callback;
_maxExecutionCount = maxExecutionCount;
_currentExecutionCount = 0;
if (autoRun)
{
TimerManager.SetTimer(this);
}
}
/// <summary>
/// 启动计时器
/// </summary>
public void Start()
{
TimerManager.SetTimer(this);
}
/// <summary>
/// 取消计时器
/// </summary>
public void Cancel()
{
_isCanceled = true;
}
public int CompareTo(object obj)
{
return CompareTo(((Timer)obj));
}
protected int CompareTo(Timer other)
{
return _endTime.CompareTo(other._endTime);
}
internal void SetEndTime(float startTime)
{
_endTime = startTime + _interval;
}
internal void AppendIntervalTime()
{
_endTime += _interval;
}
/// <summary>
/// 计时器执行一次
/// </summary>
internal void TriggerCallback()
{
if (_isCanceled)
{
return;
}
if (_maxExecutionCount > 0)
{
_currentExecutionCount++;
_isCanceled = _currentExecutionCount >= _maxExecutionCount;
}
_onTimerCallback?.Invoke(this);
}
}
/// <summary>
/// 最小二叉堆,T必须继承IComparable
/// </summary>
/// <typeparam name="T"></typeparam>
public class BinaryHeapMin<T>
{
private readonly List<T> list = new List<T>();
public int Count => list.Count;
/// <summary>
/// 入堆
/// </summary>
/// <param name="obj"></param>
public void Enqueue(T obj)
{
list.Add(obj);
int i = list.Count;
while (i > 1 && Comparer.Default.Compare(list[i / 2 - 1], obj) > 0)
{
list[i - 1] = list[i / 2 - 1];
i /= 2;
}
list[i - 1] = obj;
}
/// <summary>
/// 取最小值,堆空的时候返回空
/// </summary>
/// <returns></returns>
public T FindMin()
{
if (list.Count == 0)
{
return default;
}
return list[0];
}
/// <summary>
/// 出堆
/// </summary>
/// <returns></returns>
public T DequeueMin()
{
int nCount = list.Count;
if (nCount == 0)
{
return default;
}
T tmpObj = FindMin();
int i = 1;
while ((2 * i + 1) <= nCount)
{
if (Comparer.Default.Compare(list[2 * i - 1], list[2 * i]) <= 0)
{
list[i - 1] = list[2 * i - 1];
list[2 * i - 1] = tmpObj;
i = 2 * i;
}
else
{
list[i - 1] = list[2 * i];
list[2 * i] = tmpObj;
i = 2 * i + 1;
}
}
T delObj = list[i - 1];
if (i != nCount)
{
//如果搜索到的对象就是数组的最后一个对象,则什么都不要做
list[i - 1] = list[nCount - 1];
}
//将最后一个对象删除
list.RemoveAt(nCount - 1);
return delObj;
}
public bool Contains(T obj)
{
return list.Contains(obj);
}
}
}