Rigidbody jump script collision help.

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.

    void OnCollisionStay(Collision collisionInfo)
    {
        if ( CollisionFlags.CollidedBelow != 0)
        {
            grounded = true;
            anim.SetBool ("Grounded", true);
        }
    }

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.

No ideas? Does someone have atleast some other idea which will work and check collisions?

For mine, I do a SphereCast in FixedUpdate. Only reliable way to get the normal of the ground as well, it seems.

Like this:

Vector3 raycastOffset;

void Awake()
{
    raycastOffset = new Vector3(collider.center.x, collider.center.y - (collider.height * 0.5f) + collider.radius + 0.05f, collider.center.z);
}

void FixedUpdate()
{
    RaycastHit hit;
    if (Physics.SphereCast(transform.position + raycastOffset, collider.radius, Vector3.down, out hit, Extra + 0.05f) && GroundAngle(hit.normal) <= 45.0f)
    {
        grounded = true;
        groundNormal = hit.normal;
    }
}

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…

private float GroundAngle(Vector3 normal)
{
    return (float)System.Math.Acos((double)normal.y) * Mathf.Rad2Deg;
}

Note it will malfunction if the normal’s length is greater than 1. Thankfully RaycastHit will always return a such a normal.

1 Like

Thank you for your answer. Can I see some example?

Just edited my post with one :wink:

1 Like

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

Sorry, no. I am using a Capsule collider. I added this to my code to hide the Unity property.

new private CapsuleCollider collider;

void Awake()
{
    this.collider = (CapsuleCollider)base.collider;
}

This isn’t necessary – just convenient. You can simply replace anywhere I use “collider” with your own CapsuleCollider variable.

1 Like

I’m using capsule collider myself. The problem is that…:

        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. :slight_smile:

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.

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.

1 Like

Oh I’ve forgot to paste the code:

        if(Input.GetButton("Jump") && grounded == true)
        {
            grounded = false;
            anim.SetBool ("Grounded", false);
            rigidbody.AddForce(0, JumpForce, 0);
        }

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:

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:

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.

1 Like

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:

        RaycastHit hit;
        if (Physics.SphereCast (transform.position + raycastOffset, collider.radius * 0.9f, Vector3.down, out hit, Extra + 0.05f) && GroundAngle (hit.normal) <= 45.0f) {
            grounded = true;
            anim.SetBool ("Grounded", true);
            //groundNormal = hit.normal;

        } else {
            grounded = false;
            anim.SetBool ("Grounded", false);
        }

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.

1 Like

I see. Thanks alot and what about jump changes to make holding space not to jump all the time?

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:

bool doJump = false;

void Update()
{
    if (Input.GetButtonDown("Jump"))
        doJump = true;
}

void FixedUpdate()
{
    if (doJump)
    {
        doJump = false;

        if (grounded)
        {
            // jump here
        }
    }
}

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.

1 Like

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.

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:

void Awake()
{
    PhysicMaterial controllerMat = new PhysicMaterial();
    controllerMat.bounciness = 0.0f;
    controllerMat.dynamicFriction = 0.0f;
    controllerMat.staticFriction = 0.0f;
    controllerMat.bounceCombine = PhysicMaterialCombine.Minimum;
    controllerMat.frictionCombine = PhysicMaterialCombine.Minimum;
    collider.material = controllerMat;
}

You don’t need to touch the PhysicMaterial on any objects then. This will do it for you.

I’m pretty sure:

    // Update is called once per frame
    void Update () {
        if (Input.GetButtonDown("Jump")) {
            doJump = true;
        }      
    }
  
    void FixedUpdate ()
    {
        // Cache the inputs.
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        bool sneak = Input.GetButton("Sneak");
        bool jump = Input.GetButton("Jump");

        if (doJump)
        {
            doJump = false;
          
            if (grounded)
            {
                grounded = false;
                anim.SetBool ("Grounded", false);
                rigidbody.AddForce(0, JumpForce, 0);
            }
        }

        RaycastHit hit;
        if (Physics.SphereCast (transform.position + raycastOffset, collider.radius * 0.9f, Vector3.down, out hit, Extra + 0.05f) && GroundAngle (hit.normal) <= 45.0f) {
            grounded = true;
            anim.SetBool ("Grounded", true);
            //groundNormal = hit.normal;

        } else {
            grounded = false;
            anim.SetBool ("Grounded", false);
        }
        MovementManagement(h, v, sneak, jump);
    }

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.

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.