3D Kinematic Car: Slopes & Ramps


Your Kinematic Car climbs slopes, but it doesn’t look quite right:

alt alt


Kinematic bodies don’t automatically rotate on collision. When the wheels aren’t both touching the ground, as in the image above, we’ll need to align the car manually.

To begin, we need to detect when the wheel isn’t on the ground. Add two RayCast nodes to the car and align them with the front and rear wheels like so:

alt alt

For both, set the Cast To to (0, -0.25, 0) and don’t forget to check the “Enabled” box.

Aligning a 3D object

We’re going to reuse the code from the KinematicBody: Align with Surface recipe. Add this to car_base.gd:

func align_with_y(xform, new_y):
    xform.basis.y = new_y
    xform.basis.x = -xform.basis.z.cross(new_y)
    xform.basis = xform.basis.orthonormalized()
    return xform

Now, in the _physics_process() function, right after calling move_and_slide_with_snap(), we’ll check to see if we need to align the car:

# If either wheel is in the air, align to slope.
if $FrontRay.is_colliding() or $RearRay.is_colliding():
    # If one wheel is in air, move it down
    var nf = $FrontRay.get_collision_normal() if $FrontRay.is_colliding() else Vector3.UP
    var nr = $RearRay.get_collision_normal() if $RearRay.is_colliding() else Vector3.UP
    var n = ((nr + nf) / 2.0).normalized()
    var xform = align_with_y(global_transform, n)
    global_transform = global_transform.interpolate_with(xform, 0.1)

How it works

When neither wheel is on the ground, we don’t rotate the car at all.

Otherwise, we’re going to use an average of the front and rear rays’ results. When the ray is colliding, the collider’s surface normal is used. This way, if the two wheels are touching different slopes (like on a curved hill, for example), the result will be to try and get both wheels on the surface, like so:

alt alt

In this image, you can see the car isn’t aligned with either surface, but is halfway between.

If the ray is not hitting anything, then we’ll assume a horizontal surface. That will bring the front or rear down when the other wheel is touching.

Like video?