最近,整理项目框架逻辑时,无意中翻到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等也有提供接口功能。