Unity 定时器

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);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值