[Unity] 封装一个依赖于MonoBehaviour的计时器(上)
灵感来自下面这本书的协程部分,因此我就自己尝试写了一个
我的新书Unity3D游戏开发(第3版) | 雨松MOMO程序研究院
如果你不知道什么是协程:unity保姆级教程之协同程序_unity协同-CSDN博客
一句话概括:协程就是单线程的异步操作,其作用于Unity的主线程
1.我写了如下几个功能(只展示无参数):
基础校验
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;
}
1.等待受缩放时间影响后的秒数
public void WaitTime(float waitTime, Action callback)
{
if (CheckTime(waitTime))
{
StartCoroutine(WaitTimeHandle(waitTime, () => callback?.Invoke()));
}
}
2.等待不受缩放时间影响后的秒数
public void WaitRealTime(float waitTime, Action callback)
{
if (CheckTime(waitTime))
{
StartCoroutine(WaitRealTimeHandle(waitTime, () => callback?.Invoke()));
}
}
2.按固定时间间隔循环执行
public void LoopTime(float spacing, int overNumber, Action callback)
{
if (CheckTime(spacing) && CheckCount(overNumber))
{
StartCoroutine(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke()));
}
}
3.等待固定帧执行一次
public void WaitFrame(int frameCount, Action callback)
{
if (CheckCount(frameCount))
{
StartCoroutine(WaitFrameHandle(frameCount, () => callback?.Invoke()));
}
}
4.进度反馈
public void WaitTimeWithProgress(float waitTime, Action<float> progressCallback, Action completeCallback)
{
if (CheckTime(waitTime))
{
StartCoroutine(ProgressTimer(waitTime, progressCallback, completeCallback));
}
}
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();
}
5.等待当前帧渲染结束执行回调(本帧执行)
public void WaitForEndOfFrame(Action callback)
{
StartCoroutine(WaitForEndOfFrameHandle(callback));
}
private IEnumerator WaitForEndOfFrameHandle(Action callback)
{
yield return new WaitForEndOfFrame();
callback?.Invoke();
}
2.总览
回调函数我写了无参 一参 两参的版本 可以自行添加参数
using System;
using System.Collections;
using UnityEngine;
public class TimeManager : MonoBehaviour
{
private static TimeManager instance;
public static TimeManager Instance => instance;
private void Awake()
{
if (instance == null)
{
instance = this;
}
}
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;
}
#region 等待固定时间秒
/// <summary>
/// 等待固定时间以后执行回调
/// </summary>
/// <param name="waitTime">等待时间(秒)</param>
/// <param name="callback">回调函数</param>
public void WaitTime(float waitTime, Action callback)
{
if (CheckTime(waitTime))
{
StartCoroutine(WaitTimeHandle(waitTime, () => callback?.Invoke()));
}
}
public void WaitTime<T>(float waitTime, T param, Action<T> callback)
{
if (CheckTime(waitTime))
{
StartCoroutine(WaitTimeHandle(waitTime, () => callback?.Invoke(param)));
}
}
public void WaitTime<T, K>(float waitTime, T param1, K param2, Action<T, K> callback)
{
if (CheckTime(waitTime))
{
StartCoroutine(WaitTimeHandle(waitTime, () => callback?.Invoke(param1, param2)));
}
}
private IEnumerator WaitTimeHandle(float waitTime, Action action)
{
yield return new WaitForSeconds(waitTime);
action?.Invoke();
}
#endregion
#region 等待固定时间秒(不受缩放影响)
/// <summary>
/// 等待固定时间以后执行回调(不受Time.timeScale影响)
/// </summary>
/// <param name="waitTime">等待时间(秒)</param>
/// <param name="callback">回调函数</param>
public void WaitRealTime(float waitTime, Action callback)
{
if (CheckTime(waitTime))
{
StartCoroutine(WaitRealTimeHandle(waitTime, () => callback?.Invoke()));
}
}
public void WaitRealTime<T>(float waitTime, T param, Action<T> callback)
{
if (CheckTime(waitTime))
{
StartCoroutine(WaitRealTimeHandle(waitTime, () => callback?.Invoke(param)));
}
}
public void WaitRealTime<T, K>(float waitTime, T param1, K param2, Action<T, K> callback)
{
if (CheckTime(waitTime))
{
StartCoroutine(WaitRealTimeHandle(waitTime, () => callback?.Invoke(param1, param2)));
}
}
private IEnumerator WaitRealTimeHandle(float waitTime, Action action)
{
yield return new WaitForSecondsRealtime(waitTime);
action?.Invoke();
}
#endregion
#region 按固定时间间隔循环执行
public void LoopTime(float spacing, int overNumber, Action callback)
{
if (CheckTime(spacing) && CheckCount(overNumber))
{
StartCoroutine(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke()));
}
}
public void LoopTime<T>(float spacing, int overNumber, T param, Action<T> callback)
{
if (CheckTime(spacing) && CheckCount(overNumber))
{
StartCoroutine(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke(param)));
}
}
public void LoopTime<T, K>(float spacing, int overNumber, T param1, K param2, Action<T, K> callback)
{
if (CheckTime(spacing) && CheckCount(overNumber))
{
StartCoroutine(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke(param1, param2)));
}
}
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 等待固定帧执行一次
public void WaitFrame(int frameCount, Action callback)
{
if (CheckCount(frameCount))
{
StartCoroutine(WaitFrameHandle(frameCount, () => callback?.Invoke()));
}
}
public void WaitFrame<T>(int frameCount, T param, Action<T> callback)
{
if (CheckCount(frameCount))
{
StartCoroutine(WaitFrameHandle(frameCount, () => callback?.Invoke(param)));
}
}
public void WaitFrame<T, K>(int frameCount, T param1, K param2, Action<T, K> callback)
{
if (CheckCount(frameCount))
{
StartCoroutine(WaitFrameHandle(frameCount, () => callback?.Invoke(param1, param2)));
}
}
private IEnumerator WaitFrameHandle(int frameCount, Action action)
{
for (int i = 0; i < frameCount; i++)
{
yield return null;
}
action?.Invoke();
}
#endregion
#region 进度反馈
public void WaitTimeWithProgress(float waitTime, Action<float> progressCallback, Action completeCallback)
{
if (CheckTime(waitTime))
{
StartCoroutine(ProgressTimer(waitTime, progressCallback, completeCallback));
}
}
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();
}
public void WaitTimeWithProgress<T>(float waitTime, T param, Action<float, T> progressCallback, Action<T> completeCallback)
{
if (CheckTime(waitTime))
{
StartCoroutine(ProgressTimer(waitTime, param, progressCallback, completeCallback));
}
}
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);
}
public void WaitTimeWithProgress<T, K>(float waitTime, T param1, K param2, Action<float, T, K> progressCallback, Action<T, K> completeCallback)
{
if (CheckTime(waitTime))
{
StartCoroutine(ProgressTimer(waitTime, param1, param2, progressCallback, completeCallback));
}
}
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 等待当前帧结束执行回调
public void WaitForEndOfFrame(Action callback)
{
StartCoroutine(WaitForEndOfFrameHandle(callback));
}
private IEnumerator WaitForEndOfFrameHandle(Action callback)
{
yield return new WaitForEndOfFrame();
callback?.Invoke();
}
public void WaitForEndOfFrame<T>(T param, Action<T> callback)
{
StartCoroutine(WaitForEndOfFrameHandle(param, callback));
}
private IEnumerator WaitForEndOfFrameHandle<T>(T param, Action<T> callback)
{
yield return new WaitForEndOfFrame();
callback?.Invoke(param);
}
public void WaitForEndOfFrame<T, K>(T param1, K param2, Action<T, K> callback)
{
StartCoroutine(WaitForEndOfFrameHandle(param1, param2, callback));
}
private IEnumerator WaitForEndOfFrameHandle<T, K>(T param1, K param2, Action<T, K> callback)
{
yield return new WaitForEndOfFrame();
callback?.Invoke(param1, param2);
}
#endregion
public void Stop(IEnumerator func)
{
StopCoroutine(func);
}
public void StopAll()
{
StopAllCoroutines();
}
public TimeChainContext StartChain()
{
return new TimeChainContext(this);
}
}
3.Ai给出了如下拓展思路
优先级高
链式调用扩展
物理时间步长同步
优先级中
调试可视化工具
网络同步时钟
优先级低
自动化测试框架集成
使用Mono管理类去管理该脚本从而摆脱Mono的控制
我目前的需求没有这么复杂,当作一个简单的计时器使用
其是依赖于Unity的这一点可以用await和async来替代,但是可能涉及到了多线程,我这块学了但是用的不多还需加强