一、前言
在游戏开发当中,经常会遇到一些计时的功能,例如角色技能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方法中执行计时的方式,当然还可以在协程中去执行,也可以增加一些更精细的功能,例如手动调整计时的速率、暂停计时、继续计时等都可以自己手动拓展。