如何在Unity中檢測(cè)死循環(huán)和卡死
當(dāng)游戲在手機(jī)/模擬器上卡死,logcat沒(méi)有日志輸出,也沒(méi)有卡死堆棧信息或者bugly也沒(méi)有捕獲到異常,你是否很焦急?本文介紹一下我們項(xiàng)目中檢測(cè)Unity卡死的方法,也許適合你使用。
實(shí)現(xiàn)原理
在絕大多數(shù)情況下我們可以認(rèn)為Unity是單線程的,基于這點(diǎn)我們?cè)赨nity的系統(tǒng)函數(shù)FixedUpdate中統(tǒng)計(jì)游戲運(yùn)行期間的總幀數(shù),如果Unity沒(méi)有卡死,那么TotalFrame是會(huì)一直累加的,如果在某一段時(shí)間內(nèi)TotalFrame都不會(huì)變化了,則可以認(rèn)為Unity已經(jīng)卡死了
既然Unity的主線程已經(jīng)卡死了,我們就需要用另一個(gè)線程用來(lái)定時(shí)檢查unity主線程中的TotalFrame是否不會(huì)變化了
示例代碼
using System; using System.Threading; using UnityEngine; namespace KEngine { /// <summary> /// 開(kāi)另外一個(gè)線程檢測(cè)unity是否被卡死 /// </summary> public static class UnityThreadDetect { public static Thread _MainThread = System.Threading.Thread.CurrentThread;//獲取unity線程 private static int check_interval = 3000;//檢測(cè)間隔 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 } } } } } }
捕獲卡死的方法名
在我們的游戲中一般出現(xiàn)卡死的情況都是在定時(shí)器里面,我們的定時(shí)器是通過(guò)在Unity的Update驅(qū)動(dòng)定時(shí)器列表,當(dāng)卡死時(shí),在另一個(gè)線程中打印出定時(shí)器中正在執(zhí)行的函數(shù)就可以定位到卡死的函數(shù)了。定時(shí)器可參考:UnityTimer中的Timer.cs
同時(shí)在Unity的Update進(jìn)行派發(fā)多個(gè)事件,比如PreUpdate,Update,以便出問(wèn)題更容易定位到卡在那兒
舉例說(shuō)明問(wèn)題
下面舉例我們遇到的出現(xiàn)卡死的問(wèn)題
死循環(huán)
下面這個(gè)死循環(huán)在Unity中會(huì)卡死,而在.NET中不會(huì),.NET中當(dāng)i超過(guò)byte的最大值255時(shí)i會(huì)從0開(kāi)始
public static void TesBadCode() { byte i = 0; while (true) { i++; } }
目前我們遇到的絕大多數(shù)情況都是邏輯代碼中寫(xiě)了where(true) do xxx
然后里面某些情況不會(huì)break,導(dǎo)致循環(huán)永遠(yuǎn)退不出來(lái)
屏蔽了事件系統(tǒng)
在某些系統(tǒng)中屏蔽掉了UGUI的事件系統(tǒng),導(dǎo)致無(wú)法接受用戶輸入,這個(gè)問(wèn)題不應(yīng)該歸類為Unity卡死,但用戶反饋來(lái)看就是卡死了,無(wú)法操作。
重復(fù)添加定時(shí)器
起因是底層沒(méi)有對(duì)同名定時(shí)器進(jìn)行限制,在某些邏輯中誤使用,出現(xiàn)每秒添加一個(gè)定時(shí)器,而定時(shí)器中的邏輯很大且長(zhǎng)時(shí)間不退出的,當(dāng)不斷添加重復(fù)定時(shí)器就導(dǎo)致游戲運(yùn)行越來(lái)越慢
重復(fù)注冊(cè)事件
在一些界面的刷新函數(shù)和控制器函數(shù),被頻繁重復(fù)注冊(cè)了事件,導(dǎo)致在拋出事件時(shí),同一個(gè)函數(shù)被調(diào)用了N次,這個(gè)問(wèn)題在Unity的Profiler中可以清晰看到函數(shù)的調(diào)用次數(shù)
擴(kuò)展
遞歸調(diào)用
遞歸調(diào)用,會(huì)報(bào)stack overflow,不會(huì)讓unity卡死
為什么無(wú)限循環(huán)遞歸調(diào)用不會(huì)卡死Unity?
這是因?yàn)槊總€(gè)方法的方法調(diào)用棧容量
是有限的,當(dāng)超出之后就會(huì)跳出報(bào)stack overflow,不會(huì)讓?xiě)?yīng)用程序卡死
public static void TesBadCode() { while (true) { TesBadCode(); } }
總結(jié)
到此這篇關(guān)于在Unity中檢測(cè)死循環(huán)和卡死的文章就介紹到這了,更多相關(guān)Unity檢測(cè)死循環(huán)和卡死內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#采用HttpWebRequest實(shí)現(xiàn)保持會(huì)話上傳文件到HTTP的方法
這篇文章主要介紹了C#采用HttpWebRequest實(shí)現(xiàn)保持會(huì)話上傳文件到HTTP的方法,很實(shí)用的功能,需要的朋友可以參考下2014-08-08C#讀取與寫(xiě)入txt文件內(nèi)容的實(shí)現(xiàn)方法
在 C# 中讀取和寫(xiě)入文本文件內(nèi)容是一個(gè)常見(jiàn)的任務(wù),本文主要介紹了使用幾種不同方法讀取和寫(xiě)入文本文件的示例,并通過(guò)代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-08-08在C# WinForm應(yīng)用中實(shí)現(xiàn)多語(yǔ)種切換功能
本文將介紹如何在一個(gè)WinForm應(yīng)用程序中實(shí)現(xiàn)多語(yǔ)種切換,通過(guò)一個(gè)簡(jiǎn)單的示例,你將了解到如何使用資源文件管理不同語(yǔ)言的文本,并通過(guò)用戶界面實(shí)現(xiàn)語(yǔ)言切換,需要的朋友可以參考下2024-06-06C#實(shí)現(xiàn)Excel導(dǎo)入sqlite的方法
這篇文章主要介紹了C#實(shí)現(xiàn)Excel導(dǎo)入sqlite的方法,是C#程序設(shè)計(jì)中非常重要的一個(gè)實(shí)用技巧,需要的朋友可以參考下2014-09-09C# 兩種方式反編譯修改源碼(dnspy,ildasm & ilasm)
這篇文章主要介紹了C# 兩種方式反編譯修改源碼(dnspy,ildasm & ilasm),幫助大家更好的理解和使用c#語(yǔ)言,感興趣的朋友可以了解下2020-11-11