Search Unity

  1. Unity 2018.1 has arrived! Read about it here
    Dismiss Notice
  2. Scriptable Render Pipeline improvements, Texture Mipmap Streaming, and more! Check out what we have in store for you in the 2018.2 Beta.
    Dismiss Notice
  3. If you couldn't join the live stream, take a peek at what you missed.
    Dismiss Notice
  4. Improve your Unity skills with a certified instructor in a private, interactive classroom. Learn more.
    Dismiss Notice
  5. ARCore is out of developer preview! Read about it here.
    Dismiss Notice
  6. Magic Leap’s Lumin SDK Technical Preview for Unity lets you get started creating content for Magic Leap One™. Find more information on our blog!
    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

    skusku

    Joined:
    Jan 4, 2015
    Posts:
    17
    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):
    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):
    1.  
    2.  
    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.
    6.    
    7.                 RaycastHit hit;
    8.    
    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.
    14.  
    15.                       float radius = Mathf.Abs(slopeRayHeight / Mathf.Sin(slopeAngle)); // slopeRayHeight is the Y offset from the ground you wish to cast your ray from.
    16.  
    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.                          }
    24.  
    25.                         return false; // return false if we are very near / on the slope && the slope is steep
    26.                       }
    27.  
    28.                      return true; // return true if the slope is not steep
    29.    
    30.                    }
    31.    
    32.                 }
    33.         }
    34.  
    35.  

    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.

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

    ulysses24769

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

    yotingo

    Joined:
    Jul 10, 2013
    Posts:
    23
    Thanks, this helped me too.
     
  4. XerxesBreak

    XerxesBreak

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

    canis

    Joined:
    Oct 25, 2013
    Posts:
    30
    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);
    3.  
    4. // therefore
    5. SlopeAngle = Vector3.SignedAngle
    6. (transform.forward, SlopeForward,Vector3.up);