# The player (kinematic body) continues to slide down the slope after I have stopped moving it

Posts: 27Member
in 3D

Hello! My player, which is a kinematic body, continues to slide down the slope after I have stopped moving it. For fixing this, I applied two tactics:

1. Its movement is set in such a way, that when the player is on the ground (doesn't jump), gravity is applied only when the floor angle is greater than 45 degrees.
Here is an image with the code:

For this, I have followed the instructions from the following tutorial:

This tactic slows down the sliding considerably, but doesn't stop.

1. Also, I have set parameter "stop_on_slope" of the function "move_and_slide" to be true.

This tactic stops the sliding after 5 to 10 seconds. But this is not the desirable result.

I have heard that in the version 3.2 of Godot, the parameter "stop_on_slope" no longer works, but what workarounds are there for this?

• Posts: 27

I finally found the solution:
I use "move_and_slide_with_snap", thanks for the idea, guys

When I am walking, the snap vector (second parameter of "move_and_slide_with_snap") is set to be the opposite of the floor_normal (the perpendicular vector of the surface the Raycast collides with).

When I am jumping, the snap vector is set to null vector Vector3(0, 0, 0).

``````var floor_normal = \$Raycast.get_collision_normal()

if has_contact and Input.is_action_just_pressed("jump "):
velocity = move_and_slide_with_snap(velocity, Vector3(0, 0, 0), Vector3(0, 1, 0), true)
velocity.y = jump_height
has_contact = false

velocity = move_and_slide_with_snap(velocity, -floor_normal, Vector3(0, 1, 0), true)
``````

This works for me. Now, the player stops on the slope definitively, until I move him again.

• Posts: 260

Finally, a solution! I've also been looking for this for a long time.
The main part is setting the snap vector to the inverted floor normal, although sometimes snap needs to be disabled(e.g. for jumping, @Tudor said he sets the snap vector to Vector3.ZERO. The same can be done by using move_and_slide() instead).

• Posts: 49Member

Maybe it is related:

• Posts: 927Moderator

In one of my prototypes, I just stop applying gravity if "is_on_floor()" is true, and that seems to work.

• Posts: 749Moderator

You could also try using a ray shape for the bottom bit of the player's collision. This prevents the player from sliding period.

• Posts: 260Member

This isn't perfect, but the best solution I've found:

``````var floorAngle = acos(floorNormal.dot(Vector3.UP))
move_and_slide(velocity,Vector3.UP,true,4,clamp(floorAngle,0,maxSlopeAngle),false)
``````

For some reason, setting the maximum slope angle to the angle of the slope the player is standing on helps. I used this for now, but I don't think it's good enough. Maybe there's some way to tweak it to work better, maybe limiting the vertical velocity with raycasts....

• Posts: 18Member
edited April 30

I had the same issue and managed to solve it using a workaround.
EDIT I'm now using a different, better solution (in my opinion), see further down

The problem is the maxSlopeAngle does not work when using velocity interpolation to slow the movement gradually.
Why?
Because the slide only stops if the horizontal velocity is smaller than 0.01 (looked into the C++ code). So when reducing the velocity gradually, the gravity will always create a downward movement and make a horizontal movement greater than 0.01. If you set your horizontal velocity manually to 0, it will work (but then you lose the movement smoothing).

To fix that, I split the horizontal and vertical movement:

``````        var is_on_floor #my own is_on_floor variable, since is_on_floor() won't work

#horizontal direction at max speed, 0 if stopping
var target_velocity = input_direction * target_speed

#interpolating horizontal velocity
velocity.y = 0.0
velocity = velocity.linear_interpolate(target_velocity , acceleration * delta)

velocity = move_and_slide(velocity, Vector3.UP, true, 4, deg2rad(max_slope_angle))

#apply gravity only, hack to fix issue with move and slide (sliding down slopes)
var result = move_and_collide(Vector3(0.0, gravity * delta, 0.0), true, true, true)
if result != null:
global_transform.origin.y += result.travel.y
is_on_floor = true
fall_speed = 0.0
#no collision detected, freefall
else:
is_on_floor = false
fall_speed += gravity * delta
global_transform.origin.y += fall_speed
``````

Obviously the problem is I call two times the physics engine instead of once, but it seems to work, so that's good enough for now, while waiting for a fix.

• Posts: 260Member

So you only apply gravity when the player is falling? Does your fix work with snapping?

• Posts: 18Member

Yes, gravity is applied when falling (result = null means there is nothing under the player). For snapping, I don't know, I never used move_and_slide_with_snap, not sure what it does exactly.

• Posts: 749Moderator

The function moves the body, but snaps it to the closest floor. That helps preventing the character from bouncing down a hill for example, they would stay glued to it. It's best to disable snapping when already in the air though, so setting it's `snap` property to a zero length vector when in the air should do.

• Posts: 27Member

The video ”An example of why Godot is (currently) bad at 3D” only demonstrates the problem, but, unfortunately, it doesn't provide a solution.

• Posts: 27Member

Yes, I also apply gravity only in the following cases:

• "is_on_floor" is true, but floor_angle > MAX_FLOOR_ANGLE.
• "is_on_floor" is false.
• Posts: 27Member

Yes, I use a Raycast. In the image that I posted above, "Tail" is the Raycast.

• Posts: 18Member

I also tried to deactivate gravity when not moving first, but I had issue with monsters sometimes flying when attacking me.

My new solution with always applying gravity works, but now monsters slide when the slope is high, when they shouldn't. So I'm still trying to find a better solution.

• Posts: 27Member
edited April 28

Regarding this solution:

``````var floorAngle = acos(floorNormal.dot(Vector3.UP))
move_and_slide(velocity,Vector3.UP,true,4,clamp(floorAngle,0,maxSlopeAngle),false)
``````

I use the following code, which resembles the code above, but with fewer parameters used at "move_and_slide:

``````var floor_angle = rad2deg(acos(floor_normal.dot(Vector3(0, 1, 0))))
if floor_angle > MAX_SLOPE_ANGLE:
velocity.y += gravity * delta
velocity = move_and_slide(velocity, Vector3(0, 1, 0), true)
``````

I tried both solutions, but a did't notice any difference in Player's behaviour.
Basically, they act the same way: they allow the kinematic body to slide with very low speed for a few seconds on the slope, after which they stop it.

• Posts: 18Member
edited April 28

Interesting, I will try this solution, might work better.

• Posts: 18Member
edited April 28

I found a new way to apply gravity and stop sliding, by manipulating the gravity vector. If you set the gravity towards the floor normal, the movement is stopped. So I can make my creatures move and stop on slopes by interpolating this gravity vector.

Here is the code:

``````    if is_on_floor():
#horizontal direction at max speed, 0 if stopping
var target_velocity = input_direction * max_speed
target_velocity += -get_floor_normal() * gravity
#move the current velocity towards this target velocity
velocity = velocity.linear_interpolate(target_velocity, acceleration * delta)
else:
#falling, accelerate down
velocity.y += gravity * delta

move_and_slide(velocity, Vector3.UP)
``````
• Posts: 27Member

@joel127 , regarding your solution with splitting the horizontal and vertical movement, can you explain me, please, the following part of your code?

``````var hvel = velocity
hvel.y = 0.0
hvel = hvel.linear_interpolate(vel_max_speed, acceleration * delta)
``````

Is "val_max_speed" a Vector3() variable? And is it like a "target" variable?
In my controller:

``````var target = direction * speed
``````

where "direction" is a Vector3() and "speed" is a float constant.

I am asking because in my code, MAX_SPEED is float constant, and in the "linear_interpolate()" function, it cannot be put a float variable as the first parameter. The first parameter can be only a Vector3() variable, a Vector2() or a Color.

• Posts: 18Member
edited April 30

yes, you are guessing right, sorry my code is poorly explained (code updated).

vel_max_speed is the velocity vector we are trying to reach. It is the input direction in the world multiplied by the max speed allowed. When stopping, this vector will be equal to Vector3(0,0,0).

This code is working well, but the KinematicBody is still sliding down when the slope is too high, I don't really understand why. So now I am using my second solution that seems to work very well in all situations.

• Posts: 18Member

The "First Person Starter" template also don't slide on slopes. It's using "move_and_slide_with_snap"

Project available here:
https://github.com/Whimfoome/godot-FirstPersonStarter

• Posts: 260Member

The relevant part of his code:

``````# clamping (to stop on slopes)
if direction.dot(velocity) == 0:
var _vel_clamp := 0.25
if velocity.x < _vel_clamp and velocity.x > -_vel_clamp:
velocity.x = 0
if velocity.z < _vel_clamp and velocity.z > -_vel_clamp:
velocity.z = 0

# Move
velocity.y = move_and_slide_with_snap(velocity, _snap, FLOOR_NORMAL,
``````

FLOOR_NORMAL is a constant Vector3.UP.

• Posts: 27Member

@joel127 , I have tried both your solutions, but unfortunately, none of them worked for me.

• The climb up the hill is very slow and difficult for the player (he even tries to change the direction to the right or to the left). I suppose here is a problem with the gravity: it is too powerful.
• When the player stops on the hill, he continues to slide down slowly.

• The movement of the player is very unstable (he shakes up and down continuously).
• The player accelerates very slowly.
• Changing the direction of moving is very difficult: it takes a while until the kinematic body starts to move on a new direction.
• The player climbs the hill faster that in the previous solution, but when you stop on the hill, he slides down quickly, like on a sledge. :DD

How well do these solutions work for you?

• Posts: 27Member

I finally found the solution:
I use "move_and_slide_with_snap", thanks for the idea, guys

When I am walking, the snap vector (second parameter of "move_and_slide_with_snap") is set to be the opposite of the floor_normal (the perpendicular vector of the surface the Raycast collides with).

When I am jumping, the snap vector is set to null vector Vector3(0, 0, 0).

``````var floor_normal = \$Raycast.get_collision_normal()

if has_contact and Input.is_action_just_pressed("jump "):
velocity = move_and_slide_with_snap(velocity, Vector3(0, 0, 0), Vector3(0, 1, 0), true)
velocity.y = jump_height
has_contact = false

velocity = move_and_slide_with_snap(velocity, -floor_normal, Vector3(0, 1, 0), true)
``````

This works for me. Now, the player stops on the slope definitively, until I move him again.

• Posts: 260Member

Finally, a solution! I've also been looking for this for a long time.
The main part is setting the snap vector to the inverted floor normal, although sometimes snap needs to be disabled(e.g. for jumping, @Tudor said he sets the snap vector to Vector3.ZERO. The same can be done by using move_and_slide() instead).

• Posts: 260Member

For many games, the player should slide down slopes, as long as they exceed the maximum slope angle. For that, my code to get the slope angle is useful:

``````var floor_angle = acos(floor_normal.dot(Vector3.UP))
``````

And usually rad2deg() would be used to convert it to degrees.

``````if(rad2deg(floor_angle)>maxSlopeAngle):
#player is sliding down a steep slope
``````

Then, in move_and_slide_with_snap(), use Vector3.DOWN as the snap vector. That will make the player slide down as usual. For my game, I also apply increased downwards velocity so that he slides down quickly. This could also be multiplied by the floor angle, so that slopes slightly too steep only make him slide a little; that's just not what I did. It may also be necessary to decrease player speed, so that the player can't move up a steep slope.

• Posts: 260Member
edited May 23
``````move_and_slide_with_snap(velocity, -floor_normal, Vector3(0, 1, 0), true)
``````

This randomly stopped working. I think the raycast is returning incorrect normals(I tested with a cube mesh, and made it point at the floor normal), but I don't know why that would be. The raycast definitely points down, and my code is exactly the same as before. What could possibly be causing it?

It might be scaled collision shapes. Collision shapes have to have a uniform scale. This means that scales have to all have the same number (like `(1.5, 1.5, 1.5)`), making scales like `(1.5, 1, 1.5)` unstable. With non-uniform scales, unfortunately the collision shapes return really strange normal vectors when used with a raycast.

• Posts: 260Member

@TwistedTwigleg , which collision shape would that be? I checked the player, raycast, and terrain collision shapes, and each was scaled to (1,1,1)

edited May 23

I dunno, it was just something that I encountered when I was working with raycasts. Like you, I suddenly had strange results despite nothing with the code (or most of the project) had changed. After debugging, and I think I also opened a GitHub issue on the matter, I realized it was the scale of the collision nodes and their shapes. Using uniform scales fixed the issue, so I thought that could have been the case here.

Edit: Are any of the collision nodes themselves scaled? Like the terrain, player, etc? I believe they will also need to use uniform scales for the normal vectors from the raycast to be reported correctly.

• Posts: 260Member
edited May 24

Good advice, but I don't think that's the case here. The terrain was scaled up, but it was scaled by (2.5,2.5,2.5). It was scaled that way when it was working also, so that can't be the problem.

Edit: I found a previous build also had sliding problems, apparently I had this problem and didn't realize(I was dealing with flat surfaces). It may be the code, but I don't think there's anything I can do about it. I may need to code the character script from the start again.

• Posts: 1Member

...or just wait for 4.0 and really bug Reduz and co to make a strong case for an overhaul of the entire system. It shouldn't be this hard!