Unity实现一个通用的计时器

一、前言

        在游戏开发当中,经常会遇到一些计时的功能,例如角色技能CD、Buff持续时间、死亡时间以及生命值恢复时间等等一系列需求,在正常情况下自己去手写一个计时逻辑,这样并不方便使用,并且可复用性很差。

       于是,为了方便我日常中的开发,自己写了个简单的计时器功能,完全能够应付日常中一些简单的计时需求。

二、代码实现

首先,我们需要一个计时器状态枚举来实现计时器各个状态的声明和切换:

    /// <summary>
    /// 计时器状态枚举
    /// </summary>
    public enum TimerState
    {
        NODE,
        READY,
        WORKING,
        DONE
    }

接下来,我们实现一个简单的计时器类,用于执行定时任务:

    /// <summary>
    /// 计时器类
    /// </summary>
    public class Timer
    {
        private float _startTime;
        private float _endTime;
        private bool _stop;
        private Action _action;
        private TimerState _state;

        public Timer()
        {
            ResetTimer();
        }

        /// <summary>
        /// 重置计时器
        /// </summary>
        public void ResetTimer()
        {
            _startTime = 0f;
            _endTime = 0f;
            _stop = true;
            _action = null;
            _state = TimerState.NODE;
        }

        /// <summary>
        /// 唤醒计时器
        /// </summary>
        /// <param name="time"></param>
        /// <param name="task"></param>
        public void Arouse(float time, Action task)
        {
            _endTime = time;
            _action = task;
            _stop = false;
            _state = TimerState.READY;
        }

        /// <summary>
        /// 计时器工作中
        /// </summary>
        public void Workering()
        {
            if (_stop)
            {
                ResetTimer();
                return;
            }

            _startTime += Time.deltaTime;
            if (_state != TimerState.WORKING)
                _state = TimerState.WORKING;
            //计时完成
            if (_startTime >= _endTime)
            {
                _action?.Invoke();
                _state = TimerState.DONE;
                _stop = true;
            }
        }

        /// <summary>
        /// 获取计时器状态
        /// </summary>
        /// <returns></returns>
        public TimerState GetTimerState() => _state;
    }

最后,我们需要一个计时器管理器来负责创建、激活和管理计时器:

/// <summary>
    /// 计时器管理器
    /// </summary>
    public class TimerManager : NoMonoSingleton<TimerManager>
    {
        //激活的计时器列表
        private List<Timer> _activeTimers = new List<Timer>();
        //休眠的计时器集合
        private Queue<Timer> _dormancyTimers = new Queue<Timer>();

        /// <summary>
        /// 预加载计时器数量
        /// </summary>
        /// <param name="count"></param>
        public void PreLoadTimerCount(int count = 1)
        {
            if (count <= 0)
            {
                Debug.LogError("计时器预加载数量不能为小于等于0的数!");
                return;
            }

            for (int i = 0; i < count; i++)
            {
                _dormancyTimers.Enqueue(CreateTimer());
            }
        }

        /// <summary>
        /// 请求获取一个计时器
        /// </summary>
        /// <param name="time"></param>
        /// <param name="task"></param>
        public void GetOneTimer(float time, Action task)
        {
            if (_dormancyTimers.Count == 0)
            {
                _dormancyTimers.Enqueue(CreateTimer());
            }

            var timer = _dormancyTimers.Dequeue();
            if (timer.GetTimerState() != TimerState.NODE) return;

            timer.Arouse(time, task);
            _activeTimers.Add(timer);
            //在Update中执行更新计时器的方法
            MonoManager.Instance.AddUpdateListener(UpdateTimer);
        }

        /// <summary>
        /// 更新计时器
        /// </summary>
        private void UpdateTimer()
        {
            if (_activeTimers.Count == 0) return;

            for (int i = 0; i < _activeTimers.Count; i++)
            {
                if (_activeTimers[i].GetTimerState() == TimerState.DONE)
                {
                    _activeTimers[i].ResetTimer();
                    _dormancyTimers.Enqueue(_activeTimers[i]);
                    _activeTimers.Remove(_activeTimers[i]);
                }
                else
                {
                    _activeTimers[i].Workering();
                }
            }
        }

        /// <summary>
        /// 创建一个计时器
        /// </summary>
        /// <returns></returns>
        private Timer CreateTimer()
        {
            return new Timer();
        }
    }

在获取计时器之前,我们先调用PreLoadTimerCount方法进行计时器预加载来节省性能消耗。

完整代码如下:

using Mr.Le.Utility.Singleton;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Mr.Le.Utility.Manager
{
    /// <summary>
    /// 计时器管理器
    /// </summary>
    public class TimerManager : NoMonoSingleton<TimerManager>
    {
        //激活的计时器列表
        private List<Timer> _activeTimers = new List<Timer>();
        //休眠的计时器集合
        private Queue<Timer> _dormancyTimers = new Queue<Timer>();

        /// <summary>
        /// 预加载计时器数量
        /// </summary>
        /// <param name="count"></param>
        public void PreLoadTimerCount(int count = 1)
        {
            if (count <= 0)
            {
                Debug.LogError("计时器预加载数量不能为小于等于0的数!");
                return;
            }

            for (int i = 0; i < count; i++)
            {
                _dormancyTimers.Enqueue(CreateTimer());
            }
        }

        /// <summary>
        /// 请求获取一个计时器
        /// </summary>
        /// <param name="time"></param>
        /// <param name="task"></param>
        public void GetOneTimer(float time, Action task)
        {
            if (_dormancyTimers.Count == 0)
            {
                _dormancyTimers.Enqueue(CreateTimer());
            }

            var timer = _dormancyTimers.Dequeue();
            if (timer.GetTimerState() != TimerState.NODE) return;

            timer.Arouse(time, task);
            _activeTimers.Add(timer);
            //在Update中执行更新计时器的方法
            MonoManager.Instance.AddUpdateListener(UpdateTimer);
        }

        /// <summary>
        /// 更新计时器
        /// </summary>
        private void UpdateTimer()
        {
            if (_activeTimers.Count == 0) return;

            for (int i = 0; i < _activeTimers.Count; i++)
            {
                if (_activeTimers[i].GetTimerState() == TimerState.DONE)
                {
                    _activeTimers[i].ResetTimer();
                    _dormancyTimers.Enqueue(_activeTimers[i]);
                    _activeTimers.Remove(_activeTimers[i]);
                }
                else
                {
                    _activeTimers[i].Workering();
                }
            }
        }

        /// <summary>
        /// 创建一个计时器
        /// </summary>
        /// <returns></returns>
        private Timer CreateTimer()
        {
            return new Timer();
        }
    }

    /// <summary>
    /// 计时器类
    /// </summary>
    public class Timer
    {
        private float _startTime;
        private float _endTime;
        private bool _stop;
        private Action _action;
        private TimerState _state;

        public Timer()
        {
            ResetTimer();
        }

        /// <summary>
        /// 重置计时器
        /// </summary>
        public void ResetTimer()
        {
            _startTime = 0f;
            _endTime = 0f;
            _stop = true;
            _action = null;
            _state = TimerState.NODE;
        }

        /// <summary>
        /// 唤醒计时器
        /// </summary>
        /// <param name="time"></param>
        /// <param name="task"></param>
        public void Arouse(float time, Action task)
        {
            _endTime = time;
            _action = task;
            _stop = false;
            _state = TimerState.READY;
        }

        /// <summary>
        /// 计时器工作中
        /// </summary>
        public void Workering()
        {
            if (_stop)
            {
                ResetTimer();
                return;
            }

            _startTime += Time.deltaTime;
            if (_state != TimerState.WORKING)
                _state = TimerState.WORKING;
            //计时完成
            if (_startTime >= _endTime)
            {
                _action?.Invoke();
                _state = TimerState.DONE;
                _stop = true;
            }
        }

        /// <summary>
        /// 获取计时器状态
        /// </summary>
        /// <returns></returns>
        public TimerState GetTimerState() => _state;
    }

    /// <summary>
    /// 计时器状态枚举
    /// </summary>
    public enum TimerState
    {
        NODE,
        READY,
        WORKING,
        DONE
    }
}

测试代码:

public class Player : MonoBehaviour
{
    private void Awake()
    {
        TimerManager.Instance.PreLoadTimerCount(10);
    }

    private void Start()
    {
        TimerManager.Instance.GetOneTimer(5f, TestTimer);
    }

    private void TestTimer()
    {
        Debug.Log("释放技能A");
    }
}

三、总结

        以上就是一个简单的通用计时器的实现方法,我只写了在Update方法中执行计时的方式,当然还可以在协程中去执行,也可以增加一些更精细的功能,例如手动调整计时的速率、暂停计时、继续计时等都可以自己手动拓展。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值