Unity 封装一个依赖于MonoBehaviour的计时器(下) 链式调用

发布于:2025-03-14 ⋅ 阅读:(24) ⋅ 点赞:(0)

[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;
    }
}