Unity 生命週期-fixedupdate爲啥能維持固定幀率

轉自:https://blog.csdn.net/ak47007tiger/article/details/115056047

常見的剛體運動,物理處理都需要放到fixedupdate裏,據說可以防止因設備幀率不同導致效果不同。

思考一個問題
fixedupdate固定幀率執行,難道方法裏面幹啥它都不會卡的麼,怎麼這麼牛逼
調查
於是我寫了一個demo,在fixedupdate裏面循環10萬次,做pow運算
發現
Update和LateUpdate幀率下降了
Fixed幀率仍然做到Unity默認的50幀
結論
unity 執行FixedUpdate如果達不到幀率就會多執行幾次這個函數直到達成目標幀率
這是以犧牲渲染幀率爲代價的 

在調查的時候,發現了幾個概念

轉自:https://blog.csdn.net/lijunhe1991/article/details/98777369

幀循環/主線程概念

爲了保證數據安全,Unity核心的遊戲邏輯全部都是在一個線程裏完成。也就是我們常說的Unity主線程。 在主線程上運行一次完整的遊戲邏輯幀我們稱之爲完成了一次幀循環,也稱爲主循環。
注意幾點:
1、一個邏輯幀我們通常是指一次包含了以Update()爲主的循環調用過程。因爲我們遊戲的邏輯大部分都是運行在Update()裏面。
2、一個邏輯幀裏面可能包含了多次FixedUpdate()的調用。
3、Unity中是可以使用多線程的,但需要注意在Unity裏一些關鍵性的數據在其它線程是不能訪問修改的。
Unity限制使用多線程的原因:
保證數據安全
降低編程難度
爲何還能保持高效率的運行?
Unity在底層實現了線程池,引擎底層來實現一些可使用多線程處理的任務

更詳細的解釋

轉自:https://www.cnblogs.com/murongxiaopifu/p/7683140.html

默認情況下FixedUpdate的更新頻率是50FPS(0.02s),如果當遊戲的更新頻率較大時——例如60FPS——那麼FixedUpdate有固定的更新頻率還稍微可以理解,但是如果遊戲本身的更新頻率就很低——例如30FPS——那麼是怎麼保證FixUpdate的更新頻率呢?

而Unity的主要邏輯是單線程的(參見Aras的回答),所以也不存在有不同的邏輯循環可能,換句話說Update和FixedUpdate是在同一個線程上調用的。

如果我們來重複一下各位測試FixedUpdate時經常採用的方式:打印兩次調用之間的時間間隔,或者是計算每次調用的累積時間,但區別在於我直接使用了真實的時間:

void FixedUpdate() { Debug.Log("FixedUpdate realTime: " + Time.realtimeSinceStartup); }

void Update() { Debug.LogError("Update realTime: " + Time.realtimeSinceStartup); }

爲了以示區別,正常的Log是FixedUpdate,LogError是Update,並且設定整個遊戲的更新頻率爲30FPS。

通過上圖可以發現,FixedUpdate並不是間隔0.02s才調用一次,相反,它有可能在Update之前調用多次

例如剛啓動時,在某次Update之前連續調用了11次FixedUpdate,而之後每次Update調用之前都會調用1~2次FixedUpdate方法,這也很好理解,因爲FixedUpdate的頻率是50FPS,而我們設定的更新頻率爲30FPS,FixedUpdate的調用次數勢必要多於Update。

所以,FixedUpdate除了用來處理物理邏輯之外並不適合處理其他模塊的邏輯,尤其是當大家的潛意識裏都篤定它的更新頻率是固定的時候。因爲這很危險,比如一個需要狀態同步的遊戲要求按照固定的頻率向目標同步狀態,如果貿然採用FixedUpdate方法,就會出現上圖那樣可能在短時間內多次調用的問題。所以最好只在物理邏輯的處理中使用FixedUpdate,而不要濫用。而由於FixedUpdate主要是用於物理邏輯的,因此下文的討論也主要圍繞物理邏輯。

當機器十分快時,引擎可能通過線程休眠來保證固定的FPS。

所以看上去整個遊戲保證一個大致的更新頻率似乎不難。但是現在問題的關鍵在於每次更新時的時間增量無法保證相同。而在物理模擬中,保證一個固定的增量時間是十分重要的。這是因爲在遊戲引擎進行物理模擬時要使用數值積分,而作爲最簡單的數值積分方法——歐拉法在遊戲引擎中大量使用。

上面就是一個歐拉法的簡單例子。可以看到增量時間是很重要的。而在遊戲引擎的物理模擬中,一個不穩定的增量時間可能導致很多和預期相悖的結果。

由於此時計算的是真實的時間,而真實的增量時間無法保證固定,那換一個思路,我們把參與物理模擬的增量時間當做一個常量可以嗎?換句話說,不論遊戲的更新頻率如何,參與物理模擬的增量時間是一個常數。

一個最簡單的固定增量時間的實現,顯然就是將固定的增量時間作爲一個常量參數傳遞給物理模擬模塊,這樣我們就能夠保證物理模擬的增量時間固定,同時還能將物理模擬的更新頻率和遊戲引擎的更新頻率進行解耦——物理的模擬不受引擎的更新頻率影響,無論遊戲的更新頻率是多少,傳遞給物理模擬的增量時間都是一個常量。
這裏還拿Unity引擎來舉例子,默認情況下項目的Fixed Timestep的值爲0.02s。也就是說物理模擬的頻率是50FPS,假設我們的遊戲的更新頻率是25FPS,那麼會發生什麼呢?沒錯,遊戲每1次Update時,物理模擬都要推進2次,也就是之前我們看到的在Update之前多次調用了FixedUpdate。那麼如果我們的遊戲更新頻率是100FPS呢?這次就變成了每2次Update調用1次FixedUpdate。

這也就解釋了爲何有的朋友在做相關的小測試的時候,在每次FixedUpdate內打印Time.deltaTime時輸出的都是0.02了,因爲Time.deltaTime並非兩次調用FixedUpdate之間真實的時間間隔,而是來自我們在項目的Time設置內設置的值——它是一個與真實時間無關的常量。

那麼這種固定增量時間的邏輯如何用代碼表示呢?很簡單,只需要在主循環的內部,再來一個二級循環。

double simulationTime = 0; double fixedTime = 20;

while (!quit()){

double realTime = Timer.GetTime();

while (simulationTime < realTime){

simulationTime += fixedTime;

UpdateWorld(fixedTime); }

RenderWorld();

}

而Unity的實現顯然也是類似的,這個在Unity手冊中關於執行順序的相關章節內可以看到。

上圖標明瞭FixedUpdate是屬於物理模擬模塊的,同時在主循環的內部,物理模擬的部分還有一個二級循環。

好了,到了需要總結一下的時候了。

經過上文的分析,我想各位應該都能瞭解一個遊戲引擎是如何實現定時器的了吧,而且在物理模擬時使用一個固定的增量時間也並不神祕,只需要讓物理模擬和真實時間解耦,使用一個常量作爲其增量時間即可。

同時還要提醒各位,不要濫用Unity的FixedUpdate方法,因爲它是用來處理物理模擬的,更重要的是它並非根據真實時間的間隔執行。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章