现如今,Unity中的协程(Coroutine)方案已显得老旧,而Unitask等异步方案可以直接解决如异常捕获等各类问题。此外,Unity官方也在开发一套异步方案,但目前仍需要在协程这个方案上继续琢磨。
Unity协程中无法输出完整的栈跟踪,因为协程编译后会转换为IL编码的状态机,中间存在栈回到堆的过程。因此,在有多个yield函数嵌套的协程中出现报错时,栈信息会是缺失的,仅显示“栈信息丢失”。为了更好地解决这个问题,需要重新封装MoveNext()或采用Unitask。然而,这样做会增加代码的复杂度。经过摸索后,发现还存在一些可行的途径。
1. StackTrace类打印栈跟踪
使用StackTrace类可以得到当前执行栈的相关信息,通过接口GetFrame可以得到当前哪一层调用的相关信息。然而,由于协程会在编译后转换为状态机,所以无法获取完整的栈信息。
抖个机灵,如果在非yield语句中进行常规代码的调用或函数调用,则可正常拿到类名和代码行数。
打印:
14
19
24
下面将基于这个思路继续扩展。
2. StackTrace封装
2.1 Begin/End 语句块
下一步,创建一个名为CoroutineHelper的类,用于存放协程的相关扩展。在类中添加一个栈对象,保存每一步的栈跟踪信息。值得注意的是,没有直接使用C#自带的Stack,因为无法逆序遍历不方便输出栈日志,因此直接采用数组实现。若这样的话,每一步协程函数跳转都要用Begin、End语句包装又太丑。
2.2 使用扩展方法与using语法糖优化
实际上非yield语句,普通函数调用也是可以的,编译后不会被转换。因此可用扩展方法进行优化。这样调用时就舒服多了,对原始代码的改动也最小。
不过还需要处理函数结束时调用Pop方法,这个可以结合using语法糖使用。
最终调用时如下:
3. 打印输出
通过StackTrace类以及语法糖处理,可以拿到完整栈信息后,还需要打印输出。可以加入Unity编辑器下IDE链接的语法,这样打印日志直接具有超链接效果。最终效果如下: