Search Unity

  1. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  2. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  3. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  4. Participate with students all over the world and build projects to teach people. Join now!
    Dismiss Notice
  5. Build games and experiences that can load instantly and without install. Explore the Project Tiny Preview today!
    Dismiss Notice
  6. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  7. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

Character movement and slopes.

Discussion in 'Physics' started by skusku, Jan 11, 2015.

  1. skusku


    Jan 4, 2015
    As I had some problems with this, and came up with a solution to it, I wanted to share it with you guys.

    What I wanted:
    The character should be able to walk up slopes to a set degree.
    The character should slide down slopes that are too steep.
    The character should not be able to "stick" to slopes that are too steep while keeping the movement buttons pressed.
    The character should not be able to jump up slopes that are too steep.

    The solution:

    The character movement is handled by setting the velocity directly.

    Code (CSharp):
    1.  desiredVelocity.Set(desiredVelocity.x, rigidbody.velocity.y, desiredVelocity.z);
    The 2nd parameter of the velocity set is used to preserve any upwards or downwards velocity while moving. However this is the part that messed up the slopes. When you jumped onto a slope, the moment you hit the slope the downwards velocity would become 0, keeping the movements buttons pressed, would preserve this y-velocity of 0, making the character stick to the slope.

    I first tried finding out if we are currently standing on a slope and make that prevent all movement. However that did not work out very well, the character would often sit next to a slope, unable to move at all.

    The thing that worked:

    Find out if there is a slope in the direction we are heading. If yes, prevent movement in that direction.

    Code (CSharp):
    3.         bool checkMoveableTerrain(Vector3 position, Vector3 desiredDirection, float distance)
    4.         {
    5.                 Ray myRay = new Ray(position, desiredDirection); // cast a Ray from the position of our gameObject into our desired direction. Add the slopeRayHeight to the Y parameter.
    7.                 RaycastHit hit;
    9.                 if (Physics.Raycast(myRay, out hit, distance))
    10.                 {
    11.                     if (hit.collider.gameObject.tag == "Ground") // Our Ray has hit the ground
    12.                     {
    13.                       float slopeAngle = Mathf.Deg2Rad * Vector3.Angle(Vector3.up, hit.normal); // Here we get the angle between the Up Vector and the normal of the wall we are checking against: 90 for straight up walls, 0 for flat ground.
    15.                       float radius = Mathf.Abs(slopeRayHeight / Mathf.Sin(slopeAngle)); // slopeRayHeight is the Y offset from the ground you wish to cast your ray from.
    17.                        if (slopeAngle >= steepSlopeAngle * Mathf.Deg2Rad) //You can set "steepSlopeAngle" to any angle you wish.
    18.                       {
    19.                          if (hit.distance - collider.radius > Mathf.Abs(Mathf.Cos(slopeAngle) * radius) + slopeThreshold) // Magical Cosine. This is how we find out how near we are to the slope / if we are standing on the slope. as we are casting from the center of the collider we have to remove the collider radius.
    20. // The slopeThreshold helps kills some bugs. ( e.g. cosine being 0 at 90° walls) 0.01 was a good number for me here
    21.                          {
    22.                           return true; // return true if we are still far away from the slope
    23.                          }
    25.                         return false; // return false if we are very near / on the slope && the slope is steep
    26.                       }
    28.                      return true; // return true if the slope is not steep
    30.                    }
    32.                 }
    33.         }

    So all we gotta do in the end is

    Code (CSharp):
    1.                 if (checkMoveableTerrain(player.position, new Vector3(desiredVelocity.x, 0, desiredVelocity.z), 10f)) // filter the y out, so it only checks forward... could get messy with the cosine otherwise.
    2.                 {
    3.                     rigidbody.velocity = desiredVelocity;
    4.                 }

    So I hoped that helped anybody who is in the same position I was yesterday. Keep in mind, this is just code snippets, helping to give a general feel of the solution. You will have to adjust it. I am aware of not all code paths returning a bool value for the function, it's because I split that code into 2 functions, just trying to make it more readable here.

    Last edited: Jan 11, 2015
    ulysses24769 likes this.
  2. ulysses24769


    Aug 3, 2016
    ^^ you just made me save 1 whole day!! hurray!
  3. yotingo


    Jul 10, 2013
    Thanks, this helped me too.
  4. XerxesBreak


    Jun 14, 2015
    Just want to say thanks, this helped me a lot.
  5. canis


    Oct 25, 2013
    just a little suggestion, you can calculate slope angle by Vector3.Cross / SignedAngle instead of those tan,sin,cos

    Code (CSharp):
    1. // Getting the forward(pitch angle), based on current ground normal(raycastHit result).
    2. SlopeForward = Vector3.Cross(transform.right, GroundNormal);
    4. // therefore
    5. SlopeAngle = Vector3.SignedAngle
    6. (transform.forward, SlopeForward,Vector3.up);  
  6. neighborlee


    Jan 26, 2016
    I get why you had to script something for those goals, but what I don't get is why there was a problem with char. walking up a slope set at a current degree, that's included with unity out of the box,,a variable, vector..

    I'm having trouble though, so maybe that is what prompted this ? lol

    I know the angle my char. can not walk up, is only about 20 degrees,,just can't, YET he is able to walk up a 45 degree slope, WHICH yes is set in slope variable .

    Why is char having trouble with a 20 degree slope which is a lot smaller than 45 ?

    Last edited: Aug 29, 2018