Getting rigidbodies up slopes

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    Rigidbody rb;
    bool Grounded, Sloped;
    Vector3 moveDir, sideDir;

    // Start is called before the first frame update
    void Start()
    {
        moveDir = transform.forward;
        rb = GetComponent<Rigidbody>();
      
    }

    private void isGrounded()
    {
        RaycastHit hit;

        if (Physics.Raycast(transform.position, -transform.up, out hit ,4f))
            {
            Grounded = true;
            }
        else
        {
            Grounded = false;
        }
     
       
    }

    private void isSloped()
    {
        RaycastHit hit;
        if (Physics.Raycast(transform.position, -transform.up, out hit, 2f))
        {
            if (hit.transform.up != Vector3.up)
            {
                Sloped = true;
                moveDir = slopeMoveDir(hit.transform.up);
                sideDir = slopeSideDir(hit.transform.up);


            } else
            {
                Sloped = false;
                moveDir = transform.forward;
                sideDir = transform.right;
               
            }
        }

    }

    private Vector3 slopeMoveDir(Vector3 a)
    {
        return Vector3.ProjectOnPlane(moveDir, a).normalized;
    }

    private Vector3 slopeSideDir(Vector3 a)
    {
        return Vector3.ProjectOnPlane(sideDir, a).normalized;
    }

    // Update is called once per frame
    void Update()
    {
        isGrounded();
        isSloped();
       

        if (Input.GetKey(KeyCode.W))
        {
            rb.velocity = new Vector3(rb.velocity.x, rb.velocity.y, moveDir.z * 5);
        }
        if (Input.GetKey(KeyCode.S))
        {
            rb.velocity = new Vector3(rb.velocity.x, rb.velocity.y, -moveDir.z * 5);
        }
        if (Input.GetKey(KeyCode.D))
        {
            rb.velocity = new Vector3(sideDir.x * 5, rb.velocity.y, rb.velocity.z);
        }
        if (Input.GetKey(KeyCode.A))
        {
            rb.velocity = new Vector3(-sideDir.x * 5, rb.velocity.y, rb.velocity.z);
        }
    }
}

I’m fairly new to programming and I wanted to get my feet wet with programming player movement using RigidBodies. I’ve been having fun so far, but I’m a little confused as to how I can get slope movements right.
I want to have a consistent speed on flat ground and moving up and down hill. I thought the ProjectOnPlane method would help me, but it still has troubles with going uphill and tends to skip going down. What math would I need to do to fix this?

I asked this a long time ago and I’m not really sure I ever got a clear answer. There’s a million different ways to go about this, but I combined the Sebastian Lague and Bardent’s methods.

Sebastian’s tutorial doesn’t really work anymore with current versions of Unity because his is based on translating an object which requires specific settings in Unity and comes with its own set of headaches.

Bardent’s method is the easiest to make work, but he only checks the ground from one point.

Corgi engine and Super Colliders use box casts and other complicated methods.

I would start with Bardent’s because it is easiest to understand:

But Sebastian Lague’s, while not really easy to use with other tutorials (pass through floors, moving platforms, etc, although he does cover this)
Is more essential for understanding how to use ray casting effectively to do what you want.

You can combine the 2 methods to cast rays to check normals to get the angle of the slope and see if it is above or below you, then multiply X and Y according to the angle.

Sorry for the wall of text, you’re on the right and it will work, but to get rid of any janky effects I would do more than one slope check so you know what is in front of the player, behind, and where the player is.

1 Like
using UnityEngine;
public class PlayerMovement : MonoBehaviour // use this script on a default capsule with a rigidbody
{
    Rigidbody rb;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }
    void FixedUpdate() // physics must go in FixedUpdate:
    {
        Vector3 input=new Vector3(Input.GetAxisRaw("Horizontal"),0,Input.GetAxisRaw("Vertical")); // Create the input vector
        if (Physics.SphereCast(transform.position,0.5f,Vector3.down,out RaycastHit hit,0.6f))   // are we grounded?
            input=Vector3.ProjectOnPlane(input,hit.normal).normalized;  // project our input vector onto the surface below
        rb.AddForce(input*30f);
    }
}