每一次接觸到unity的 FixedUpdate與Update的時候我都會去查一遍到底兩者之間有什麼樣的區別,但始終是一知半解,今天因為工作的關係又在去查了一次,經過了兩個小時的英文轟炸之後我才對這個既熟悉又神秘的function有了大概的理解。
以下只針對有物理的環境當作前提來解釋,沒有任何物理碰撞與反彈,只是要他強制移動的話怎麼做都不會有太大的影響。
移動的拆解
當玩家按下按鍵,並且畫面更新,角色移動,對於程式而言所發生的事情我們把它拆成兩件事情來看:
- 程式讀取到你的按鍵
- 程式讓你的角色移動
那麼簡單的“移動”這種行為有可能出現什麼問題呢?不外乎就是你按了按鍵,要不是沒動,要不是暴衝飛到不知道哪裡去,而這就會牽扯到Unity 如何去執行 FixedUpdate與Update了。
Unity如何執行 FixedUpdate與Update
放這張圖的意義不是要你開始深度頗析Unity 每一個function執行的順序,而是要你看看從上面數下來第三個灰色區塊的 FixedUpdate,他其實是夾著物理運算一起的,所以每執行一次 FixedUpdate就會執行一次物理運算,而緊隨其後的是Input system,接著就是我們的Update。
這裡先直接說結論。
我們的移動的步驟的第一步,程式讀取我們的Input,我會建議你把這段讀取輸入的程式放到Update裡面,如:float vertical = Input.GetAxis(“Vertical”)。
第二步“角色的移動”就會關乎到物理,例如Addforce(),所以會建議你把角色的物理移動放在 FixedUpdate()裡面處理。
到這裡你可能會問:那為什麼我不能用Update來執行我的移動?或者在 FixedUpdate裡面執行我的讀取輸入?又或者兩個步驟全部丟到同一個function裡面做撒尿牛丸?
這是因為,Update跟 FixedUpdate根本就不會乖乖聽你的話。
幀與Update跟 FixedUpdate
幀(frame):指的是遊戲更新的一個畫面。
幀率(frame per second):指的是一秒更新多少幀,也就是遊戲更新多少畫面。
幀與幀之間的時間(Frame time):也就是渲染一個幀所需要的時間,其實也就是Time.deltatime,當前幀與上一幀的時間差。
首先,你要先知道一個遊戲每一個時間點的幀率(FPS),都有可能是不一樣的,看每個遊戲角落裡跳動的FPS數值就會知道,也就是說每個時刻遊戲渲染一幀的時間(frame time)都會因為各種因素而有長有短。
而Update在Unity官方的api給出的執行方法,是每一幀,執行一次,也就是每一次更新畫面的時候,Update就會loop一次,而Update或快或慢,完全就取決於當時電腦渲染一幀的時間快慢。
FixedUpdate雖然與Update看起來很接近,但他們之間的頻率完全不是在同一個時間上, FixedUpdate擁有自己的獨立物理頻率,所以它的幀率永遠是固定的,預設的frame time是0.02秒,也就是它每秒會呼叫50次。
這裡以預設的 FixedUpdate來說,假設說你的遊戲以fps 25進行,也就是每秒渲染25幀,在Unity看來就會是每幀呼叫兩次 FixedUpdate(50/25),如果你的fps 是100,則每一次呼叫 FixedUpdate時就會進行大約兩次渲染幀,也就是進行兩次Update。
從遊戲渲染幀根本不固定可以看得出來,我們當初想的情況是這樣的:
但實際情況是這樣的:
有可能Update 執行很多次才執行一次 FixedUpdate,也有可能顛倒過來,而上面那張圖的兩段程式碼則揭露了如果不把移動的第一個步驟放到Update、第二個步驟放到 FixedUpdate很有可能會發生的情況。
第一種情況:把讀取Input放到 FixedUpdate裡面
Input system是發生在Update前面的。可以看到上面第一段綠色的程式碼,你按下了按鍵,Unity的確也在第一時間偵測到你按下了按鍵,但是眾所皆知,GetButtonDown只能是一次迴圈的事情(GetButton才是持續狀態),而這時候你電腦剛好渲染得快,所以你的Update 跑了第二次,這次GetButtonDown=false了,接著你來到了 FixedUpdate開開心心地要執行讀取Input的動作進入你的參數,結果啥都沒有,系統偵測到沒有參數,自然就不會跳了,然後你的使用者就會覺得你這個程式按了沒反應,爛。
第二種情況:把角色移動放到Update裡面
物理計算是與 FixedUpdate同步執行的。今天你把角色移動的程式放在Update裡面 ,可以看到第二段綠色的程式碼,電腦讀取到你的Input了,也要執行移動了,但是因為Update不穩定的關係,在一個固定的物理幀連續Loop了兩次,等於同樣你本來只是輕輕按一下,卻有double的addforce,這將導致物理運算結果跟之前按下按鍵得出來的運算結果會有所不同(有可能之前只執行一次addforce),使用者就會認為怎麼你每次按下按鍵給出的物理回饋不太一樣,害他死掉了,爛。
到這裡你有沒有更了解Update 與 FixedUpdate這對兄弟有更深的理解了呢?其實簡單的結論就是 FixedUpdate是固定時間執行一次,所以你把要時時刻刻偵測的輸入擺到這裡面,有可能會在 FixedUpdate之間lost掉。如果在Update擺物理向量,則有可能每次按給的物理回饋會有所不同。
如果我有幫助到你,就請按爆下面的拍手,讓這篇文章能讓更多英文苦手看到,感謝你的閱讀。