Unity定时器堆栈显示
一般定时器是一个匿名函数,callback,在函数执行错误的话,很难查找到错误信息。
在这里可以利用添加定时器的时候 对调用栈进行一个快照,当发生错误时输出即可,这样调试起来方便多了。
AddTimer中栈 信息的一个快照为string 作为Timer对象的成员,release发行时可以去掉这种辅助机制
/* * Author: caoshanshan * Email: me@dreamyouxi.com code copy from https://git.oschina.net/dreamyouxi/XYGame; */ using UnityEngine; using System.Collections; using System.Collections.Generic; using ExtBase; public sealed class TimerQueue : Singleton<TimerQueue> { /// <summary> /// 添加一个真实时间定时器,返回函数调用即可 打断定时任务 /// </summary> /// <param name="time_delay"></param> /// <param name="cb"></param> /// <param name="repeat_times"> <0 will be run forever</param> public VoidFuncVoid AddTimer(float time_delay, VoidFuncNN cb, int repeat_times = 1, params object[] objs) { return this.AddTimer(time_delay, () => { cb(objs); }, repeat_times); } /// <summary> /// 添加一个真实时间定时器,返回函数调用即可 打断定时任务 /// </summary> /// <param name="time_delay"></param> /// <param name="cb"></param> /// <param name="repeat_times"> <0 will be run forever</param> public VoidFuncVoid AddTimer(float time_delay, VoidFuncVoid cb, int repeat_times = 1) { if (repeat_times == 0) return null;// () => { }; Timer time = new Timer(); time.delay = time_delay; time.cb = cb; time.repeat_times = repeat_times; //记录定时器添加的时候的快照栈信息,以 提供错误时 更多调试信息 System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(true); string ss = "\n"; for (int i = 0; i < st.FrameCount; i++) { var f = st.GetFrame(i); string name = f.GetFileName(); if (name.Length > Application.dataPath.Length) { name = name.Substring(Application.dataPath.Length, name.Length - Application.dataPath.Length); } ss += " " + f.GetMethod().ToString() + " at " + name + " (" + f.GetFileLineNumber() + ") \n"; } time.stackInfo = ss; time.Init(); list.Add(time); return () => { if (time != null && this.list.Contains(time)) time.SetInValid(); }; } /// <summary> /// unity engine tick /// </summary> public void Tick() { this.Tick(ref list); } private void Tick(ref List<TimerBase> list) { if (list.Count < 1) return; for (int i = 0; i < list.Count; i++) { if (list[i] == null) continue; TimerBase timer = list[i] as TimerBase; timer.Tick(); } for (int i = 0; i < list.Count; ) { TimerBase b = list[i] as TimerBase; if (b.IsInValid()) { list.Remove(b); } else { ++i; } } } public void Clear() { this.list_ms.Clear(); this.list.Clear(); } List<TimerBase> list_ms = new List<TimerBase>(); List<TimerBase> list = new List<TimerBase>(); } class TimerBase : GAObject { public string stackInfo = ""; public virtual void Tick() { } public VoidFuncVoid cb = null; public int repeat_times = 1; protected int repeat_times_current = 0; } sealed class Timer : TimerBase { public override void Tick() { if (this.IsInValid()) return; if (current < delay) { current += Time.deltaTime; return; } if (cb != null) { try { cb(); } catch (System.Exception e) { Debug.LogError(e.Message); Debug.LogError("add timer stackTrace:" + this.stackInfo); Debug.LogError("call stackTrace:" + e.StackTrace); } cb = null; } current = 0.0f; if (++repeat_times_current >= repeat_times && repeat_times >= 0) { this.SetInValid(); } } public override bool Init() { return true; } float current = 0.0f; public float delay = 0.0f; }