Unity3D延迟回调的封装

  最近,整理项目框架逻辑时,无意中翻到n年前封装的延迟回调管理器,就拎出来说道说道:

  先说一下系统提供的几种常用的延迟回调方式:

1)Invoke(Invoke、CancelInvoke):首先需要继承自MonoBehaviour,其次使用的是函数名作为参数调用,比较麻烦,容易出错(运行时才会审查,编译检查不到);
2)Coroutine(StartCoroutine、StopCoroutine):仍然继承自MonoBehaviour,调用时可以使用函数名,也可以直接传入委托,效率上不高,每次调用都会牵扯到额外的堆内存分配,并且所挂载的游戏对象状态不能被打断,否则执行也会被打断;
3、Update:依赖MonoBehaviour,每次调用需要单独申请变量,重写逻辑,也会因状态打断而停止执行;
在不使用插件的情况下,索性进行一些封装,综合各自的优点,去其糟粕:

public class InvokeManager
{
    private InvokeManager()    //泛型单件
    {
        timeDelayLst = new List<float>();
        timeStateLst = new List<bool>();
        callbackDelayLst = new List<System.Action>();
    }

    private IList<float> timeDelayLst;
    private IList<bool> timeStateLst;
    private IList<System.Action> callbackDelayLst;

    public void Invoke(float _timeDelay, System.Action _callback)
    {
        timeDelayLst.Add(_timeDelay);
        timeStateLst.Add(false);
        callbackDelayLst.Add(_callback);
    }
    public int CancelInvoke(System.Action _callback, bool _cancelAll = false)    //false:仅取消最后注册的(和延迟时间无关);返回int型,可以根据剩余的次数立即执行未执行的回调函数
    {
        int tCount = 0;
        for(int i = callbackDelayLst.Count - 1; i >= 0; i--)
        {
            if(callbackDelayLst[i].Equals(_callback))
            {
                tCount++;
                timeDelayLst.RemoveAt(i);
                timeStateLst.RemoveAt(i);
                callbackDelayLst.RemoveAt(i);
                if(!_cancelAll)
                {
                    return tCount;
                }
            }
        }
        return tCount;
    }
    public void ClearInvoke()
    {
        timeDelayLst.Clear();
        timeStateLst.Clear();
        callbackDelayLst.Clear();
    }
    public void TickUpdate(float _deltaTime)
    {
        filtTick(_deltaTime);
        for(int i = timeStateLst.Count - 1; i >= 0; i--)
        {
            timeStateLst[i] = false;
        }
    }
  private void filtTick(float _deltaTime)
  {
    for(int i = timeDelayLst.Count - 1; i >= 0; i--)
    {
      if(timeStateLst.Count > i && !timeStateLst[i])
      {
        timeDelayLst[i] -= _deltaTime;
        timeStateLst[i] = true;
        if(timeDelayLst[i] <= 0)
        {
          var tCallback = callbackDelayLst[i];//从后向前遍历:删除最后注册(和延迟时间无关)的事件回调(可以自行扩展根据延迟时间进行删除)    
          timeDelayLst.RemoveAt(i);
          timeStateLst.RemoveAt(i);
          callbackDelayLst.RemoveAt(i);

          tCallback();
          filtTick(_deltaTime);
        }
      }
    }
  }
}        

封装之后仅需要在主函数逻辑中进行驱动计时,之后就可以根据需要方便的延迟及取消延迟了。(PS:因为只是简单的封装,因此还有以下缺点:1)递归回调的问题;2)无法取消匿名的回调函数。)
当然,也有一些比较方便使用的插件,这个可以根据需要自行研究了,eg:Vision Timer等,DoTween等也有提供接口功能。

原文地址:https://www.cnblogs.com/wayland/p/6229233.html