Fix slopes / sloped tiles movement problem in platformer games

Hi, any of you had problems making a platfotmer or creating a good movement in Godot 3.6?
In godot 4 all you have to do is enable constant speed and use snap, while in godot 3 using sloped tiles is way harder.

I’ve been making a platformer game for over a year and every solution that I found had its problems, now, I have found the “perfect solution”, it’s a little complicated but it works fine.

I used the solution that Godot gives: Godot 3.1 will get many improvements to KinematicBody – Godot Engine but it has a specific problem that I will talk about later.

In this solution you have to use move_and_slide_with_snap (also used by all other solutions) so the player moves with the floor, because the raycast shape makes the collision of the player have just one collision point with the floor, making the same effect that “constant speed” does in the godot 4. I also recommend to reading the article, it can help a lot.

the shape of the player looks like this:

446x472

note: the longer the raycast Shape is, more inclined the player can move:

image

(image removed form the article)

But, I found an issue with this solution, the player can be pinned in the corner im certain situations:
image
(with player sprite)
image

The first thing I did was adding 2 more raycast shapes, in my game, looks like this:
image

But this makes the movement very flickery in some moments, so when there’s nothing “inside” the player I want it to be disabled. For that, I added 2 raycasts(not the collision shape one) to detect that. But we don’t wanna it to trigger when moving in sloped tiles, so we do that:

func detectInside():
     $rayShapeLeft.disabled = not  ($raycastLeft.is_colliding() and abs($raycastLeft.get_colliding_normal().x) > 0.8) # it use the normal of the colision to determinates if it is a sloped tile or not, you can also adjust the 0.8 if needed.
     $rayShapeRight.disabled = not  ($raycastRight.is_colliding() and abs($raycastRight.get_colliding_normal().x) > 0.8)

image

but when the raycast shape is activaded, the player goes upward and after this, the player isn’t “inside of something” anymore and then the raycast shape is disabled.

before:

image

after:

image
The raycast isn’t colliding now, so the ray shape will disable, and it will repeat

To solve that, we will use 2 more raycasts, to verify if it is secure to disable the raycast shape

image
But this doesn’t work in all cases, like this one bellow, that’s because of the tile it collides with is a slope, the “… > 0.8” is not more true.
image

So we use one more raycast, that will verify if the rayshape of the center can collide with the floor
image

Now we have finished! That’s the final script that I’ve used is that (it needs to be called in the physics_process(delta) or in the process(delta)).

onready var insideDetect := [$insideDetectLeft, $insideDetectRight, $insideDetectLeftDown, $insideDetectRightDown]

func detectInside():
    var normalMin := 0.8
    
    if not $rayShapeLeft.disabled:
        if $insideDetectLeftDown.is_colliding():
            $rayShapeLeft.disabled = (not abs($insideDetectLeftDown.get_collision_normal().x) >= normalMin) and $floorDetect.is_colliding()

        else:
            $rayShapeLeft.disabled = true
    else:
        if $insideDetectLeft.is_colliding():
            $rayShapeLeft.disabled = (not abs($insideDetectLeft.get_collision_normal().x) >= normalMin) and $floorDetect.is_colliding()

        else:
            $rayShapeLeft.disabled = true
    
    if not $rayShapeRight.disabled:
        if $insideDetectRightDown.is_colliding():
            $rayShapeRight.disabled = (not abs($insideDetectRightDown.get_collision_normal().x) >= normalMin) and $floorDetect.is_colliding()
        else:
            $rayShapeRight.disabled = true
    else:
        if $insideDetectRight.is_colliding():
            $rayShapeRight.disabled = (not abs($insideDetectRight.get_collision_normal().x) >= normalMin) and $floorDetect.is_colliding()
        else:
            $rayShapeRight.disabled = true

Feel free to improve it and please, teel me if you found any issues with that. And good bye and hope it could help you :slight_smile: .