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. Dismiss Notice

Rigidbody jump script collision help.

Discussion in 'Scripting' started by wesan, Jan 3, 2015.

  1. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    Hello. Lately I was searching for perfect rigidbody collision script. What I mean by that is something to check where character is touching the ground and switch grounded properly. I have found many ideas with tags on the floor, which is no go since tagging everything will be kinda chaotic, second idea was to check for velocity but it doesn't work. So I kept digging myself and well, I've found out about collision flags but I don't know how to apply them properly.

    Code (CSharp):
    1.     void OnCollisionStay(Collision collisionInfo)
    2.     {
    3.         if ( CollisionFlags.CollidedBelow != 0)
    4.         {
    5.             grounded = true;
    6.             anim.SetBool ("Grounded", true);
    7.         }
    8.     }
    It's simply not working properly and I still get ground toggled back on even if touching side which makes my jump repeatable on a wall launching me off like rocket.
     
  2. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    No ideas? Does someone have atleast some other idea which will work and check collisions?
     
  3. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    For mine, I do a SphereCast in FixedUpdate. Only reliable way to get the normal of the ground as well, it seems.

    Like this:
    Code (CSharp):
    1. Vector3 raycastOffset;
    2.  
    3. void Awake()
    4. {
    5.     raycastOffset = new Vector3(collider.center.x, collider.center.y - (collider.height * 0.5f) + collider.radius + 0.05f, collider.center.z);
    6. }
    7.  
    8. void FixedUpdate()
    9. {
    10.     RaycastHit hit;
    11.     if (Physics.SphereCast(transform.position + raycastOffset, collider.radius, Vector3.down, out hit, Extra + 0.05f) && GroundAngle(hit.normal) <= 45.0f)
    12.     {
    13.         grounded = true;
    14.         groundNormal = hit.normal;
    15.     }
    16. }
    I temporarily calculate the raycast offset so I don't have to do it every frame. The "Extra" variable is a float which is extra distance beneath your character's feet which will register the player as grounded (so little bumps won't cause missed jumps and such). Set it to about 0.2.

    Also note that the maximum angle the player can climb is hard coded at 45 in this particular example. You can change that if you wish.

    Edit (yet again, sorry): I forgot to include my optimized GroundAngle function...
    Code (CSharp):
    1. private float GroundAngle(Vector3 normal)
    2. {
    3.     return (float)System.Math.Acos((double)normal.y) * Mathf.Rad2Deg;
    4. }
    Note it will malfunction if the normal's length is greater than 1. Thankfully RaycastHit will always return a such a normal.
     
    Last edited: Jan 3, 2015
    wesan likes this.
  4. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    Thank you for your answer. Can I see some example?
     
  5. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Just edited my post with one ;)
     
    wesan likes this.
  6. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    I see. So you're using Box collider yes?
    I'm asking because I'm getting errors of such values like collider.center.x, collider.center.y
     
  7. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Sorry, no. I am using a Capsule collider. I added this to my code to hide the Unity property.

    Code (CSharp):
    1. new private CapsuleCollider collider;
    2.  
    3. void Awake()
    4. {
    5.     this.collider = (CapsuleCollider)base.collider;
    6. }
    This isn't necessary -- just convenient. You can simply replace anywhere I use "collider" with your own CapsuleCollider variable.
     
    wesan likes this.
  8. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    I'm using capsule collider myself. The problem is that...:

    Code (CSharp):
    1.         raycastOffset = new Vector3(collider.center.x, collider.center.y - (collider.height * 0.5f) + collider.radius + 0.05f, collider.center.z);
    Is...:


    ...Saying that it does not contain definition for "Red thingy"

    Ofcourse your code from post above solved it. :)
     
  9. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    The system works fine, however I am experiencing two problems. One is that When I run towards an object, be it cube for example I can't Jump onto it. The second one is That holding space makes character to continue jump. Any ideas for changes?

    You don't even know how thankfull I am for your response. I've spent 20+ hours on searching for proper script like yours.
     
  10. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    When you say you can't jump onto the cube, do you mean when you land on it, you aren't grounded on it? And for the continuing to jump problem, I'd have to see some relevant code to figure out why.
     
    wesan likes this.
  11. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    Oh I've forgot to paste the code:

    Code (CSharp):
    1.         if(Input.GetButton("Jump") && grounded == true)
    2.         {
    3.             grounded = false;
    4.             anim.SetBool ("Grounded", false);
    5.             rigidbody.AddForce(0, JumpForce, 0);
    6.         }
    The running towards a wall disallows jumping. When I run towards and press space, nothing happens. Yet when I do stand still and jump it works.

    Video:
     
  12. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Hmm that's a very interesting problem. Did you make any changes to the code I posted? If not, I am assuming the problem is the spherecast is detecting the wall and seeing the angle is greater than the maximum slope, and registering as ungrounded. This is odd, since in my game, this does not happen. Try this for the SphereCast:

    Code (CSharp):
    1. if (Physics.SphereCast(transform.position + raycastOffset, collider.radius * 0.9f, Vector3.down, out hit, Extra + 0.05f) && GroundAngle(hit.normal) <= 45.0f)
    The only difference in this line of code is "collider.radius * 0.9f" for the sphere's radius. This will hopefully prevent walls from causing issues.
     
    wesan likes this.
  13. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    I can't notice any difference in reaction of character. I think the problem must be somewhere else. I've done some research and what I've noticed is that changing material of rock to ice slightly fixes it. So it might be something with friction and gravity. Adittional bug I've found is that when you slide down without jumping grounded doesn't toggle off (duh). So obvious fix was adding else in check:

    Code (CSharp):
    1.         RaycastHit hit;
    2.         if (Physics.SphereCast (transform.position + raycastOffset, collider.radius * 0.9f, Vector3.down, out hit, Extra + 0.05f) && GroundAngle (hit.normal) <= 45.0f) {
    3.             grounded = true;
    4.             anim.SetBool ("Grounded", true);
    5.             //groundNormal = hit.normal;
    6.  
    7.         } else {
    8.             grounded = false;
    9.             anim.SetBool ("Grounded", false);
    10.         }
     
  14. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    For Rigidbody player controllers, you generally want zero friction and zero bounciness, with both set to minimum combine. See if that helps.

    And yeah, you want an else there with that 'if' statement. That's how I have it set up in my rigidbody player controller.
     
    wesan likes this.
  15. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    I see. Thanks alot and what about jump changes to make holding space not to jump all the time?
     
  16. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    You're using GetButton which returns a boolean containing the state of the button. If you hold the button down, it will continue to jump because the state is always returning true. If you use GetButtonDown, it will return true ONLY the frame in which the button is pressed. The problem is, FixedUpdate does NOT sync with normal frames, so you will miss a lot of jumps. What you want to do is check if the user is jumping with GetButtonDown in the Update() method, then set a boolean to true. We'll call this boolean variable doJump. So you set that to true if GetButtonDown("Jump"). Then in FixedUpdate(), check if doJump is true. If so, set doJump to false. Then check if grounded. If so, apply jump forces. Something like this:

    Code (CSharp):
    1. bool doJump = false;
    2.  
    3. void Update()
    4. {
    5.     if (Input.GetButtonDown("Jump"))
    6.         doJump = true;
    7. }
    8.  
    9. void FixedUpdate()
    10. {
    11.     if (doJump)
    12.     {
    13.         doJump = false;
    14.  
    15.         if (grounded)
    16.         {
    17.             // jump here
    18.         }
    19.     }
    20. }
    Please note I just typed this in and may have made typos! But this is the general idea of what you want to do. The reason I set doJump to false even if we are not grounded (and thus unable to jump) is to prevent arming a jump while you're mid-air and then jumping as soon as you are grounded.
     
    wesan likes this.
  17. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    Using
    Input.GetButtonDown("Jump") does nothing. No jump is happening. Changing this to GetButton makes it work like previously.

    Also, how should I apply zero friction and zero bounciness adding material to capsule with everything on zero doesn't change a thing.
     
  18. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Are you moving GetButtonDown into the Update() function instead of the FixedUpdate(), as per my above example?

    I'm not sure if I understand your second sentence. If you are having trouble creating the PhysicMaterial, you can create it programmatically in the Awake() function like this:

    Code (CSharp):
    1. void Awake()
    2. {
    3.     PhysicMaterial controllerMat = new PhysicMaterial();
    4.     controllerMat.bounciness = 0.0f;
    5.     controllerMat.dynamicFriction = 0.0f;
    6.     controllerMat.staticFriction = 0.0f;
    7.     controllerMat.bounceCombine = PhysicMaterialCombine.Minimum;
    8.     controllerMat.frictionCombine = PhysicMaterialCombine.Minimum;
    9.     collider.material = controllerMat;
    10. }
    You don't need to touch the PhysicMaterial on any objects then. This will do it for you.
     
  19. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    I'm pretty sure:

    Code (CSharp):
    1.  
    2.     // Update is called once per frame
    3.     void Update () {
    4.         if (Input.GetButtonDown("Jump")) {
    5.             doJump = true;
    6.         }      
    7.     }
    8.  
    9.     void FixedUpdate ()
    10.     {
    11.         // Cache the inputs.
    12.         float h = Input.GetAxis("Horizontal");
    13.         float v = Input.GetAxis("Vertical");
    14.         bool sneak = Input.GetButton("Sneak");
    15.         bool jump = Input.GetButton("Jump");
    16.  
    17.         if (doJump)
    18.         {
    19.             doJump = false;
    20.          
    21.             if (grounded)
    22.             {
    23.                 grounded = false;
    24.                 anim.SetBool ("Grounded", false);
    25.                 rigidbody.AddForce(0, JumpForce, 0);
    26.             }
    27.         }
    28.  
    29.         RaycastHit hit;
    30.         if (Physics.SphereCast (transform.position + raycastOffset, collider.radius * 0.9f, Vector3.down, out hit, Extra + 0.05f) && GroundAngle (hit.normal) <= 45.0f) {
    31.             grounded = true;
    32.             anim.SetBool ("Grounded", true);
    33.             //groundNormal = hit.normal;
    34.  
    35.         } else {
    36.             grounded = false;
    37.             anim.SetBool ("Grounded", false);
    38.         }
    39.         MovementManagement(h, v, sneak, jump);
    40.     }
    41.  
    About the second: I meant that adding material didn't solve the problem. But now after I've added info to awake the hero is like icecube, permamently sliding.
     
  20. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Is JumpForce perhaps too low? Try setting it to something absurdly high to see if it works.

    The reason the player slides without friction is probably because of the way you are applying movement. You are probably adding acceleration forces, but not using deceleration forces to slow the player down and instead relying on friction. The problem with that is friction is applied to every part of the player, not just their feet. So jumping against a wall will cause friction and perhaps limit jumping. Rigidbody character controllers are really tough to create, which I'm sure you're beginning to realize :p. I feel like they are really great though once you get them working correctly.

    Despite the undesired sliding behavior, have you tried jumping against a wall to see if the physic material solved that issue? If it did not solve it, then friction is not the issue and something else is. If it did solve it, then we need to implement deceleration forces and not use friction anymore.
     
  21. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    Yes! Adjusting jumpforce helped... One step closer...

    Yes despite sliding the jumping near the wall worked properly ;)
     
  22. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Ok, great to hear! So unless I'm forgetting something, sliding is the only problem you're experience right now. Well, this subject is slightly complicated, so I'm just going to copy/paste some of the code from my PlayerController here (with a few slight modifications, so mind my typos). A mathematician might laugh at my process, but it works, and is pretty efficient. So here it is:

    Code (CSharp):
    1. // align our movement vectors with the ground normal (ground normal = up)
    2. Vector3 targetSpeed = Vector3.Cross(groundNormal, transform.forward) * (Input.GetAxis("Horizontal") * MoveSpeed) + Vector3.Cross(transform.right, groundNormal) * (Input.GetAxis("Vertical") * MoveSpeed);
    3.  
    4. length = targetSpeed.magnitude;
    5. float difference = length - rigidbody.velocity.magnitude;
    6.  
    7. // avoid divide by zero
    8. if (Mathf.Approximately(difference, 0.0f))
    9.     movement = Vector3.zero;
    10.  
    11. else
    12. {
    13.     float acceleration;
    14.  
    15.     // determine if we should accelerate or decelerate
    16.     if (difference > 0.0f)
    17.         acceleration = Mathf.Min(AccelRate * Time.deltaTime, difference);
    18.     else
    19.         acceleration = Mathf.Max(-DecelRate * Time.deltaTime, difference);
    20.  
    21.     difference = 1.0f / difference * acceleration;
    22.     movement = new Vector3((targetSpeed.x - rigidbody.velocity.x) * difference, (targetSpeed.y - rigidbody.velocity.y) * difference, (targetSpeed.z - rigidbody.velocity.z) * difference);
    23. }
    24.  
    25. rigidbody.AddForce(movement, ForceMode.VelocityChange);
    You will need to add a few new public variables or rename the ones in my code to match yours:

    AccelRate (float)
    DecelRate(float)
    MoveSpeed (float)

    You will also need groundNormal from the SphereCast. Just set groundNormal = hit.normal.
     
    Last edited: Jan 5, 2015
  23. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    Thank you again for helping me so much, but the problem stays the same, maybe I did add the code in wrong place or maybe we're talkin about other action. So to be clear here's the video and code:



    http://pastebin.com/gHp0uxVq

    As you can see, after I enter at tilted object I begin to slide, forever.
     
  24. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    That's odd. Was the problem the exact same before you added my code? Perhaps AccelRate, DecelRate, and/or MoveSpeed are set to zero?

    Also, I noticed you are moving the player in Rotating() with this line: "rigidbody.transform.Translate(Vector3.forward* BaseSpeed);"

    This line is odd because it doesn't use transform.forward, but rather Vector3.forward... try removing that line. It would explain why you move in a constant direction, but not why it only happens after you land on the slope.
     
  25. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    public float AccelRate = 5.0f;
    public float DecelRate = 5.0f;
    public float MoveSpeed = 5.0f;
    Made changes and also checked movement with different numbers on them. After I've removed the line you've asked me to character doesn't move as it shouldn't and there is no difference while changing values of Rates and MoveSpeed.

    I feel like I need to thank you again for your awesome help!
     
  26. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Hmm it seems as though you are moving the character with the animation and not physics. If you remove the movement code I gave you, I'll bet your character will still move. If so, then the movement is done through the animation. If you do that, is the behavior desired? I'm not understanding if the sliding issue is still present.

    And no problem! I'm glad to help out. I love making Player Controllers, so I just take a break every now and then from working on mine to visit this thread ;).
     
  27. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    No. I have disabled animation movement long ago. It wasn't something I was searching for.

    Glad to know. But something tells me we will talk for a loooong time!
     
  28. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    So the player is still currently sliding?
     
  29. arthur_fukushima

    arthur_fukushima

    Joined:
    Nov 12, 2014
    Posts:
    28
  30. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    Yes. It's sliding after entering not flat surface it slides off and continues sliding.
     
  31. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Hmm, the problem isn't apparent to me. Is it possible for you to recreate your project without any proprietary assets (never trust anyone on the internet ;)) and just the minimum scripts and a sample scene to reproduce the problem? If so, send it here and I'd be glad to have a go at debugging it. It's really tough to debug without a bunch of shots in the dark, and I feel that could take a while without the ability to adjust the code myself.
     
  32. wesan

    wesan

    Joined:
    Dec 22, 2014
    Posts:
    18
    Check PM please.