[Unity] 封装一个依赖于MonoBehaviour的计时器(上)-CSDN博客
目录
1.加入等待间隔时间"永远'执行方法
2.修改为支持链式调用
实现链式调用
管理"链式"调度顺序
3.测试
即时方法编辑
"永久"方法
链式调用
4.总结
1.加入等待间隔时间"永远'执行方法
我将这个计时器修改为了支持链式调用,不过我还是发现了一个问题,有些地方的计时器要求是这样子的
using UnityEngine;
public class RegularTimer : MonoBehaviour
{
private float timer = 0f;
public float targetTime = 5f;
private void Update()
{
timer += Time.deltaTime;
if (timer >= targetTime)
{
// 达到目标时间,执行相应操作
Debug.Log("Timer reached!");
timer = 0f;
}
}
}
但是我的协程计时器之中并没有随游戏"无限"进行的间隔计时器
所以添加了一个无限循环执行的函数
#region 无限循环执行
/// <summary>
/// 按固定时间间隔无限循环执行回调
/// </summary>
/// <param name="spacing">时间间隔(秒)</param>
/// <param name="callback">回调函数</param>
public void LoopForever(float spacing, Action callback)
{
if (CheckTime(spacing))
{
StartCoroutine(LoopForeverHandle(spacing, () => callback?.Invoke()));
}
}
public void LoopForever<T>(float spacing, T param, Action<T> callback)
{
if (CheckTime(spacing))
{
StartCoroutine(LoopForeverHandle(spacing, () => callback?.Invoke(param)));
}
}
public void LoopForever<T, K>(float spacing, T param1, K param2, Action<T, K> callback)
{
if (CheckTime(spacing))
{
StartCoroutine(LoopForeverHandle(spacing, () => callback?.Invoke(param1, param2)));
}
}
private IEnumerator LoopForeverHandle(float spacing, Action action)
{
while (true)
{
yield return new WaitForSeconds(spacing);
action?.Invoke();
}
}
#endregion
2.修改为支持链式调用
有两个要点:
实现链式调用
先别管怎么去写 看看怎么使用
TimeManager.Instance.BeginChain().WaitTime().WaitRealTime();
如果想要每一次"点"出后面的方法 要求是Instance一致
所以要是方法的返回值是本身不就行了
public TimeManager BeginChain()
{
enumeratorQ.Clear();//别管这一句
return this;
}
完美解决问题
管理"链式"调度顺序
因为链式调用是多个且有序的,所以一定要有个容器去存储,我画个图
所以有没有想起什么? 先进先出 队列不就是这样的吗?
en造数据结构与算法 c#语言 数组实现队列很难???看我一击破之!!!-CSDN博客
// 链式调用的队列
private Queue<IEnumerator> enumeratorQ = new Queue<IEnumerator>();
private bool isChaining; // 是否正在执行队列
然后就是自己解决如何去跑了 ,很简单,有调用就入队,跑起来就出队,全权由该队列管理即可
public TimeManager BeginChain() { enumeratorQ.Clear(); return this; } /// <summary> /// 开始执行链式调用 /// </summary> private void ExecuteChain() { if (!isChaining && enumeratorQ.Count > 0) { StartCoroutine(RunChain()); } } /// <summary> /// 出队跑 /// </summary> /// <returns></returns> private IEnumerator RunChain() { isChaining = true; while (enumeratorQ.Count > 0) { yield return StartCoroutine(enumeratorQ.Dequeue()); } isChaining = false; }
public TimeManager WaitTime(float waitTime, Action callback) { if (CheckTime(waitTime)) { enumeratorQ.Enqueue(WaitTimeHandle(waitTime, () => callback?.Invoke())); ExecuteChain(); } return this; }
3.测试
即时方法
"永久"方法
private void Start()
{
//1秒后执行
TimeManager.Instance.LoopForever(1,Callback);
}
private void Callback() {
Debug.Log("我是哈基咩");
}
链式调用
using System;
using UnityEngine;
public class Test : MonoBehaviour
{
private void Start()
{
// 链式调用测试
TimeManager.Instance.BeginChain()
// 1秒后执行 Callback
.WaitTime(1f, Callback)
// 等待120帧后执行 Callback1,并传递字符串参数
.WaitFrame<string>(120, "在120帧后执行", Callback1)
// 本帧结束时执行 Callback2,传递int和string参数
.WaitForEndOfFrame<int, string>(520, "在本帧调的最后执行", Callback2)
// 等待5秒,过程中反馈进度(Progress),完成后执行 Callback
.WaitTimeWithProgress(5f, Progress, Callback);
}
private void Callback()
{
Debug.Log("我是哈基咩");
}
private void Callback1(string param1)
{
Debug.Log($"帧:{Time.frameCount},我是哈基咩: {param1}");
}
private void Callback2(int param1, string param2)
{
Debug.Log($"帧:{Time.frameCount},我是哈基咩: {param1} 我是咩咩 {param2}");
}
private void Progress(float param1)
{
Debug.Log($"我是哈基咩: {param1}");
}
}
4.总结
这是一次很好的体验 我从来没写过链式调用的代码 原来是这么一回事
同时也做好了一个计时器以后就不用在每一个需要的地方进行update里面手搓辣
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TimeManager : MonoBehaviour
{
private static TimeManager instance;
public static TimeManager Instance => instance;
// 链式调用的队列
private Queue<IEnumerator> enumeratorQ = new Queue<IEnumerator>();
private bool isChaining; // 是否正在执行队列
private void Awake()
{
if (instance == null)
{
instance = this;
}
}
public TimeManager BeginChain()
{
enumeratorQ.Clear();
return this;
}
/// <summary>
/// 开始执行链式调用
/// </summary>
private void ExecuteChain()
{
if (!isChaining && enumeratorQ.Count > 0)
{
StartCoroutine(RunChain());
}
}
/// <summary>
/// 出队跑
/// </summary>
/// <returns></returns>
private IEnumerator RunChain()
{
isChaining = true;
while (enumeratorQ.Count > 0)
{
yield return StartCoroutine(enumeratorQ.Dequeue());
}
isChaining = false;
}
#region 检查合法
private bool CheckCount(int count)
{
if (count < 0)
{
Debug.LogError("循环次数不能为负数!");
return false;
}
return true;
}
private bool CheckTime(float time)
{
if (time < 0)
{
Debug.LogError("等待时间不能为负数!");
return false;
}
return true;
}
#endregion
#region 等待固定时间秒
public TimeManager WaitTime(float waitTime, Action callback)
{
if (CheckTime(waitTime))
{
enumeratorQ.Enqueue(WaitTimeHandle(waitTime, () => callback?.Invoke()));
ExecuteChain();
}
return this;
}
public TimeManager WaitTime<T>(float waitTime, T param, Action<T> callback)
{
if (CheckTime(waitTime))
{
enumeratorQ.Enqueue(WaitTimeHandle(waitTime, () => callback?.Invoke(param)));
ExecuteChain();
}
return this;
}
public TimeManager WaitTime<T, K>(float waitTime, T param1, K param2, Action<T, K> callback)
{
if (CheckTime(waitTime))
{
enumeratorQ.Enqueue(WaitTimeHandle(waitTime, () => callback?.Invoke(param1, param2)));
ExecuteChain();
}
return this;
}
private IEnumerator WaitTimeHandle(float waitTime, Action action)
{
yield return new WaitForSeconds(waitTime);
action?.Invoke();
}
#endregion
#region 等待固定时间秒(不受缩放影响)
public TimeManager WaitRealTime(float waitTime, Action callback)
{
if (CheckTime(waitTime))
{
enumeratorQ.Enqueue(WaitRealTimeHandle(waitTime, () => callback?.Invoke()));
ExecuteChain();
}
return this;
}
public TimeManager WaitRealTime<T>(float waitTime, T param, Action<T> callback)
{
if (CheckTime(waitTime))
{
enumeratorQ.Enqueue(WaitRealTimeHandle(waitTime, () => callback?.Invoke(param)));
ExecuteChain();
}
return this;
}
public TimeManager WaitRealTime<T, K>(float waitTime, T param1, K param2, Action<T, K> callback)
{
if (CheckTime(waitTime))
{
enumeratorQ.Enqueue(WaitRealTimeHandle(waitTime, () => callback?.Invoke(param1, param2)));
ExecuteChain();
}
return this;
}
private IEnumerator WaitRealTimeHandle(float waitTime, Action action)
{
yield return new WaitForSecondsRealtime(waitTime);
action?.Invoke();
}
#endregion
#region 按固定时间间隔循环执行
/// <summary>
/// 按固定时间间隔循环执行
/// </summary>
/// <param name="spacing"></param>
/// <param name="overNumber"></param>
/// <param name="callback"></param>
/// <returns></returns>
public TimeManager LoopTime(float spacing, int overNumber, Action callback)
{
if (CheckTime(spacing) && CheckCount(overNumber))
{
enumeratorQ.Enqueue(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke()));
ExecuteChain();
}
return this;
}
public TimeManager LoopTime<T>(float spacing, int overNumber, T param, Action<T> callback)
{
if (CheckTime(spacing) && CheckCount(overNumber))
{
enumeratorQ.Enqueue(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke(param)));
ExecuteChain();
}
return this;
}
public TimeManager LoopTime<T, K>(float spacing, int overNumber, T param1, K param2, Action<T, K> callback)
{
if (CheckTime(spacing) && CheckCount(overNumber))
{
enumeratorQ.Enqueue(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke(param1, param2)));
ExecuteChain();
}
return this;
}
private IEnumerator LoopTimeHandle(float spacing, int overNumber, Action action)
{
for (int i = 0; i < overNumber; i++)
{
yield return new WaitForSeconds(spacing);
action?.Invoke();
}
}
#endregion
#region 等待固定帧执行一次
/// <summary>
/// 等待固定帧执行一次
/// </summary>
/// <param name="frameCount"></param>
/// <param name="callback"></param>
/// <returns></returns>
public TimeManager WaitFrame(int frameCount, Action callback)
{
if (CheckCount(frameCount))
{
enumeratorQ.Enqueue(WaitFrameHandle(frameCount, () => callback?.Invoke()));
ExecuteChain();
}
return this;
}
public TimeManager WaitFrame<T>(int frameCount, T param, Action<T> callback)
{
if (CheckCount(frameCount))
{
enumeratorQ.Enqueue(WaitFrameHandle(frameCount, () => callback?.Invoke(param)));
ExecuteChain();
}
return this;
}
public TimeManager WaitFrame<T, K>(int frameCount, T param1, K param2, Action<T, K> callback)
{
if (CheckCount(frameCount))
{
enumeratorQ.Enqueue(WaitFrameHandle(frameCount, () => callback?.Invoke(param1, param2)));
ExecuteChain();
}
return this;
}
private IEnumerator WaitFrameHandle(int frameCount, Action action)
{
for (int i = 0; i < frameCount; i++)
{
yield return null;
}
action?.Invoke();
}
#endregion
#region 进度反馈
/// <summary>
/// 进度反馈
/// </summary>
/// <param name="waitTime"></param>
/// <param name="progressCallback"></param>
/// <param name="completeCallback"></param>
/// <returns></returns>
public TimeManager WaitTimeWithProgress(float waitTime, Action<float> progressCallback, Action completeCallback)
{
if (CheckTime(waitTime))
{
enumeratorQ.Enqueue(ProgressTimer(waitTime, progressCallback, completeCallback));
ExecuteChain();
}
return this;
}
public TimeManager WaitTimeWithProgress<T>(float waitTime, T param, Action<float, T> progressCallback, Action<T> completeCallback)
{
if (CheckTime(waitTime))
{
enumeratorQ.Enqueue(ProgressTimer(waitTime, param, progressCallback, completeCallback));
ExecuteChain();
}
return this;
}
public TimeManager WaitTimeWithProgress<T, K>(float waitTime, T param1, K param2, Action<float, T, K> progressCallback, Action<T, K> completeCallback)
{
if (CheckTime(waitTime))
{
enumeratorQ.Enqueue(ProgressTimer(waitTime, param1, param2, progressCallback, completeCallback));
ExecuteChain();
}
return this;
}
private IEnumerator ProgressTimer(float duration, Action<float> progress, Action complete)
{
float startTime = Time.time;
while (Time.time - startTime < duration)
{
progress?.Invoke((Time.time - startTime) / duration);
yield return null;
}
complete?.Invoke();
}
private IEnumerator ProgressTimer<T>(float duration, T param, Action<float, T> progress, Action<T> complete)
{
float startTime = Time.time;
while (Time.time - startTime < duration)
{
progress?.Invoke((Time.time - startTime) / duration, param);
yield return null;
}
complete?.Invoke(param);
}
private IEnumerator ProgressTimer<T, K>(float duration, T param1, K param2, Action<float, T, K> progress, Action<T, K> complete)
{
float startTime = Time.time;
while (Time.time - startTime < duration)
{
progress?.Invoke((Time.time - startTime) / duration, param1, param2);
yield return null;
}
complete?.Invoke(param1, param2);
}
#endregion
#region 等待当前帧结束执行回调
/// <summary>
/// 等待当前帧结束执行回调
/// </summary>
/// <param name="callback"></param>
/// <returns></returns>
public TimeManager WaitForEndOfFrame(Action callback)
{
enumeratorQ.Enqueue(WaitForEndOfFrameHandle(callback));
ExecuteChain();
return this;
}
public TimeManager WaitForEndOfFrame<T>(T param, Action<T> callback)
{
enumeratorQ.Enqueue(WaitForEndOfFrameHandle(param, callback));
ExecuteChain();
return this;
}
public TimeManager WaitForEndOfFrame<T, K>(T param1, K param2, Action<T, K> callback)
{
enumeratorQ.Enqueue(WaitForEndOfFrameHandle(param1, param2, callback));
ExecuteChain();
return this;
}
private IEnumerator WaitForEndOfFrameHandle(Action callback)
{
yield return new WaitForEndOfFrame();
callback?.Invoke();
}
private IEnumerator WaitForEndOfFrameHandle<T>(T param, Action<T> callback)
{
yield return new WaitForEndOfFrame();
callback?.Invoke(param);
}
private IEnumerator WaitForEndOfFrameHandle<T, K>(T param1, K param2, Action<T, K> callback)
{
yield return new WaitForEndOfFrame();
callback?.Invoke(param1, param2);
}
#endregion
#region 无限循环执行
/// <summary>
/// 放在链式的最后!
/// </summary>
/// <param name="spacing"></param>
/// <param name="callback"></param>
/// <returns></returns>
public TimeManager LoopForever(float spacing, Action callback)
{
if (CheckTime(spacing))
{
enumeratorQ.Enqueue(LoopForeverHandle(spacing, () => callback?.Invoke()));
ExecuteChain();
}
return this;
}
public TimeManager LoopForever<T>(float spacing, T param, Action<T> callback)
{
if (CheckTime(spacing))
{
enumeratorQ.Enqueue(LoopForeverHandle(spacing, () => callback?.Invoke(param)));
ExecuteChain();
}
return this;
}
public TimeManager LoopForever<T, K>(float spacing, T param1, K param2, Action<T, K> callback)
{
if (CheckTime(spacing))
{
enumeratorQ.Enqueue(LoopForeverHandle(spacing, () => callback?.Invoke(param1, param2)));
ExecuteChain();
}
return this;
}
private IEnumerator LoopForeverHandle(float spacing, Action action)
{
while (true)
{
yield return new WaitForSeconds(spacing);
action?.Invoke();
//如果前期链式调用的时候把它算进去 就会卡死掉
if (enumeratorQ.Count > 0)
yield break;
}
}
#endregion
public void Stop(IEnumerator func)
{
StopCoroutine(func);
//停止退队的时候要还一个新的回去 不然其仍在队列之中
if (enumeratorQ.Contains(func))
{
var tempQueue = new Queue<IEnumerator>();
while (enumeratorQ.Count > 0)
{
IEnumerator item = enumeratorQ.Dequeue();
if (item != func)
{
tempQueue.Enqueue(item);
}
}
enumeratorQ = tempQueue;
}
}
public void StopAll()
{
StopAllCoroutines();
enumeratorQ.Clear();
isChaining = false;
}
}