I'm expecting the car to land and engage the suspension to dampen the landing. The code I'm using currently has this effect: car hops

# vehicle.gd
extends RigidBody3D

# Suspension variables
@export var suspension_rest_length: float = 0.5 
@export var suspension_max_length: float = 1.0
@export var suspension_stiffness: float = 500.0
@export var dampening_sensitivity: float = 450.0
@export var dampening_max: float = 30.0

# Suspension Raycasts
@onready var fr: RayCast3D = $FR
@onready var fl: RayCast3D = $FL
@onready var rr: RayCast3D = $RR
@onready var rl: RayCast3D = $RL

# Wheel nodes
@onready var fr_wheel: Marker3D = $FR/Wheel
@onready var fl_wheel: Marker3D = $FL/Wheel
@onready var rr_wheel: Marker3D = $RR/Wheel
@onready var rl_wheel: Marker3D = $RL/Wheel

func _physics_process(delta):
    if fr.is_colliding():
        # Get distance to ground
        var dist = fr.get_collision_point().distance_to(fr.global_transform.origin)

        # Get dampening, clamp between 0 and max dampening
        var dampening = clamp(fr_wheel.get_velocity().length() * dampening_sensitivity, 0, dampening_max)

        # At location of raycast, apply suspension forces
        self.apply_force(Vector3.UP * (suspension_stiffness * (dist - suspension_rest_length) + dampening), fr.global_transform.basis.y)

        # Set location of wheel to raycast collision point
        fr_wheel.global_transform.origin = fr.get_collision_point()
    if fl.is_colliding():
        var dist = fl.get_collision_point().distance_to(fl.global_transform.origin)
        var dampening = clamp(fl_wheel.get_velocity().length() * dampening_sensitivity, 0, dampening_max)
        self.apply_force(Vector3.UP * (suspension_stiffness * (dist - suspension_rest_length) + dampening), fl.global_transform.basis.y)
        fl_wheel.global_transform.origin = fl.get_collision_point()
    if rr.is_colliding():
        var dist = rr.get_collision_point().distance_to(rr.global_transform.origin)
        var dampening = clamp(rr_wheel.get_velocity().length() * dampening_sensitivity, 0, dampening_max)
        self.apply_force(Vector3.UP * (suspension_stiffness * (dist - suspension_rest_length) + dampening), rr.global_transform.basis.y)
        rr_wheel.global_transform.origin = rr.get_collision_point()
    if rl.is_colliding():
        var dist = rl.get_collision_point().distance_to(rl.global_transform.origin)
        var dampening = clamp(rl_wheel.get_velocity().length() * dampening_sensitivity, 0, dampening_max)
        self.apply_force(Vector3.UP * (suspension_stiffness * (dist - suspension_rest_length) + dampening), rl.global_transform.basis.y)
        rl_wheel.global_transform.origin = rl.get_collision_point()

1 Answer 1


Three issues:

  1. suspension_stiffness is 500 which seems like a very large massive force to apply. (Maybe the car is so heavy that this level is necessary?)
  2. Assuming your RayCast3D are casting below the wheel's neutral contact point, checking fl.is_colliding() means when the wheels are on the ground, they are applying the upward force. So the car will never want to rest on the ground.
  3. An upward "dampening" force is applied whenever the wheels are moving (in any direction).

Adjust your RayCast3D so they end their cast just above the lowest acceptable position for the wheel.

Step through your code and see what kinds of force values you're getting. Use a local variable so you can easily see the force strength:

var force_magnitude = suspension_stiffness * (dist - suspension_rest_length) + dampening
self.apply_force(Vector3.UP * force_magnitude, fl.global_transform.basis.y)

Try removing the damping component of the force so it can sometimes be zero. You may need to omit the call to apply_force if the total force is zero.

Try using a CSGSphere3D and changing its colour to indicate different states in your code to make it easier to see what's going on (or use the godot_debug_draw_3d plugin). You can also draw debug text and record your screen so you can scrub back and forth to see what's happening.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .