+3 votes

I want to prevent my character from climbing slopes of greater than 45 degrees, the player has a capsule shaped collision box. The code below shows how I am moving the character, however, the player is able to scale anything less than 90 degrees, only a wall will stop it. If I stop on the slope he falls down accordingly, but he should be prevented from climbing steep enough ones as well.

extends KinematicBody

var max_speed = 30
var gravity = -9.8/2
var jump_height = 50

var motion = Vector3()

func _physics_process(delta):

func run_and_jump(delta):
    var dir = Vector3(0, 0, 0)
    motion.y += gravity
    motion.x = 0
    motion.z = 0

    if Input.is_action_pressed("ui_left"):
        dir.x = -1
    elif Input.is_action_pressed("ui_right"):
        dir.x = 1

    if Input.is_action_pressed("ui_up"):
        dir.z = -1
    elif Input.is_action_pressed("ui_down"):
        dir.z = 1

    dir = dir.normalized() * max_speed

    if Input.is_action_just_pressed("action_jump") and is_on_floor():
        motion.y = jump_height

    dir += motion

    motion = move_and_slide(dir, Vector3(0, 1, 0))
in Engine by (95 points)

3 Answers

0 votes

I have the exact same problem.

I found this:

Which presents a weird solution using raycast. But that is for 2d and it doesn't seem to have been ported to 3D as intended. Maybe that is on the way yet.

Maybe if we did this:

But it is also for 2d, I'm afraid in 3d we would get into situations where this can't be done as we would get too many collisions to read from. Imagine a slight slope next to a steep one where we want to get up on one way but not another.

I really thought the "max slope angle" on the moveandslide() would prevent this from happening, but that seems to only affect rather the player may slide down from a slope or not.

by (21 points)
0 votes

I came up with this solution where I compute the dot-product for a 45% slope vector against the y-axis (choosing x or z doesn't matter). It works sort of ok:


// here we define what a steep slope is
var steepSlope = new Vector3(1,1,0)).Normalized();
var _FLOOR_MIN_DOT_PRODUCT = (steepSlope.Dot(Vector3.Up);

// note the testOnly parameter here
var collision = MoveAndCollide(velocity, testOnly: true);

var slopeDotProduct = collision.Normal.Dot(Vector3.Up);

if (slopeDotProduct < _FLOOR_MIN_DOT_PRODUCT)
    velocity = Vector3.Zero;

This is in C#, so it has to be translated to GDScript first. I usually just look at GDScript documentation and translate method names to CamelCase when I code in C#, so it shouldn't be hard to reverse this.

Dot-product is -1 if opposite from a vector in 3D and 1 if the same direction. Make sure they are normalized to get a predictable value.

by (18 points)
0 votes

One thing that helped in my case, is not applying x-z movements when not floored. This would still need some fine-tuning to work properly, but it mostly solves the issue. For me it makes it also easier to jump while running around.

Here an example. Instead of:

velocity.y -= delta * GRAVITY
velocity.x = direction.x * MAX_SPEED
velocity.z = direction.z * MAX_SPEED

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

I do:

velocity.y -= delta * GRAVITY
if is_on_floor():
    velocity.x = direction.x * MAX_SPEED
    velocity.z = direction.z * MAX_SPEED

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

This avoids applying the x and z movements when you're up a steep slope, thus stopping the "force" the object is doing against it, which causes the sliding. It also keeps the object better grounded instead of flying around, so that jumping is easier. Remaining problems:

  • The object will still slightly go up a slope, then bounce back.
  • There is some jitter when pushing against steep slopes.
by (14 points)
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.