Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Walking down slopes and positioning char with Raycast

Discussion in 'Scripting' started by Iron-Warrior, Dec 16, 2009.

  1. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    836
    Having a bit of trouble getting this to work, been trying to do it for a bit now...bear in mind I'm new to Unity.

    I'm trying to make it so that when my character is walking down a slope of any kind, the character won't 'bounce' down the slope awkwardly. You can easily see this in the Island Demo, if you walk down a steep slope, the character bounces down it. This makes for unwieldly character control and bugs me. Here I have a picture of the problem.



    Once the character moves from position A to B, he is no longer touching the platform, and doesn't count as 'grounded', which means gravity takes over. Since in my game when you're jumping you lose control over a fair amount of the character's movements, this is a problem. It also looks ugly.

    Most of my movement code is based off of the FPS Walker from the FPS tutorial (mine is a third person game, though)

    Code (csharp):
    1. var speed = 5.0;
    2. var turnSpeed = 0.1;
    3. var jumpSpeed = 8.0;
    4. var gravity = 20.0;
    5. var sensitivity = 3.0;
    6. var dropStep = 10.0;
    7.  
    8. private var moveDirection = Vector3.zero;
    9. private var grounded : boolean = false;
    10.  
    11. function FixedUpdate ()
    12. {
    13.    
    14.     if (grounded)
    15.     {      
    16.         moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    17.         moveDirection = transform.TransformDirection(moveDirection);
    18.         moveDirection *= speed;
    19.     }
    20.    
    21.     var hit : RaycastHit;
    22.        
    23.     if (Physics.Raycast (transform.position, -Vector3.up, hit)) {
    24.         moveDirection.y -= hit.distance;
    25.         print ("HitDistance" + hit.distance);
    26.         print ("MoveDirectY" + moveDirection.y);
    27.     }
    28.    
    29.     //moveDirection.y -= gravity * Time.deltaTime;
    30.    
    31.     var controller : CharacterController = GetComponent(CharacterController);
    32.    
    33.     var flags = controller.Move(moveDirection * Time.deltaTime);
    34.    
    35.    
    36.     rotationX = 0.0;
    37.     rotationX += Input.GetAxis("Mouse X") * sensitivity;
    38.     transform.Rotate(Vector3.up, rotationX);
    39.    
    40.     grounded = (flags  CollisionFlags.CollidedBelow) != 0;
    41.    
    42.  
    43.    
    44.     Screen.showCursor = false;
    45. }
    The important part is here

    Code (csharp):
    1.     var hit : RaycastHit;
    2.        
    3.     if (Physics.Raycast (transform.position, -Vector3.up, hit)) {
    4.         moveDirection.y -= hit.distance;
    5.         print ("HitDistance" + hit.distance);
    6.         print ("MoveDirectY" + moveDirection.y);
    7.     }
    I define my RaycastHit var as var, then do a raycast. I shoot it downwards from the position of the character, then add the distance it fired before hitting something to the moveDirection Vector3...and it's not really working. When I walk off a ledge 10 meters high, I should instantly hit the ground, right? The char falls faster than normal, but because I move him the distance of the ray, he should INSTANTLY land on the ground.

    Once I get this working, I would of course add in things like only checking when he's walking off a small ledge, but since I can't figure this out it'll have to wait. As I said before, I new to Unity and it's fairly possible I'm missing something here.

    This seems like something most people would have to solve to make any kind of game with slopes, so I hope it's a common problem...any kind of solution would be nice. Thanks for any help!
     
  2. jcurrie33

    jcurrie33

    Joined:
    Dec 9, 2009
    Posts:
    49
    This issue has bugged me as well, but I haven't set out to fix it - until now!

    I think the problem with your initial approach is that Move() does not just change the transform's position, but has calculations that handle the ground collisions, so you're still going to be calling the offending code after your "adjustment," which will essentially get overridden.

    I think we might have to do an "after-the fact" correction, putting similar code after the Move() gets called.

    I'll mess around with it right now and let you know if I came up with anything good.
     
  3. jcurrie33

    jcurrie33

    Joined:
    Dec 9, 2009
    Posts:
    49
    Think I got it :)

    This works for me, and I am also using a modified ThirdPersonController movement:

    Code (csharp):
    1.  
    2. var hit : RaycastHit;
    3.     var slopeAdjust : Vector3 = Vector3.zero;
    4.     if (Physics.Raycast (transform.position, -Vector3.up, hit)) {
    5.         slopeAdjust.y = hit.distance-controller.height/2;
    6.     }
    7.     transform.position.y -= slopeAdjust.y;
    8.  
    Put this as the last thing in your Update() function.

    The important differences to your script are:
    1. Do this after all other Move commands have processed.
    2. Apply the movement directly to the transform.
    3. Take into account the Controller's height (assuming it's at the origin, so just take into account 1/2 the height)

    This produces the desired result for me (no skipping), but I do not know if this would produce any negative side effects. I don't use jumping right now, for example, so you'd need to add flags to handle that, etc.

    Let me know if it works for you.
     
  4. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    836
    Just put it in and it works great, no skipping at all. I havn't put in any jumping or stuff like that, but I wouldn't think it would cause a problem...this would only need to be done if the character is walking down a slope, or off a small ledge, rather than falling through the air.

    There is one problem for me...I have setup a large box (10x10x10) in my game world with a plank angled at 45 degrees for the character to walk up to the box. Sometimes the character will stop walking, freeze for about 3 seconds, and appear below the plank on the ground.

    I just added in your code so I have done any troubleshooting, but I'm guessing it relates to the width of the platform or something. Probably will be able to figure it out.

    Thanks for the help, really quick response!
     
  5. jcurrie33

    jcurrie33

    Joined:
    Dec 9, 2009
    Posts:
    49
    Yeah I'm reproducing what I think is the same problem. I'll try to resolve it, and let you know. Please let me know if you come up with something first. Thx!
     
  6. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    836
    Okay, just figured out that it has to do with whether the character is grounded or not. I took out the

    Code (csharp):
    1. if (grounded) {
    And the problem no longer happens. No a particularly scientific solution...but it's fine for now, since alot of stuff will get rewritten anyways to accomodate jumping and falling.

    Second problem I found: Since the raycast shoots out from the center of the character controller, when a character falls off the edge of a slope like a an angled plank, he will be sitting halfway through the slope, and can walk through it. Probably not too tough to solve, maybe doing multiple raycasts from each edge of the character controller or something.
     
  7. jcurrie33

    jcurrie33

    Joined:
    Dec 9, 2009
    Posts:
    49
    I'm unclear which lines you commented out. If you remove that aren't you removing the gravity handling?
     
  8. jcurrie33

    jcurrie33

    Joined:
    Dec 9, 2009
    Posts:
    49
  9. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    836
    Here's my most recent code, from my last post. Havn't done anything on it since I'm sort of studying for my Computing final (sort of is the key word)

    Code (csharp):
    1. function FixedUpdate ()
    2. {
    3.    
    4.     //if (grounded)
    5.     //{    
    6.         moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    7.         moveDirection = transform.TransformDirection(moveDirection);
    8.         moveDirection *= speed;
    9.     //}
    10.  
    11.     moveDirection.y -= gravity * Time.deltaTime;
    12.    
    13.     var controller : CharacterController = GetComponent(CharacterController);
    14.    
    15.     var flags = controller.Move(moveDirection * Time.deltaTime);
    16.    
    17.    
    18.     rotationX = 0.0;
    19.     rotationX += Input.GetAxis("Mouse X") * sensitivity;   
    20.     transform.Rotate(Vector3.up, rotationX);
    21.    
    22.     var hit : RaycastHit;
    23.     var slopeAdjust : Vector3 = Vector3.zero;
    24.     if (Physics.Raycast (transform.position, -Vector3.up, hit)) {
    25.         slopeAdjust.y = hit.distance-controller.height/2;
    26.     }
    27.     transform.position.y -= slopeAdjust.y;
    28.    
    29.     grounded = (flags  CollisionFlags.CollidedBelow) != 0;
    30.    
    31.     Screen.showCursor = false;
    32. }
    The character moves downwards at the rate of gravity whether he's grounded or not - I guess Unity doesn't let the controller.Move function push objects through colliders.

    And good find on the threads. For the second one concerning characters walking up slopes, I had the same problem and planned to fix it similar to the way they did, nice to see I wasn't the only one with that issue.

    EDIT: Ahh made a stupid error.

    EDIT 2: Okay, trying to make some changes and getting odd bugs...get back to you on it...
     
  10. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    836
    Alrighty...just got it to work completely bug free, as far as I know. Here is what I had...

    Code (csharp):
    1. function FixedUpdate ()
    2. {
    3.     var controller : CharacterController = GetComponent(CharacterController);
    4.    
    5.     if (grounded)
    6.     {      
    7.         moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    8.         moveDirection = transform.TransformDirection(moveDirection);
    9.         moveDirection *= speed;
    10.     } else {
    11.         moveDirection.y -= gravity * Time.deltaTime;
    12.     }  
    13.    
    14.     rotationX = 0.0;
    15.     rotationX += Input.GetAxis("Mouse X") * sensitivity;   
    16.     transform.Rotate(Vector3.up, rotationX);
    17.    
    18.     var hit : RaycastHit;
    19.     var slopeAdjust : Vector3 = Vector3.zero;
    20.    
    21.     if (Physics.Raycast (transform.position, -Vector3.up, hit)) {
    22.         if (hit.distance < 5.0) {
    23.             slopeAdjust.y = hit.distance-controller.height/2;
    24.         }
    25.     }
    26.  
    27.     moveDirection -= slopeAdjust / Time.deltaTime;
    28.    
    29.     var flags = controller.Move(moveDirection * Time.deltaTime);
    30.    
    31.     grounded = (flags  CollisionFlags.CollidedBelow) != 0;
    32.    
    33.    Screen.showCursor = false;
    34. }
    The changes are actually fairly simple. Moving the transform directly with transform.position was causing all sorts of problems, like the character being positioned halfway through meshes and stuff, and was causing all the freezing on slopes. So I decided to go back and try adding it into the moveDirection vector...but the problem was it still wasn't quite working right, he wouldn't 'snap' to the bottom. Then I figured out it was because I was multiplying moveDirection by deltaTime, so I divided the slopeAdjust by deltaTime (super algebra skills)

    whew. Made it sound alot tougher than it was. Thanks for all the help along the way!
     
  11. jcurrie33

    jcurrie33

    Joined:
    Dec 9, 2009
    Posts:
    49
    Sweet! I'll try it out. Thanks!
     
  12. mecha-box

    mecha-box

    Joined:
    Dec 13, 2012
    Posts:
    3
    Thank you very much!! This correction fixed the problem! I'm using the the thrid person tutorial from 3D Buzz and had the same problem. But I would recommend aply the adjustment when CharacterController is grounded.
     
    Last edited: May 7, 2014
  13. wolvz38

    wolvz38

    Joined:
    Feb 6, 2013
    Posts:
    66
    Or you could simply input a negative value for y variable in moveDirection.

    For example:
    Code (csharp):
    1. moveDirection = new Vector3(Input.GetAxis("Horizontal"), -0.75f, Input.GetAxis("Vertical"));
     
    jwinn likes this.