使用 Jolt 物理引擎
前言
在 4.4 中,Jolt 物理引擎被加入作為現有 Godot Physics 物理引擎的替代方案。Jolt 由 Jorrit Rouwe 開發,重點面向遊戲與 VR 應用。它先前以擴充功能提供,如今已內建於 Godot。
需要特別注意的是,內建的 Jolt 物理模組目前被視為 尚未完成、實驗性,且與 Godot Physics 與 Godot Jolt 擴充功能相比 尚未達到功能一致。其行為在持續開發中可能改變。選擇要在專案中使用何者時,請將此納入考量。
現有的擴充功能目前處於維護模式。也就是說,錯誤修正仍會合併,並會維持與新版 Godot 的相容性,直到內建模組達到與該擴充功能功能一致為止。你可在 GitHub 上這裡 與 Godot 的素材庫中找到該擴充功能。
若要將 3D 物理引擎切換為 Jolt Physics,請將 Project Settings > Physics > 3D > Physics Engine 設為 Jolt Physics。完成後點擊 Save & Restart 按鈕。編輯器重新開啟後,3D 場景就會改用 Jolt 進行物理模擬。
與 Godot Physics 的顯著差異
現有的 Godot Physics 引擎與 Jolt 之間有許多差異。
關節屬性
目前 3D 關節節點的介面與 Jolt 自身關節的介面並不完全一致。因此有一些關節屬性尚未支援,主要是與關節「軟限制」設定相關的屬性。
未支援的屬性如下:
PinJoint3D:
bias、damping、impulse_clampHingeJoint3D:
bias、softness、relaxationSliderJoint3D:
angular_\*、\*_limit/softness、\*_limit/restitution、\*_limit/dampingConeTwistJoint3D:
bias、relaxation、softnessGeneric6DOFJoint3D:
*_limit_*/softness、*_limit_*/restitution、*_limit_*/damping、*_limit_*/erp
目前若將上述屬性設定為非預設值,會顯示警告。
單一剛體關節
在 Godot 中,你可以在雙剛體關節中省略其中一個剛體,讓「世界」充當另一個剛體。然而,你為剛體指定的節點路徑(node_a 與 node_b)會被忽略。Godot Physics 總是會把它視為指定到 node_a。由於 node_a 也用來定義關節限制的參考座標,你最終會得到反向的限制,甚至可能出現奇怪的限制形狀,特別是在你的限制同時允許線性與角度自由度時。
Jolt 則會把你的剛體視為指定到 node_b,並以 node_a 表示「世界」。如果你需要與既有專案相容,可以透過專案設定 Physics > Jolt Physics 3D > Joints > World Node 來切換此行為。
碰撞邊距
Jolt(以及其它類似的物理引擎)使用一種 Jolt 稱為「convex radius」的概念,來改善凸形狀所仰賴的碰撞偵測效能與行為。其他物理引擎(包含 Godot)可能會將其稱為「collision margins(碰撞邊距)」。Godot 在所有繼承自 Shape3D 的類別上以 margin 屬性公開這個值,但 Godot Physics 本身並不使用它。
在其他引擎中(如 Godot 文件所述),碰撞邊距有時等同於在形狀外加上一層「殼」,稍微放大形狀並使邊角變得圓滑。然而在 Jolt 中,會先用該邊距縮小形狀,之後再套用「殼」,同樣能讓邊角圓滑,但不會增加形狀的實際尺寸。
由於預設邊距對較小的形狀可能造成問題, 為了避免需要手動調整, Jolt 模組提供了專案設定 Physics > Jolt Physics 3D > Collisions > Collision Margin Fraction , 其會與該形狀 AABB 的最小軸長相乘來計算實際邊距; 而形狀本身的邊距屬性則改作為上限值。
在多數情況下,這些邊距應該幾乎是透明無感的,但在進行形狀查詢時,偶爾可能導致奇怪的碰撞法線。你可以降低上述專案設定來緩解(包含設為 0.0),但邊距太小也可能造成異常的碰撞結果,因此通常不建議。
Baumgarte 穩定化
Baumgarte 穩定化是一種處理相互穿透的剛體並將它們推回到恰好接觸狀態的方法。在 Godot Physics 中,它的行為類似彈簧,意味著剛體可能產生加速並造成超調,甚至完全分離。在 Jolt 中,穩定化僅應用於位置而非速度,這表示不會超調,但解除穿透可能需要更長時間。
可以透過專案設定 Physics > Jolt Physics 3D > Simulation > Baumgarte Stabilization Factor 來調整此穩定化的強度。將其設為 0.0 會關閉 Baumgarte 穩定化;設為 1.0 則會在 1 個模擬步驟內解決穿透,速度很快但通常不太穩定。
幽靈碰撞
Jolt 採用兩種技術來減輕幽靈碰撞,也就是與形狀/剛體內部邊緣發生的碰撞,導致碰撞法線方向與移動方向相反的情況。
第一種技術稱為「主動邊緣偵測(active edge detection)」,會依照與鄰近三角形的夾角,將 ConcavePolygonShape3D 或 HeightMapShape3D 中三角形的邊標記為「啟用」或「停用」。當碰撞發生在停用的邊上時,會以該三角形的法線取代碰撞法線,以降低幽靈碰撞的影響。
此主動邊緣偵測的角度門檻可透過專案設定 Physics > Jolt Physics 3D > Collisions > Active Edge Threshold 進行設定。
第二種技術稱為「增強型內部邊緣移除(enhanced internal edge removal)」,會在執行階段根據兩個剛體的接觸點來檢測邊是否為啟用或停用。此方法的好處是,不僅適用於與 ConcavePolygonShape3D 與 HeightMapShape3D 的碰撞,也適用於同一剛體內任意形狀之間的邊。
可透過專案設定 Physics > Jolt Physics 3D > Simulation > Use Enhanced Internal Edge Removal 來在各適用情境中啟用或停用「增強型內部邊緣移除」,以及針對 queries 與 motion queries 的類似設定。
請注意,當涉及兩個不同剛體之間的幽靈碰撞時,無論是主動邊緣偵測或增強型內部邊緣移除都不會套用。
記憶體用量
Jolt 在模擬步驟中對暫時分配使用堆疊配置器。此堆疊配置器需要預先配置一定量的記憶體,並可透過專案設定 Physics > Jolt Physics 3D > Limits > Temporary Memory Buffer Size 來調整。
Ray Cast 面索引
在 Jolt 中,intersect_ray() 與 RayCast3D 的結果所回傳的 face_index 屬性,預設一律為 -1。可透過專案設定 Physics > Jolt Physics 3D > Queries > Enable Ray Cast Face Index 啟用。
請注意,啟用此設定會使 ConcavePolygonShape3D 的記憶體需求增加約 25%。
運動式 RigidBody3D 的接觸
使用 Jolt 時,若 RigidBody3D 以 FREEZE_MODE_KINEMATIC 冷凍,基於效能考量,預設不會回報與其他靜態/運動剛體的碰撞接觸,即使將 max_contacts_reported 設為非零亦同。若你有許多/大面積的運動剛體與複雜的靜態幾何(如 ConcavePolygonShape3D 或 HeightMapShape3D)重疊,可能在不自覺中浪費大量 CPU 效能與記憶體。
因此,是否採用此行為需透過專案設定 Physics > Jolt Physics 3D > Simulation > Generate All Kinematic Contacts 主動開啟。
接觸衝量
由於 Jolt 的內部限制,PhysicsDirectBodyState3D.get_contact_impulse() 所提供的接觸衝量是預先估算的,根據接觸流形以及碰撞剛體的速度等資訊推得。這表示僅在所涉兩個剛體沒有與其他剛體碰撞時,回報的衝量才會精確。
Area3D 與 SoftBody3D
Jolt 目前不支援 SoftBody3D 與 Area3D 之間的任何互動,例如重疊事件,或 Area3D 上的風場屬性。
WorldBoundaryShape3D
用來表示無限平面的 WorldBoundaryShape3D 在 Jolt 與 Godot Physics 中的實作略有不同。兩者皆對此平面的有效尺寸有上限,但為避免精度問題,Jolt 下的上限要小得多。
你可以透過專案設定 Physics > Jolt Physics 3D > Limits > World Boundary Shape Size 來調整此尺寸。
與 Godot Jolt 擴充功能的顯著差異
雖然內建的 Jolt 模組在很大程度上是對 Godot Jolt 擴充功能的直接移植,但仍有一些差異。
專案設定
所有專案設定皆已從 physics/jolt_3d 類別移至 physics/jolt_physics_3d。
此外,個別的專案設定也進行了一些重新命名與重構,包括:
sleep/enabled現為simulation/allow_sleep.sleep/velocity_threshold現為simulation/sleep_velocity_threshold.sleep/time_threshold現為simulation/sleep_time_threshold.collisions/use_shape_margins現為collisions/collision_margin_fraction,其中值為 0 等同於停用。collisions/use_enhanced_internal_edge_removal現為simulation/use_enhanced_internal_edge_removal.collisions/areas_detect_static_bodies現為simulation/areas_detect_static_bodies.collisions/report_all_kinematic_contacts現為simulation/generate_all_kinematic_contacts.collisions/soft_body_point_margin現為simulation/soft_body_point_radius.collisions/body_pair_cache_enabled 現為 simulation/body_pair_contact_cache_enabled.collisions/body_pair_cache_distance_threshold現為simulation/body_pair_contact_cache_distance_threshold.collisions/body_pair_cache_angle_threshold 現為 simulation/body_pair_contact_cache_angle_threshold.continuous_cd/movement_threshold現為simulation/continuous_cd_movement_threshold,但以小數(fraction)而非百分比表達。continuous_cd/max_penetration現為simulation/continuous_cd_max_penetration,但以小數(fraction)而非百分比表達。kinematics/use_enhanced_internal_edge_removal現為motion_queries/use_enhanced_internal_edge_removal.kinematics/recovery_iterations現為motion_queries/recovery_iterations,但以小數(fraction)而非百分比表達。kinematics/recovery_amount現為motion_queries/recovery_amount.queries/use_legacy_ray_casting已被移除。solver/position_iterations現為simulation/position_steps.solver/velocity_iterations現為simulation/velocity_steps.solver/position_correction現為simulation/baumgarte_stabilization_factor,但以小數(fraction)而非百分比表達。solver/active_edge_threshold現為collisions/active_edge_threshold.solver/bounce_velocity_threshold現為simulation/bounce_velocity_threshold.solver/contact_speculative_distance現為simulation/speculative_contact_distance.solver/contact_allowed_penetration現為simulation/penetration_slop.limits/max_angular_velocity現改以弧度儲存。limits/max_temporary_memory現為limits/temporary_memory_buffer_size.
關節節點
Godot Jolt 擴充功能中提供的關節節點(JoltPinJoint3D、JoltHingeJoint3D、JoltSliderJoint3D、JoltConeTwistJoint3D、JoltGeneric6DOFJoint)並未包含在 Jolt 模組中。
執行緒安全
與 Godot Jolt 擴充功能不同,Jolt 模組具備執行緒安全,包含支援專案設定 Physics > 3D > Run On Separate Thread。然而這方面尚未經過非常充分的測試,應視為實驗性功能。