KinematicBody: Stopping on Slopes

Problem

Your KinematicBody slides down slopes.

Solution

We’ve started with a no-frills KinematicBody, using move_and_slide(), using the script below:

extends KinematicBody

export var gravity = -10.0
export var speed = 5.0
export var rot_speed = 4.0
export var jump_speed = 5.0

var velocity = Vector3.ZERO
var jumping = false

func get_input(delta):
    var input = Vector3.ZERO
    if Input.is_action_pressed("forward"):
        input += -transform.basis.z * speed
    if Input.is_action_pressed("back"):
        input += transform.basis.z * speed
    if Input.is_action_pressed("right"):
        rotate_y(-rot_speed * delta)
    if Input.is_action_pressed("left"):
        rotate_y(rot_speed * delta)
    velocity.x = input.x
    velocity.z = input.z

func _physics_process(delta):
    get_input(delta)
    velocity.y += gravity * delta

    velocity = move_and_slide(velocity, Vector3.UP)

    if jumping and is_on_floor():
        jumping = false

    if Input.is_action_just_pressed("jump"):
        if is_on_floor():
            jumping = true
            velocity.y = jump_speed

We see the problem if we stop moving on a slope:

alt alt

This is move_and_slide() doing what it’s supposed to do.

The downward velocity caused by gravity is being slid along the surface.

Checking the move_and_slide() documentation, we see there’s a parameter called stop_on_slope, which defaults to false:

If stop_on_slope is true, body will not slide on slopes when you include gravity in linear_velocity and the body is standing still.

So we can change our movement to this instead:

velocity = move_and_slide(velocity, Vector3.UP, true)

Now we stop sliding down slopes!

alt alt

But there is still a problem, which is easier to see if you use a low value for gravity:

alt alt

When we come to a stop, we have a little bit of upward momentum, which causes the small “hop”. We can solve this by switching to the move_and_slide_with_snap() method.

In order to ensure we can still jump, we also need to disable snapping during a jump, or we’ll remain “snapped” to the ground:

    var snap = Vector3.DOWN if not jumping else Vector3.ZERO
    velocity = move_and_slide_with_snap(velocity, snap, Vector3.UP, true)

Now the “hop” is gone, and everything works as expected.

Finally, you may notice that on very steep slopes, you still have a problem:

alt alt

This is because the default value of the floor_max_angle parameter is 45°, and the slope shown is greater. Any angle above this value does not count as a floor. Increasing the value makes this slope behave like the others:

velocity = move_and_slide_with_snap(velocity, snap, Vector3.UP,
        true, 4, deg2rad(52))

Like video?