Unity中如何有效检测并避免游戏死循环或卡死现象?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1262个文字,预计阅读时间需要6分钟。
当游戏在手机/模拟器上卡死,logcat没有日志输出,也没有卡死堆栈信息或bugly捕获到异常,你是否非常焦虑?以下简要介绍我们项目中检测Unity卡死的几种方法,或许能帮助你。
1. 日志监控:即使logcat没有输出,也可以通过Unity自身的日志系统来捕获信息。在Unity编辑器中,可以通过`Debug.Log`等方法手动添加日志,或者在代码中设置日志级别,确保关键信息被记录。
2. 内存分析:使用Unity Profiler或第三方工具(如Visual Studio的Performance Profiler)来监控内存使用情况。当检测到内存占用异常增加时,可能意味着卡死。
3. 堆栈跟踪:在Unity中,可以通过`System.Diagnostics.StackTrace`获取当前的堆栈信息。虽然这不能直接在模拟器或手机上实现,但可以在调试模式下通过日志记录这些信息。
4. 性能计数器:使用Unity的Performance Counter来监控CPU、GPU等性能指标。如果发现某项指标异常升高,可能是卡死的原因。
5. 脚本调试:在关键脚本中添加断点,观察执行过程。如果脚本卡死,断点不会触发,这时可以检查脚本逻辑。
6. 第三方工具:使用如Crashlytics、Bugsee等第三方异常监测工具,它们可以在用户设备上捕获卡死信息,并通过网络发送回服务器。
以上方法可以帮助你初步定位卡死问题,但实际操作时还需结合具体情况进行调整。希望这些建议对你有所帮助。
当游戏在手机/模拟器上卡死,logcat没有日志输出,也没有卡死堆栈信息或者bugly也没有捕获到异常,你是否很焦急?本文介绍一下我们项目中检测Unity卡死的方法,也许适合你使用。
实现原理
在绝大多数情况下我们可以认为Unity是单线程的,基于这点我们在Unity的系统函数FixedUpdate中统计游戏运行期间的总帧数,如果Unity没有卡死,那么TotalFrame是会一直累加的,如果在某一段时间内TotalFrame都不会变化了,则可以认为Unity已经卡死了
既然Unity的主线程已经卡死了,我们就需要用另一个线程用来定时检查unity主线程中的TotalFrame是否不会变化了
示例代码
using System; using System.Threading; using UnityEngine; namespace KEngine { /// <summary> /// 开另外一个线程检测unity是否被卡死 /// </summary> public static class UnityThreadDetect { public static Thread _MainThread = System.Threading.Thread.CurrentThread;//获取unity线程 private static int check_interval = 3000;//检测间隔 public static void Start() { new Thread(CheckMainThread).Start(); } static void CheckMainThread() { long frame = 0; while(!AppEngine.IsApplicationQuit) { frame = AppEngine.TotalFrame; Thread.Sleep(check_interval); if (frame == AppEngine.TotalFrame) { Log.LogToFile("unity thread dead,ThreadState:{0}",_MainThread.ThreadState); if (AppEngine.IsApplicationFocus) { //todo report error } } } } } }
捕获卡死的方法名
在我们的游戏中一般出现卡死的情况都是在定时器里面,我们的定时器是通过在Unity的Update驱动定时器列表,当卡死时,在另一个线程中打印出定时器中正在执行的函数就可以定位到卡死的函数了。定时器可参考:UnityTimer中的Timer.cs
同时在Unity的Update进行派发多个事件,比如PreUpdate,Update,以便出问题更容易定位到卡在那儿
举例说明问题
下面举例我们遇到的出现卡死的问题
死循环
下面这个死循环在Unity中会卡死,而在.NET中不会,.NET中当i超过byte的最大值255时i会从0开始
public static void TesBadCode() { byte i = 0; while (true) { i++; } }
目前我们遇到的绝大多数情况都是逻辑代码中写了where(true) do xxx 然后里面某些情况不会break,导致循环永远退不出来
屏蔽了事件系统
在某些系统中屏蔽掉了UGUI的事件系统,导致无法接受用户输入,这个问题不应该归类为Unity卡死,但用户反馈来看就是卡死了,无法操作。
重复添加定时器
起因是底层没有对同名定时器进行限制,在某些逻辑中误使用,出现每秒添加一个定时器,而定时器中的逻辑很大且长时间不退出的,当不断添加重复定时器就导致游戏运行越来越慢
重复注册事件
在一些界面的刷新函数和控制器函数,被频繁重复注册了事件,导致在抛出事件时,同一个函数被调用了N次,这个问题在Unity的Profiler中可以清晰看到函数的调用次数
扩展
递归调用
递归调用,会报stack overflow,不会让unity卡死
为什么无限循环递归调用不会卡死Unity?
这是因为每个方法的方法调用栈容量是有限的,当超出之后就会跳出报stack overflow,不会让应用程序卡死
public static void TesBadCode() { while (true) { TesBadCode(); } }
总结
到此这篇关于在Unity中检测死循环和卡死的文章就介绍到这了,更多相关Unity检测死循环和卡死内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!
本文共计1262个文字,预计阅读时间需要6分钟。
当游戏在手机/模拟器上卡死,logcat没有日志输出,也没有卡死堆栈信息或bugly捕获到异常,你是否非常焦虑?以下简要介绍我们项目中检测Unity卡死的几种方法,或许能帮助你。
1. 日志监控:即使logcat没有输出,也可以通过Unity自身的日志系统来捕获信息。在Unity编辑器中,可以通过`Debug.Log`等方法手动添加日志,或者在代码中设置日志级别,确保关键信息被记录。
2. 内存分析:使用Unity Profiler或第三方工具(如Visual Studio的Performance Profiler)来监控内存使用情况。当检测到内存占用异常增加时,可能意味着卡死。
3. 堆栈跟踪:在Unity中,可以通过`System.Diagnostics.StackTrace`获取当前的堆栈信息。虽然这不能直接在模拟器或手机上实现,但可以在调试模式下通过日志记录这些信息。
4. 性能计数器:使用Unity的Performance Counter来监控CPU、GPU等性能指标。如果发现某项指标异常升高,可能是卡死的原因。
5. 脚本调试:在关键脚本中添加断点,观察执行过程。如果脚本卡死,断点不会触发,这时可以检查脚本逻辑。
6. 第三方工具:使用如Crashlytics、Bugsee等第三方异常监测工具,它们可以在用户设备上捕获卡死信息,并通过网络发送回服务器。
以上方法可以帮助你初步定位卡死问题,但实际操作时还需结合具体情况进行调整。希望这些建议对你有所帮助。
当游戏在手机/模拟器上卡死,logcat没有日志输出,也没有卡死堆栈信息或者bugly也没有捕获到异常,你是否很焦急?本文介绍一下我们项目中检测Unity卡死的方法,也许适合你使用。
实现原理
在绝大多数情况下我们可以认为Unity是单线程的,基于这点我们在Unity的系统函数FixedUpdate中统计游戏运行期间的总帧数,如果Unity没有卡死,那么TotalFrame是会一直累加的,如果在某一段时间内TotalFrame都不会变化了,则可以认为Unity已经卡死了
既然Unity的主线程已经卡死了,我们就需要用另一个线程用来定时检查unity主线程中的TotalFrame是否不会变化了
示例代码
using System; using System.Threading; using UnityEngine; namespace KEngine { /// <summary> /// 开另外一个线程检测unity是否被卡死 /// </summary> public static class UnityThreadDetect { public static Thread _MainThread = System.Threading.Thread.CurrentThread;//获取unity线程 private static int check_interval = 3000;//检测间隔 public static void Start() { new Thread(CheckMainThread).Start(); } static void CheckMainThread() { long frame = 0; while(!AppEngine.IsApplicationQuit) { frame = AppEngine.TotalFrame; Thread.Sleep(check_interval); if (frame == AppEngine.TotalFrame) { Log.LogToFile("unity thread dead,ThreadState:{0}",_MainThread.ThreadState); if (AppEngine.IsApplicationFocus) { //todo report error } } } } } }
捕获卡死的方法名
在我们的游戏中一般出现卡死的情况都是在定时器里面,我们的定时器是通过在Unity的Update驱动定时器列表,当卡死时,在另一个线程中打印出定时器中正在执行的函数就可以定位到卡死的函数了。定时器可参考:UnityTimer中的Timer.cs
同时在Unity的Update进行派发多个事件,比如PreUpdate,Update,以便出问题更容易定位到卡在那儿
举例说明问题
下面举例我们遇到的出现卡死的问题
死循环
下面这个死循环在Unity中会卡死,而在.NET中不会,.NET中当i超过byte的最大值255时i会从0开始
public static void TesBadCode() { byte i = 0; while (true) { i++; } }
目前我们遇到的绝大多数情况都是逻辑代码中写了where(true) do xxx 然后里面某些情况不会break,导致循环永远退不出来
屏蔽了事件系统
在某些系统中屏蔽掉了UGUI的事件系统,导致无法接受用户输入,这个问题不应该归类为Unity卡死,但用户反馈来看就是卡死了,无法操作。
重复添加定时器
起因是底层没有对同名定时器进行限制,在某些逻辑中误使用,出现每秒添加一个定时器,而定时器中的逻辑很大且长时间不退出的,当不断添加重复定时器就导致游戏运行越来越慢
重复注册事件
在一些界面的刷新函数和控制器函数,被频繁重复注册了事件,导致在抛出事件时,同一个函数被调用了N次,这个问题在Unity的Profiler中可以清晰看到函数的调用次数
扩展
递归调用
递归调用,会报stack overflow,不会让unity卡死
为什么无限循环递归调用不会卡死Unity?
这是因为每个方法的方法调用栈容量是有限的,当超出之后就会跳出报stack overflow,不会让应用程序卡死
public static void TesBadCode() { while (true) { TesBadCode(); } }
总结
到此这篇关于在Unity中检测死循环和卡死的文章就介绍到这了,更多相关Unity检测死循环和卡死内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!

