Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

使用物理插值

我們要如何在 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() 裡手動插值處理這類屬性。

備註

In 2D, the position of visible collision shapes shown by the Debug > Visible Collision Shapes option will take physics interpolation into account.

By contrast, in 3D, the position of visible collision shapes will not take physics interpolation into account. This means the visible collision shapes can appear to move less smoothly and appear slightly in front of the object's visual representation when the object is moving. This is not a bug, but a consequence of how physics interpolation is implemented in 3D.