溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

理解Unity的Timesteps(步長)和實現(xiàn)平滑移動

發(fā)布時間:2020-02-29 07:38:34 來源:網(wǎng)絡(luò) 閱讀:21944 作者:xiaosongfang 欄目:游戲開發(fā)

首先不知道有沒有像我一樣,一直不是很清楚Timesteps具體怎么解釋的,稍微找了一下:

理解Unity的Timesteps(步長)和實現(xiàn)平滑移動


上面說的是兩個物理檢測幀之間的時間間隔

如果不是物理的話,可以直接理解為,兩幀之間的時間間隔


-----



接下來開始翻譯原文

原文:http://www.kinematicsoup.com/news/2016/8/9/rrypp5tkubynjwxhxjzd42s3o034o8


在Unity社區(qū)里,其中有一個辯論的最為激勵的話題,就是何如去除游戲中生澀的動作,讓它顯得自然。這個問題不只是在Unity引擎才有,所有引擎都有這個問題,而且他的產(chǎn)生的原因來自于你的引擎用怎么樣的timesteps。

并沒有哪種解決方案能解決所有的這些情況,不過有一種直接的方法解決問題。許多開發(fā)者遇到這個問題是在移動的過程中,發(fā)現(xiàn)物體一抖一抖的,而且想要改善很難。令人驚訝的是,外面關(guān)于unity的timesteps有很多誤傳,許多unity論壇的回答者,即使他說對了,也沒有綜合的分析,并且留下了許多理解上的空白讓人無法完全解決這些問題。本文旨在在更深層次的解釋unity的timesteps來解決這個問題,解釋為什么會產(chǎn)生抖動,并提供一個解決方案來解決這個問題。并且發(fā)布了一個AssetPackage來演示這個解決方案

理解Unity的Timesteps(步長)和實現(xiàn)平滑移動

額!這種類型的抖動是不是很熟悉?


上面的這個圖片就是一種簡單的運動抖動。很明顯這并不是我們想要出現(xiàn)在游戲里的畫面。如果你要重現(xiàn)這個非常簡單:

  1. 創(chuàng)建一個新project

  2. 導(dǎo)入一個默認(rèn)的第一人稱角色控制器,放在一個新場景

  3. 放置一些物件,并選擇一個朝著它畫圓

刪除掉頭的擺動,放置一個游戲平板在地面使得觀察效果更佳明顯。一般情況下,你能注意到,當(dāng)你繞著它轉(zhuǎn)時,這里會有特別明顯的抖動。

現(xiàn)在看看這種情況,當(dāng)我使用了稍后會討論的這些技術(shù)。和第一個例子對比,你能明顯看到在順滑上的重要的變化。

理解Unity的Timesteps(步長)和實現(xiàn)平滑移動

 如絲般順滑,好多了!

在解決這個問題之前,先去理解Unity(Mono)的生命周期是很重要的。特別是,我們必須去探索Update和FixedUpdate背后的邏輯。下面的鏈接是一遍來自Unity官方文檔的,關(guān)于Unity生命周期的小的摘錄:

https://docs.unity3d.com/Manual/ExecutionOrder.html

特別提醒一下,如果你以前沒有注意過Update的執(zhí)行順序,那么你需要特別的研究一下,因為接下來的部分與這個密切相關(guān)。

理解Unity的Timesteps(步長)和實現(xiàn)平滑移動

Unity的各個Update的執(zhí)行順序,原文:https://docs.unity3d.com/Manual/ExecutionOrder.html

先了解這個流程圖(生命周期)。


這個流程圖概括了Unity的各個Update執(zhí)行順序中,一幀里面哪些函數(shù)會被調(diào)用。這部分我們需要注意的是FixedUpdate和Update函數(shù),圖上綠色和紅色的部分。

Unity實現(xiàn)的是半固定的timestep。這意味著主游戲循環(huán)用一個可變的timestep跑在任何幀率上,這個在Unity里叫做deltaTime。并且用來控制一個使用固定timesteps的內(nèi)部的循環(huán)。

這里有一些好處:

首先,在游戲中物理在固定幀率的檢測下,可以進行游戲中畫面上的高幀率的Update,只要硬件允許。

相反的,這里也有一些壞處,容易出現(xiàn)上面所說的抖動。

下面是簡單的用代碼來解釋一下Unity的Update循環(huán)的結(jié)構(gòu):

float currentSimulationTime = 0;
float lastUpdateTime = 0;

while (!quit) // variable steps
{
        while (currentSimulationTime  < Time.time) // fixed steps
        {		
              FixedUpdate();
              Physics.SimulationStep(currentState, Time.fixedDeltaTime);
              currentSimulationTime += Time.fixedDeltaTime;
        }
 Time.deltaTime = Time.time - lastUpdateTime;
 Update();
 Render();
 lastUpdateTime = Time.time;
}

這個Update函數(shù)對你來說應(yīng)該很熟悉了,它在Monobehaviours的每幀里,在輸入已經(jīng)執(zhí)行后,在渲染前的中間這段時間里被調(diào)用。

如果垂直同步關(guān)閉了的話,當(dāng)一幀結(jié)束以后,Unity會立刻執(zhí)行下一幀,這樣子去獲得盡可能高的幀率。

當(dāng)每幀的硬件和計算量在不停的變化時,幀率也會不停的變化,即使垂直同步打開了,由于Unity嘗試去讓每個幀率盡量相同,也不會真的固定不變。

由于以上這些,Update函數(shù)能在一秒內(nèi)被調(diào)用任意次數(shù)。


FixedUpdate在每次Monobehaviors的物理檢測中進行。即使多個物理檢測中,他們的檢測的時間間隔不是相同的時間,Unity也會把他們放到固定的時間間隔內(nèi)調(diào)用。 這是因為在游戲的物理里,尤其是加速運動中,使用固定的時間差,是最準(zhǔn)確和最穩(wěn)定的。在Unity里,這個固定的時間差就叫做fixedDeltaTime。默認(rèn)的時候,fixedDeltaTime的值為0.02,這意味著在游戲里每秒鐘總有50次FixedUpdate的調(diào)用。用這種方法,你可以理解FixedUpdate是一個獨立的幀率,它在每秒鐘被調(diào)用的次數(shù)是固定的,即使這時你的渲染幀率非常低或者非常高。重點的提出FixedUpdate和物理的循環(huán)是相關(guān)關(guān)聯(lián)的很有必要的,不是發(fā)現(xiàn)在不同線程的。


基于這點,一起來觀察一下幾個關(guān)于更新時機的例子。


理解Unity的Timesteps(步長)和實現(xiàn)平滑移動


上面是當(dāng)每秒有50個FixedUpdate和60個Update的時候的更新時機??墒?,在實際中由于幀率是不固定所以不會這么完美,下面這個圖這個就是現(xiàn)實中的,一點點夸大的時間軸。注意到,有一些Update之間是沒有FixedUpdate的,這種情況一般出現(xiàn)在渲染簡單畫面的時候。另一方面一些Update之間有還幾次FixedUpdate,這個一般出現(xiàn)在加載資源的過程中。這意味著,即使你盡可能的讓Update和FixedUpdate次數(shù)相同,這也很難做到對齊。

理解Unity的Timesteps(步長)和實現(xiàn)平滑移動

根據(jù)我們對Unity的timesteps的知識,我們可以理解下面的這個案例。這個場景里,一個球和照相機繞著同一個支點。這個球的transform的改變放在Update循環(huán)里,與此同時,左邊照相機的transform改變放在FixedUpdate循環(huán)里,右邊的照相機放在Update循環(huán)里。左邊這個看起來就有很明顯的抖動,右邊的就很順滑

理解Unity的Timesteps(步長)和實現(xiàn)平滑移動

左邊的照相機在FixedUpdate里移動,右邊的在Update里


由于Update和FixedUpdate的調(diào)用不在通過頻率上,當(dāng)球在移動的時候照相機依然還在原地。這就導(dǎo)致了球相對于照相機移動的不同步,產(chǎn)生了抖動。當(dāng)在慢鏡頭下,這些行為就更為明顯。

理解Unity的Timesteps(步長)和實現(xiàn)平滑移動

和上面一樣的模型,5%的速度

所以,我們能看到導(dǎo)致抖動的緣由是因為移動不同的objects的時候,有的在Update里有的在FixedUpdate里。所以簡單的修復(fù)方法就是必須把所有移動transform的地方,要不放在Update里,要不放在FixedUpdate里。


然而,這時事情開始變得棘手了,這個常見的回答會使很多Unity開發(fā)者把游戲中的許多游戲內(nèi)的運動放在Update里,而FixedUpdate只放小部分物理邏輯。雖然這有它的優(yōu)點,比如簡化了你的操作,但是由于很多原因,它是有問題的。也許最大的問題是讓你的游戲依靠畫面的幀率進行。這就導(dǎo)致了有很多bug和連貫性的行為不一致的問題,打開了這些錯誤大門。此外,它會影響很多決策,包括幾乎所有的實時策略類型。當(dāng)你需要執(zhí)行加速度運動時,比如玩家的重力,這也會出現(xiàn)問題。像這些物體就應(yīng)該使用FixedUpdate,但由于其他的物體使用了Update,所以你會看到很多抖動。(請參考標(biāo)準(zhǔn)assets里面的第一人稱控制器來管著這個問題)

因此,一個常見的且有時是必要的選擇是,把所有的狀態(tài)和游戲邏輯放在固定的timestep里像FixedUpdate,嚴(yán)格的把畫面和輸入信息放在Update里。

然而,這里面不是只有它自己產(chǎn)生的問題,首先,你可能希望你游戲的物理幀也不同于游戲邏輯幀的節(jié)奏出現(xiàn)。這個操作非常簡單,Unity給了你很多操作的空間,允許你自己去選擇和優(yōu)化。接著,輸入信息只在Update里也會有問題,當(dāng)兩個FixedUpdate直接出現(xiàn)了多個Update時,只有最后那一個Update里的輸入信息會影響到后面的FixedUpdate。這個特然容易出現(xiàn)在上下按鈕事件,因為它們支隊單一幀有用。這個問題的解決方案是,在下一個FixedUpdate前,把輸入信息用一個input buffer進行緩沖。將此行為集成到您使用的任何輸入控制器中是一種相當(dāng)無縫的方式來執(zhí)行此操作,并將緩沖保留在一個位置。

然而,一個更大的問題是,一般來說FixedUpdate的調(diào)用會比Update少,所以移動物件的頻率跟不上畫面渲染的頻率,導(dǎo)致雖然畫面是流暢的,但是移動的時候斷斷續(xù)續(xù)的。

有很多種方法可以借鑒這種問題,比如用差值和推測的方法解決,使得可以平滑的移動。差值,可以很方便的流暢的使物體從一個狀態(tài)移動到另一個狀態(tài),很容易使用。不過這會導(dǎo)致有一個fixedDeltaTime的延時。這個延時在大部分游戲內(nèi)的一般情況下,是能被接受的,即使是射擊游戲的射手也可以使用這種方法獲得平滑的移動。推測,是預(yù)測物體的下個狀態(tài)會在哪個位置,避免了延時,但是本質(zhì)是更難做到無縫的滑動并且還帶來了性能的壓力。


理解Unity的Timesteps(步長)和實現(xiàn)平滑移動

攝像頭和球都在FixedUpdate中移動,右邊使用了差值計算

以上是演示差值的另一個比較案例。左邊這個圖,鏡頭和球的transform設(shè)置都在FixedUpdate里。右邊也是,不過右邊在FixedUpdate的兩次調(diào)用之間用了差值計算是的移動的時候更順滑。注意到兩邊的物件都基本保持了一致性,不過右邊的移動更流暢更少抖動。


所以,怎么在Unity用差值計算呢?我做了一個assetPackage,鏈接在下面:

http://ksblogcontent.s3-website-us-east-1.amazonaws.com/TimeStepBlog/Timesteps.unitypackage


此外,您可以獲取用于創(chuàng)建上述示例的構(gòu)建:

http://ksblogcontent.s3-website-us-east-1.amazonaws.com/TimeStepBlog/TimestepsBuild.zip


具體就看上面的demo了,后面懶得翻了。。。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI