Unity中实现Timer(2)
需求背景
上次做的timer,在实际开发中遇到了error,报错为:在遍历dictionary中,不能对collection进行更改。后面我尝试过使用lock字段锁住字典,但上网查询才知道lock常用于多线程中,所以在我的需求环境中是无法生效的。另外上一次是使用的UpdateRegister对timer进行更新的,此种调用方式不灵活。所以此次的需求就是对timer进行优化
这是上一次timer:https://www.cnblogs.com/JunJiang-Blog/p/16948170.html
设计思路
外部想要加入到更新中的timer列表中,就需要调用add和remove方法。上一次是通过add和remove直接对更新列表进行操作,错误原因也在这里:我无法保证在遍历中,不对更新列表进行修改。
- 在add方法中,新建一个容器waitAddTimers,将准备加入到更新列表中的timer存储起来,在每次遍历之前,将waitAddTimers中的元素复制到更新列表中去,然后清空waitAddTimers,这样就能保证在遍历中不会操作更新列表
- 在remove方法中,新建一个容器waitRemoveTimers,将准备从更新列表中移除的timer的flag记录下来,在每次遍历之前,将waitRemoveTimers的flag取出,并在更新列表中移除。
- timer更新可以使用自身的静态更新方法,而不必注册到UpdateRegister中。这样我的timer更新方法想放在哪里就放在哪里,更加灵活。
具体实现
Timer的实例方法
构造函数
public Timer(int _repeatTimes, float _repeatDeltaTime, Action _callback)
{
this.isLoop = false;
this.repeatTimes = _repeatTimes;
this.repeatDeltaTime = _repeatDeltaTime;
this.callback = _callback;
}
public Timer(bool _isLoop, float _repeatDeltaTime, Action _callback)
{
if (!_isLoop)
{
this.repeatTimes = 1;
}
this.isLoop = _isLoop;
this.repeatDeltaTime = _repeatDeltaTime;
this.callback = _callback;
}
public Timer(float _repeatDeltaTime, Action _callback)
{
this.isLoop = false;
this.repeatTimes = 1;
this.repeatDeltaTime = _repeatDeltaTime;
this.callback = _callback;
}
对外接口
/// <summary>
/// 开始计时器
/// </summary>
public void Start()
{
if (isPlaying)
{
Debug.LogWarning("timer is alreay play, so this action will not works!!!");
return;
}
isPlaying = true;
curTime = 0f;
preInvokeTime = 0f;
curRepeatTimes = 0;
flag = Timer.AddTimer(this);
}
/// <summary>
/// 停止计时器(如果只是想暂停可用Pause,Stop操作不可回复)
/// </summary>
public void Stop()
{
if (!isPlaying)
{
Debug.LogWarning("timer is not playing, so this action will not works!!!");
return;
}
isPlaying = false;
Timer.RemoveTimer(flag);
}
/// <summary>
/// 暂停
/// </summary>
public void Pause()
{
isPlaying = false;
}
/// <summary>
/// 继续
/// </summary>
public void Resume()
{
isPlaying = true;
}
public void Complete()
{
if (Timer.ContainsTimer(timerFlag))
{
Timer.RemoveTimer(timerFlag);
}
callback?.Invoke();
isPlaying = false;
}
/// <summary>
/// 注销
/// </summary>
public void Dispose()
{
callback = null;
}
更新
private void Update(float deltaTime)
{
if (!isPlaying)
{
return;
}
curTime += deltaTime;
if (curTime - preInvokeTime >= repeatDeltaTime)
{
callback?.Invoke();
preInvokeTime = curTime;
if (!isLoop)
{
++curRepeatTimes;
if (curRepeatTimes >= repeatTimes)
{
Stop();
}
}
}
}
Timer的静态方法
对外接口
/// <summary>
/// 判断是否包含此timer
/// </summary>
/// <param name="flag">timer唯一标识</param>
/// <returns></returns>
public static bool ContainsTimer(uint flag)
{
return allTimers.ContainsKey(flag) || waitAddTimers.ContainsKey(flag);
}
/// <summary>
/// 添加timer到更新列表中
/// </summary>
/// <param name="timer"></param>
/// <returns></returns>
public static uint AddTimer(Timer timer)
{
uint result = timerFlag;
if (!allTimers.ContainsKey(result))
{
waitAddTimers.Add(result, timer);
++timerFlag;
return result;
}
Debug.LogError(string.Format("this flag{0} alreay exsit in cache, please check!!!", result));
return 0;
}
/// <summary>
/// 移除timer
/// </summary>
/// <param name="targetFlag">timer唯一标识</param>
public static void RemoveTimer(uint targetFlag)
{
waitRemoveTimers.Add(targetFlag);
}
/// <summary>
/// 清理所有timer
/// </summary>
public static void ClearAllTimers()
{
lock (allTimers)
{
using (var e = allTimers.GetEnumerator())
{
while (e.MoveNext())
{
e.Current.Value.Stop();
}
}
}
allTimers.Clear();
}
更新
/// <summary>
/// 更新
/// </summary>
/// <param name="deltaTime">每次更新间隔时长</param>
public static void Tick(float deltaTime)
{
if (waitAddTimers.Count > 0)
{
ClearWaitAddTimers();
}
if (waitRemoveTimers.Count > 0)
{
ClearWaitRemoveTimers();
}
using (var e = allTimers.GetEnumerator())
{
while (e.MoveNext())
{
e.Current.Value.Update(deltaTime);
}
}
}
对内接口
/// <summary>
/// 清理等待添加的timer队列
/// </summary>
private static void ClearWaitAddTimers()
{
using (var e = waitAddTimers.GetEnumerator())
{
while (e.MoveNext())
{
allTimers.Add(e.Current.Key, e.Current.Value);
}
}
waitAddTimers.Clear();
}
/// <summary>
/// 清理等待移除的timer队列
/// </summary>
private static void ClearWaitRemoveTimers()
{
foreach (var flag in waitRemoveTimers)
{
if (allTimers.ContainsKey(flag))
{
allTimers.Remove(flag);
}
}
waitRemoveTimers.Clear();
}
Timer代码
点击查看代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
namespace JunFramework
{
public class Timer:IDisposable
{
private uint flag;
private float curTime;
private float preInvokeTime;
private bool isLoop;
private int repeatTimes;
private int curRepeatTimes;
private float repeatDeltaTime;
private Action callback;
private bool isPlaying;
public bool IsPlaying
{
get => isPlaying;
}
public Timer(int _repeatTimes, float _repeatDeltaTime, Action _callback)
{
this.isLoop = false;
this.repeatTimes = _repeatTimes;
this.repeatDeltaTime = _repeatDeltaTime;
this.callback = _callback;
}
public Timer(bool _isLoop, float _repeatDeltaTime, Action _callback)
{
if (!_isLoop)
{
this.repeatTimes = 1;
}
this.isLoop = _isLoop;
this.repeatDeltaTime = _repeatDeltaTime;
this.callback = _callback;
}
public Timer(float _repeatDeltaTime, Action _callback)
{
this.isLoop = false;
this.repeatTimes = 1;
this.repeatDeltaTime = _repeatDeltaTime;
this.callback = _callback;
}
private void Update(float deltaTime)
{
if (!isPlaying)
{
return;
}
curTime += deltaTime;
if (curTime - preInvokeTime >= repeatDeltaTime)
{
callback?.Invoke();
preInvokeTime = curTime;
if (!isLoop)
{
++curRepeatTimes;
if (curRepeatTimes >= repeatTimes)
{
Stop();
}
}
}
}
/// <summary>
/// 开始计时器
/// </summary>
public void Start()
{
if (isPlaying)
{
Debug.LogWarning("timer is alreay play, so this action will not works!!!");
return;
}
isPlaying = true;
curTime = 0f;
preInvokeTime = 0f;
curRepeatTimes = 0;
flag = Timer.AddTimer(this);
}
/// <summary>
/// 停止计时器(如果只是想暂停可用Pause,Stop操作不可回复)
/// </summary>
public void Stop()
{
if (!isPlaying)
{
Debug.LogWarning("timer is not playing, so this action will not works!!!");
return;
}
isPlaying = false;
Timer.RemoveTimer(flag);
}
/// <summary>
/// 暂停
/// </summary>
public void Pause()
{
isPlaying = false;
}
/// <summary>
/// 继续
/// </summary>
public void Resume()
{
isPlaying = true;
}
public void Complete()
{
if (Timer.ContainsTimer(timerFlag))
{
Timer.RemoveTimer(timerFlag);
}
callback?.Invoke();
isPlaying = false;
}
/// <summary>
/// 注销
/// </summary>
public void Dispose()
{
callback = null;
}
#region static
private static uint timerFlag = 0;
private static Dictionary<uint, Timer> allTimers = new Dictionary<uint, Timer>();
private static Dictionary<uint, Timer> waitAddTimers = new Dictionary<uint, Timer>();
private static List<uint> waitRemoveTimers = new List<uint>();
/// <summary>
/// 判断是否包含此timer
/// </summary>
/// <param name="flag">timer唯一标识</param>
/// <returns></returns>
public static bool ContainsTimer(uint flag)
{
return allTimers.ContainsKey(flag) || waitAddTimers.ContainsKey(flag);
}
/// <summary>
/// 添加timer到更新列表中
/// </summary>
/// <param name="timer"></param>
/// <returns></returns>
public static uint AddTimer(Timer timer)
{
uint result = timerFlag;
if (!allTimers.ContainsKey(result))
{
waitAddTimers.Add(result, timer);
++timerFlag;
return result;
}
Debug.LogError(string.Format("this flag{0} alreay exsit in cache, please check!!!", result));
return 0;
}
/// <summary>
/// 移除timer
/// </summary>
/// <param name="targetFlag">timer唯一标识</param>
public static void RemoveTimer(uint targetFlag)
{
waitRemoveTimers.Add(targetFlag);
}
/// <summary>
/// 更新
/// </summary>
/// <param name="deltaTime">每次更新间隔时长</param>
public static void Tick(float deltaTime)
{
if (waitAddTimers.Count > 0)
{
ClearWaitAddTimers();
}
if (waitRemoveTimers.Count > 0)
{
ClearWaitRemoveTimers();
}
using (var e = allTimers.GetEnumerator())
{
while (e.MoveNext())
{
e.Current.Value.Update(deltaTime);
}
}
}
/// <summary>
/// 清理所有timer
/// </summary>
public static void ClearAllTimers()
{
lock (allTimers)
{
using (var e = allTimers.GetEnumerator())
{
while (e.MoveNext())
{
e.Current.Value.Stop();
}
}
}
allTimers.Clear();
}
/// <summary>
/// 清理等待添加的timer队列
/// </summary>
private static void ClearWaitAddTimers()
{
using (var e = waitAddTimers.GetEnumerator())
{
while (e.MoveNext())
{
allTimers.Add(e.Current.Key, e.Current.Value);
}
}
waitAddTimers.Clear();
}
/// <summary>
/// 清理等待移除的timer队列
/// </summary>
private static void ClearWaitRemoveTimers()
{
foreach (var flag in waitRemoveTimers)
{
if (allTimers.ContainsKey(flag))
{
allTimers.Remove(flag);
}
}
waitRemoveTimers.Clear();
}
#endregion
}
}
应用场景
回调队列的实现
https://www.cnblogs.com/JunJiang-Blog/p/17207901.html
延迟调用方法
总结
上一次的Timer居然有bug,这是我没想到的。原因时忽略了collection在遍历途中不能修改的性质。这一次的Timer目前没遇到问题,还在不断完善中,欢迎指正
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!