使用物理插值

我們要如何在 Godot 專案中應用物理插值?有什麼需要注意的事項嗎?

我們盡量讓這個系統易於使用,許多現有專案只需少量修改即可運作。不過某些情境下仍需特別處理,這些狀況將於下方說明。

開啟物理插值設定

第一步是於 專案設定 > 物理 > 共用 > 物理插值 開啟物理插值。這樣就可以開始執行你的遊戲了。

你可能不會發現明顯的差異,特別是當你的物理更新率設為 60 TPS 或其倍數時。然而,底層其實已經進行了許多運算。

小訣竅

若要將舊有遊戲轉換為使用插值,建議你暫時將 專案設定 > 物理 > 共用 > 每秒物理更新次數 設為較低的值(例如 10),這會讓插值問題變得更明顯,方便偵錯。

將(幾乎)所有遊戲邏輯從 _process 移至 _physics_process

物理插值最根本的要求(你可能已經這麼做了)是:所有物件的移動與遊戲邏輯,應該在 _physics_process``(每次物理更新時觸發)中執行,而不是在 ``_process``(每個畫面更新時觸發)中。換句話說,你的腳本應該主要在 ``_physics_process 處理大部分邏輯,包括處理輸入與 AI。

僅在物理更新時設定物件的變換,可讓自動插值系統處理物理更新之間的變換,同時確保遊戲在不同裝置上表現一致。此外,若遊戲在高 FPS 下執行,這也能減少 CPU 負擔,因為像 AI 這類邏輯將不會在每個畫面更新時都執行。

備註

如果你在物理更新之外設定插值物件的變換,插值的位置計算將會錯誤,導致抖動。這種抖動可能在你的裝置上看不出來,但對某些玩家來說會很明顯。因此,應避免在物理更新之外修改插值物件的變換。Godot 編輯器偵測到這種情況時,會嘗試給予警告。

小訣竅

這僅是*軟性規定*。有些情況下你可能需要在物理更新之外瞬移物件(例如關卡開始、物件重生時)。不過一般來說,建議你還是盡量在物理更新中處理節點變換。

確保所有間接移動都在物理更新時進行

請注意,在 Godot 中,節點不僅可以在腳本中直接移動,也能透過 Tween、動畫、路徑導引等自動方法移動。如果你用這些方法移動物件,應將它們的時序設為在物理更新(而不是每個畫面更新,即「idle」)時執行。(這些方法也可用於控制不插值的屬性,這種情況則不受限於此)。

備註

此外,節點除了可以直接移動,也可能因為父節點在 場景樹 中移動而間接移動。因此,父節點的移動也應盡量只在物理更新時進行。

選擇物理更新頻率

啟用物理插值後,畫面算繪與物理運算將分離,你可以根據遊戲需求自由選擇物理更新頻率。不再受限於螢幕刷新率的整數倍(以避免卡頓的情況)。

以下是簡單的參考建議:

低更新率(10-30)

中等更新率(30-60)

高更新率(60 以上)

CPU 效能較佳

複雜場景下物理表現良好

適合物理運算要求快的遊戲

輸入會有些微延遲

適合第一人稱遊戲

適合賽車遊戲

物理運算較簡單

備註

在開發過程中,你隨時可以調整物理更新頻率,只需修改專案設定即可。

物件瞬移時請呼叫 reset_physics_interpolation()

大多數情況下,你會希望在兩個物理更新之間進行插值。但有一種例外:當你一開始擺放物件,或將物件瞬移到新位置時,此時你不希望物件從原來位置(如原點)到新位置產生平滑移動,而是希望物件瞬間移動。

這時候,你應該呼叫 Node.reset_physics_interpolation。這個方法會將物件內部儲存的「前一個變換」設為與「當前變換」相同,如此在插值運算時就不會產生移動。

即便你忘了呼叫這個方法,在多數情況下(尤其是高更新率時)其實也不太會有問題。這通常可以等到遊戲優化時再處理。最嚴重的情況是,物件瞬移時會有一兩個影格出現「拖影」現象,你一看就會發現需要補上這行程式碼!

實際上,reset_physics_interpolation() 有兩種常見用法:

靜止起始(例如玩家角色)

  1. 設定初始變換

  2. 呼叫 reset_physics_interpolation()

這樣前後兩次的變換會完全一致,就不會產生起始時的移動。

動態起始(例如子彈)

  1. 設定初始變換

  2. 呼叫 reset_physics_interpolation()

  3. 直接設定物件於首次移動後應有的變換

這麼做會讓前一個變換為起始位置,並且讓當前變換看起來像已經模擬過一次。這樣物件會立刻開始移動,不會在原地多停留一個更新週期。

重要

請務必依照上述順序先設定變換,再呼叫 reset_physics_interpolation(),否則會看到不必要的「拖影」現象。

測試與除錯建議

即使你打算用 60 TPS 執行物理運算,為了徹底測試插值效果並確保遊戲體驗順暢,強烈建議你暫時將物理更新率設為較低的值(如 10 TPS)。

雖然遊戲流程可能不會完全如你預期,但這樣能更容易找出哪些地方需要呼叫 Node.reset_physics_interpolation,或是哪裡你該對(例如 Camera3D)自訂插值。等這些問題都修正後,再把物理更新率調回你想要的數值。

低物理更新率測試還有另一個好處:你能更容易發現其他和物理更新同步的子系統也可能導致異常。常見例子如動畫混合值,有時你可能會選擇在 _process() 裡手動插值處理這類屬性。