How to move a character on a platform and also not have them slide down a slope

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Ertain

I have a 3D game that involves platforms. One of the problems I encountered was my character not properly sticking to the platforms (even when my character is standing idle on the platform, the platform moves from underneath my character). I did a bit of research and found that using move_and_slide_with_snap() will solve this issue. I have used it a couple of time, and the function worked well. However, there’s the long-standing issue of stopping on slopes. I have tried turning on “stop on slopes” (it’s the fourth argument to move_and_slide_with_snap()), and that works with slopes. But it also breaks the mechanic of moving on platforms. I’m fine with using move_and_slide() to move my character. But the requirement is that the character has to:

  1. Move along with a platform.
  2. Not slide on a slope.

Any ideas on how these requirements can be fulfilled?

:bust_in_silhouette: Reply From: DaddyMonster

One easy fix is to use move_and_slide and add friction with velocity = lerp(Vector3.ZERO, velocity, friction) where friction is a value between 0 and 1. You can even break up the individual components if you prefer the results.

There are more exact solutions as well using the dot product but let me know if that works for you first.

Are you suggesting the use of friction on the platform or on a slope? I do break up the components such that the “Y” component of the velocity is used for gravity (the “X” and “Z” components are used for movement).

Ertain | 2021-06-17 06:42

That line just adds friction generally. It’s a solution that can be an optimal solution in certain games / undesirable in others but it’s super easy to try out which is why I suggested it first. Individual vectors are handled in the same way velocity.y = lerp(0, velocity.y, y_friction)

Failing this we’re taking the surface normal’s dot product off the global y.

DaddyMonster | 2021-06-17 08:14

Is lerp() used to change the velocity (or one of the components of the velocity) only a little?

What is your suggestion with the dot product? I have tried something with the dot product (applying gravity only when the difference between the surface normal and a raycast is off by a certain angle), but that has yielded mix results.

Ertain | 2021-06-17 23:19

You can use lerp to change it as little or as much as you want. it’s a case of exporting it and playing with the weights to see what works best. If at all.

With the normals, rather than launch into linear algebra - try this hack, just reversing the y component of velocity based on the y component of the normal of each collision after a few checks. It should cancel the slide if you’re on a slope.

var slide_count = get_slide_count()

if slide_count:
    if is_on_floor():
        for slide in slide_count:
            var slide_col = get_slide_collision(slide)
            if slide_col.normal.y < 1.0 and (velocity.x or velocity.z):
                velocity.y += slide_col.normal.y

Code not tested.

DaddyMonster | 2021-06-19 10:26

This looks like a good solution which may prove useful if I need some more sophisticated code for controls, e.g. moving platforms that have slopes on them.

Ertain | 2021-06-19 19:41

:bust_in_silhouette: Reply From: Ertain

I was able to fix this (or at least get to a place where it works) by looking at the official platformer demo. I set the platforms in my game to use a different collision layer and mask. Then I placed a RayCast on my character which checks for these platform layers (I had the RayCast point downward from the character). When the character lands on these platforms, it flips a variable to true. In the move_and_slide_with_snap() function for the character, it changes that variable to false. In the demo, it looks like this:

var is_on_platform = platform_detector.is_colliding()
    _velocity = move_and_slide_with_snap(
             _velocity, snap_vector, FLOOR_NORMAL, not is_on_platform, 4, 0.9, false
    )

This will work for what I have right now. Hopefully that’s all I need for my character to move on platforms.