Best way to handle jumps in 3D environment?

What’s the best way to handle jumping in a 3D environment? I’m used to making 2D Flash games where you would use a bool like isGrounded to check where the player is on the ground and if so allow that player to jump otherwise it can’t.

You see my friend sent me an empty room he modeled and I’m using a mesh collider for it. The player has a capsule collider. I don’t know how to test if the player is on the “ground” of the room. Additionally, what if the player jumped and then landed on a box in the room - how would it know it is on the “ground” and able to jump again.

As you can see I’m having trouble wrapping my head around all of this. I’m open to suggestions.

Changed up my google search keywords and found this:

Is this a way you guys would recommend?

That solution is fine but you’ll want to test the ray intersecting object to make sure it’s something you consider “ground”. You don’t want your raycast to collide with say, a coin or enemy, and allow the player to jump again (necessarily).

1 Like

Not sure if this is the best advice but you can also check if rigidbody.velocity.y is zero (or very close to zero). If it is, you’re probably grounded. Either that or you’re at the apex of a jump… so you would need to check for that too.

Ah yeah that makes sense. I tried implementing that example (it was JS but fixed it for C#) and this part:

    public bool IsGrounded()
    {
        return Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1);
    }

Is throwing me 2 errors:

and

I don’t quite understand them.

For the record I’m not the kind of person that just copy/pastes code to build a game. I use existing examples to understand how new code works so that I can write my own and modify it as I see fit with my new knowledge. Just felt the need to say this as it bugs me when beginners constantly ask questions about code they stole from somewhere yet give no effort to try and understand it haha…

I think you’re just missing an f on 0.1.

That is: 0.1f

C# is more strict than JavaScript. :slight_smile:

2 Likes

Good eye good sir :slight_smile: That cleared all of the errors…so simple lol Player still won’t jump but I’m sure I can figure it out with enough tinkering now that the errors are gone. Thank you :smile:

One last simple question if you don’t mind… I simply want to be able to do Debug.Log (IsGrounded); somewhere in the update so I can check it in the console while I test things out. I get the error:

I kind of understand it but not really :confused:

Debug.Log(IsGrounded())

1 Like

Of course…lol Thank you. The distToGround is reading 2.25 because I am using a capsule collider (I know cuz I switched it to a box and it registered 0). Either way it seems that IsGrounded always reads false even when I got it to jump by manipulating the conditions for the jump. Oh well. You guys have helped me enough. I’ll figure it out eventually.

Thanks again you guys for all of the help :slight_smile:

Obvious question: Does your room model have a collider attached to it? Does it work when you put the player over a default 3D Cube GameObject?

If not, post code.

1 Like

Currently the player has a capsule collider and the room has a mesh collider (Not Convex). I’ll go ahead and post the code anyways should you feel the need to ponder over it:

using UnityEngine;
using System.Collections;

public class ControllerTest : MonoBehaviour {
  
    public float speed = 10f;
    public float strafeSpeed = 10f;
    public float jumpSpeed = 100f;
    public Vector3 direction;
    public Vector3 sidestrafe;

    public float distToGround;
  

    void Start ()
    {
        direction = SmoothMouseLook.forwardMove;
        sidestrafe = SmoothMouseLook.sideMove;
        direction.y = 0f;

        // get the distance to ground
        distToGround = GetComponent<Collider>().bounds.extents.y;
    }
  
    void Update()
    {
        direction = SmoothMouseLook.forwardMove;
        sidestrafe = SmoothMouseLook.sideMove;
        direction.y = 0f;

        if ( (Input.GetAxis("Vertical") != 0) || (Input.GetAxis("Horizontal") != 0) )
        {
            transform.position += (direction*speed)*Input.GetAxis("Vertical") * Time.fixedDeltaTime;
            transform.position += (sidestrafe*speed)*Input.GetAxis("Horizontal") * Time.fixedDeltaTime;
        }

        if (Input.GetKeyDown(KeyCode.Space) && IsGrounded())
        {
            this.GetComponent<Rigidbody>().AddForce (Vector3.up * jumpSpeed);
        }
          
        Debug.Log (IsGrounded());
    }

    public bool IsGrounded()
    {
        return Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1f);
    }

}

It seems that distToGround always reads 2.25 with the capsule collider or 0 if I switched the collider to a box collider. I must not understand bounds.extents which I’m guessing is why these two colliders have different values.
IsGrounded always returns false.
I changed the jump condition if (Input.GetKeyDown(KeyCode.Space) && IsGrounded())
to if (Input.GetKeyDown(KeyCode.Space) && !IsGrounded()) just to get the player off the ground for debugging purposes and things remain the same:
distToGround doesn’t change and IsGrounded still stays false;

Unfortunately, I’m not able to duplicate your issue. Your (slightly gutted) code works fine for me:

distToGround shouldn’t be changing ever - it’s calculated once on Start() and not touched again, so no reason to be surprised about that.

Have you tried changing your scenery to a cube and see if that works as a test? It could be something with the mesh collider but I’ve never used them, so I couldn’t tell you exactly.

Also try changing the 0.1f to something larger like 5.0f just for testing.

1 Like

Changing the 0.1f did it. I tried 5f for testing purposes and it allowed me to multiple jump for to a specific height which I totally understand why.

I set it to 0.5f and sometimes IsGrounded would stay false and other times it would let him jump once and then get stuck at false. So I did 0.75f and it seems to be working smoothly. If I set it higher, like 1f, I noticed that you could tap the jump button just before landing and it would hiccup his decent.

Because I do want to understand all of the code I’m working with could you elaborate on this line:

distToGround = GetComponent<Collider>().bounds.extents.y;

I understand everything up until the bounds.extents.y. I looked up the documentation for it but it’s rather vague. In theory it seems like some kind of raycast to measure distance only I know it’s not a raycast…

Thank you for everything dude. I kinda feel bad you went through all that effort to test my code but I do greatly appreciate it.

The bounds of an object is an axis-aligned (not rotated) box that encases it. So if you had a sphere with a diameter of 1 unit, the bounds would be a 1 unit cube. The extents of that bounds is a Vector3 representing the distance from the centre of the box to the edge (x = half the width, y = half the height, z = half the depth). So the code from that Unity Answers post projects a ray with a length of half of the height of the collider + a little bit extra.

No reason to feel bad - pasting code into a test project takes ten seconds. Happy to help.