From 0f7603d3cb3c722c9cbe29769bd71bb17fed5ff7 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Wed, 5 Aug 2026 09:13:52 +0100 Subject: [PATCH] fix(ship): delta-scale drag so it stays correct at any physics tick rate drag_coefficient/angular_drag/the idle angular-drag multiplier were applied once per physics tick with no delta scaling, correct only because project.godot never pins physics/common/physics_ticks_per_second and Godot's default happens to be 60. _tick_scaled(k, state.step) makes the decay rate invariant to tick rate instead. Also promotes the previously hardcoded 0.9 idle angular-drag literal to an export, matching its sibling. --- Game/scripts/ship.gd | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Game/scripts/ship.gd b/Game/scripts/ship.gd index e1310025..788faeee 100644 --- a/Game/scripts/ship.gd +++ b/Game/scripts/ship.gd @@ -18,6 +18,7 @@ extends RigidBody3D @export var max_angular_speed = 3.0 # Maximum rotation speed @export var drag_coefficient = 0.98 # Linear drag (air resistance) @export var angular_drag = 0.95 # Rotational drag +@export var idle_angular_drag = 0.9 # Rotational drag when no rotation input is held @export_group("Surface Pull") @export var wall_pull_strength = 6.0 # Wall grav-plating strength (m/s^2-equivalent) @@ -279,12 +280,22 @@ func apply_rotation_forces(state: PhysicsDirectBodyState3D, rotation_input: Vect state.apply_torque(torque) +# Scales a per-tick decay multiplier `k` (defined at a 60 Hz reference rate) +# by the actual elapsed tick time `step`, so `v *= _tick_scaled(k, step)` +# decays at the same rate per second regardless of physics_ticks_per_second. +func _tick_scaled(k: float, step: float) -> float: + return pow(k, step * 60.0) + + func apply_drag_and_limits(state: PhysicsDirectBodyState3D, rotation_input: Vector3): # Linear drag (air resistance) # Physics: F_drag = -½ * ρ * v² * C_d * A (drag force equation) # Simplified: v_new = v_old * drag_coefficient (exponential decay) - # This simulates air resistance reducing velocity over time - state.linear_velocity *= drag_coefficient + # This simulates air resistance reducing velocity over time. + # _tick_scaled makes the decay rate invariant to the physics tick rate — + # drag_coefficient/angular_drag/idle_angular_drag are all defined as the + # per-tick multiplier at a 60 Hz reference rate. + state.linear_velocity *= _tick_scaled(drag_coefficient, state.step) # Angular drag (rotational resistance) # Physics: Similar to linear drag but for rotational motion @@ -292,10 +303,10 @@ func apply_drag_and_limits(state: PhysicsDirectBodyState3D, rotation_input: Vect # Simplified: ω_new = ω_old * angular_drag (exponential decay) if rotation_input.length() < 0.01: # More drag when not actively rotating to stop quicker - state.angular_velocity *= 0.9 + state.angular_velocity *= _tick_scaled(idle_angular_drag, state.step) else: # Normal drag when actively rotating - state.angular_velocity *= angular_drag + state.angular_velocity *= _tick_scaled(angular_drag, state.step) # Limit maximum speeds # Physics: Terminal velocity concept - maximum achievable speed