计时器是游戏开发的一个非常常用的功能,简单计时器的实现方式有很多中,可以每帧给一个float变量加Time.deltaTime,到一定时间的时候调用某个函数或者是执行某个任务。可以使用unity的协程和InvokeRepeating。但是简单的使用这些组合调用起来不是很方便,并且无法实现暂停,继续等功能,也无法方便的在计时器运行到某个时间点的时候移除或者添加新的事件。
一、使用方式
1.自行构造
步骤一:构造
Timer timer = new Timer(1000,3); // 计时器的构造以毫秒为单位,这是一个没秒触发一次,触发三次结束的计时器
步骤二:添加/移除事件
public void AddListener(Action fun)
public void RemoveListener(Action fun)
步骤三:操作
public void Start() // 开始
public void Pause() // 暂停
public void Stop() // 停止
暂停后继续计时器也调用Start();
2.静态函数
调用如下两个静态函数会直接开始计时,不用调用Start,实际项目中第一个静态函数运用比较多
public static Timer Register(float interval, Action action)
public static Timer Register(float interval, int repeatCount, Action action)
二、源码
using System.Collections.Generic;
using UnityEngine;
using Action = System.Action;
namespace XFramework
{
public class Timer
{
private static TimerManager s_Manager;
private static TimerManager Manager
{
get
{
if (Timer.s_Manager == null)
{
TimerManager managerInScene = Object.FindObjectOfType<TimerManager>();
if (managerInScene != null)
{
Timer.s_Manager = managerInScene;
}
else
{
GameObject managerObject = new GameObject { name = "TimerManager" };
Timer.s_Manager = managerObject.AddComponent<TimerManager>();
}
}
return s_Manager;
}
}
private Action timeUpDel;
/// <summary>
/// 是否执行
/// </summary>
private bool _isRunning;
/// <summary>
/// 已执行时间(每次满足运行间隔就会加这个)
/// </summary>
private float _useTime;
/// <summary>
/// 运行时间
/// </summary>
public float RunTime { get; private set; }
/// <summary>
/// 已运行次数
/// </summary>
public int UseCount { get; private set; }
/// <summary>
/// 运行间隔
/// </summary>
private readonly float interval;
/// <summary>
/// 设置的运行次数
/// </summary>
private readonly int repeatCount;
/// <summary>
/// <param name="interval">时间间隔,单位是毫秒</param>
/// <param name="repeatCount">运行次数,一秒一次的话MaxValue可以执行68年</param>
/// </summary>
public Timer(float interval, int repeatCount = 1)
{
RunTime = 0f;
if (interval <= 0)
interval = 0.01f;
this.interval = interval;
this.repeatCount = repeatCount;
}
/// <summary>
/// 是否运行中
/// </summary>
private bool IsRunning
{
get
{
return _isRunning;
}
set
{
if (_isRunning != value)
{
_isRunning = value;
if (_isRunning)
{
Manager.AddTimer(this);
}
else
{
Manager.RemoveTimer(this);
}
// TODO 这里可以加一个计时器状态变换的委托
}
}
}
/// <summary>
/// 每帧执行
/// </summary>
/// <param name="deltaTime"></param>
internal void Update(float deltaTime)
{
if (IsRunning && UseCount < repeatCount)
{
RunTime += deltaTime;
while (RunTime - _useTime > interval && UseCount < repeatCount)
{
UseCount++;
_useTime += interval;
timeUpDel?.Invoke();
}
}
if (UseCount >= repeatCount)
{
IsRunning = false;
}
}
/// <summary>
/// 添加事件
/// </summary>
/// <param name="type"></param>
/// <param name="fun"></param>
public void AddListener(Action fun)
{
timeUpDel += fun;
}
/// <summary>
/// 移除事件
/// </summary>
/// <param name="type"></param>
/// <param name="fun"></param>
public void RemoveListener(Action fun)
{
timeUpDel -= fun;
}
#region 生命周期
/// <summary>
/// 开始(调用了IsRunning的Set,初始化了TimerManager)
/// </summary>
public void Start()
{
IsRunning = true;
}
/// <summary>
/// 停止
/// </summary>
public void Pause()
{
IsRunning = false;
}
/// <summary>
/// 停止
/// </summary>
public void Stop()
{
IsRunning = false;
RunTime = 0f;
_useTime = 0f;
UseCount = 0;
}
#endregion
#region 外部调用的静态函数
/// <summary>
/// 注册一个延时执行的任务
/// </summary>
/// <param name="interval">间隔时间</param>
/// <param name="action">任务</param>
public static Timer Register(float interval, Action action)
{
return Register(interval, 1, action);
}
/// <summary>
/// 注册一个每隔一段时间执行一次的任务
/// </summary>
/// <param name="interval">时间间隔</param>
/// <param name="repeatCount">重复次数</param>
/// <param name="action">任务</param>
public static Timer Register(float interval, int repeatCount, Action action)
{
Timer timer = new Timer(interval, repeatCount);
timer.AddListener(action);
timer.Start();
return timer;
}
/// <summary>
/// 计时器的时间速度
/// </summary>
public static float TimeScale
{
get
{
return Manager.timeScale;
}
set
{
Manager.timeScale = value;
}
}
#endregion
/// <summary>
/// 计时器管理
/// 除了计时器以外其他类暂时不需要调用,以后需要再放到外面去
/// </summary>
private class TimerManager : MonoBehaviour
{
private readonly List<Timer> m_timers = new List<Timer>();
internal float timeScale = 1;
private void Update()
{
float deltaTime = Time.unscaledDeltaTime * timeScale;
for (var i = 0; i < m_timers.Count; i++)
{
if (m_timers[i].IsRunning)
{
// unscaledDeltaTime和deltaTime一样,但是不受TimeScale影响
m_timers[i].Update(deltaTime);
}
}
}
public void AddTimer(Timer timer)
{
if (m_timers.Contains(timer) == false)
{
m_timers.Add(timer);
}
}
public void RemoveTimer(Timer timer)
{
if (m_timers.Contains(timer))
{
m_timers.Remove(timer);
}
}
}
}
}