How do i check if my rigidbody player is grounded?
Asked Answered
R

11

0

How do i check if my player is grounded? Because its kind of annoying how my player can jump in mid air.

This is the script im using, its in Java Script:

var jumpSpeed: float = 8;

function Update ()
{
    if (Input.GetKeyDown (KeyCode.Space)){
        rigidbody.velocity.y = jumpSpeed;
    }
}
Reg answered 5/1 at 11:5 Comment(1)

@aldo - 3,000+ views on this question in two weeks! Strange isn't it? It's impossible to guess what will be wildly popular!! happy new year!

Flocculus
W
0

You could do a short Raycast in the down direction to check if the ground is there. “short” in this case means the distance from the player pivot to the ground (distToGround); in most cases, collider.bounds.extents.y is this distance (unless collider.bounds.center isn’t 0,0,0). It’s advisable to add a small margin (say, 0.1) to compensate for small ground irregularities or inclination:

var distToGround: float;
  
function Start()
{
  // get the distance to ground
  distToGround = collider.bounds.extents.y;
}
  
function IsGrounded(): boolean
{
  return Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1);
}
  
function Update ()
{
  if (Input.GetKeyDown(KeyCode.Space) && IsGrounded()){
    rigidbody.velocity.y = jumpSpeed;
  }
}
Wickliffe answered 5/1 at 11:6 Comment(5)

thanks that helped me alot!

Reg

thanks that helped me alot too!

Priestley

thanks that helped me alot too too! just wanted to join in :P

Agglutination

you're welcome!

Wickliffe

There were lots of similar questions with hard to follow answers, lots of dead end forum posts.. and there is one answer from Aldo Naletto. Usually i learn something new from his posts. This time it wasnt anything new, just ..use your brain and keep it simple :)

Delogu
C
0

This works for me quiet well.

var isgrounded : boolean = true;
  
function Update()
{
    if(isgrounded == true)
    {
        //Do your action Here...
    }
}
  
//make sure u replace "floor" with your gameobject name.on which player is standing
function OnCollisionEnter(theCollision : Collision)
{
    if(theCollision.gameObject.name == "floor")
    {
        isgrounded = true;
    }
}
  
//consider when character is jumping .. it will exit collision.
function OnCollisionExit(theCollision : Collision)
{
    if(theCollision.gameObject.name == "floor")
    {
        isgrounded = false;
    }
}
Cytoplasm answered 5/1 at 11:7 Comment(5)

This worked for me as well! Been searching for a script that tells unity that my character left the ground. Now I can stop my character from jumping infinitely! Thank you! Now if only I can change the speed at which he goes up and comes down, without sending the character shooting for the stars..

Gristede

I tried this but I found it too unreliable. Even if I set the sleeping mode to "Never sleep" OnCollisionExit sometimes fails to fire for some reason (I think). This causes my player to suddenly learn to fly... My script was a bit modified, though. Instead of a boolean used a counter to keep track of how many objects of the "floor" type the player is colliding with (I have multiple platforms instead of just one "floor").

Topcoat

If you find the OnCollisionExit to be too unreliable, Add "isGrounded = false;" to your jump code as well. So, unless something aside from jumping throws you into the air, you are covered.

Dermatoid

but then if player not stand on a "floor" its not grounded?

Airel

In my game, using the OnCollisionEnter/ OnCollisionExit approach led to an annoying behavior where if the collision detection box for the ground check left the space of one ground object while still in the space of another ground object (imagine the border between two flush rectangular platforms) it would activate the OnCollisionExit method, despite my character being grounded. I am unsure how to avoid this behavior, so I will be using the Physics.CheckCapsule approach instead.

Pray
S
0

Aldo @Wickliffe’s idea’s really good, I’ve been using this in my game for about 2 months.

But there’s one small flaw. If the ground is a bit uneven, it doesn’t really work. “I’ll cast the ray further then” was my first idea, but what if it’s a hole?

What is needed is a capsulecast instead of a raycast:

// collider.bounds are the bounds collider relative to the world. I wanted a 0.1 margin, and 0.18 is the radius of my collider.
Physics.CheckCapsule(
    collider.bounds.center ,
    new Vector3( collider.bounds.center.x , collider.bounds.min.y-0.1f , collider.bounds.center.z ) ,
    0.18f
);
Sumikosumma answered 5/1 at 11:29 Comment(5)

I was gonna go down the route of raycast like the thirdpersoncontroller in standard assets, but you just led me to Physics.CheckBox which is perfect for my case!

Latten

Hi i am also using this for detecting my ground, i have the problem with the uneven ground thing, so how do you solve this? ...

Metalanguage

The way it's presented currently means it would check if there is a collider within the volume between the player's collider and the player's bottom, wouldn't that always be true? Is there a way to get this to ignore the attached collider?

Handling

what if the object is a sphere?

Dixon

@Dixon There is a Physics.CheckSphere() method if you are using a sphere. Please look at the documentation: https://docs.unity3d.com/ScriptReference/Physics.CheckSphere.html @Handling You can provide a layer mask to the method, which will allow you to exclude your player character's collider.

Boots
C
0

Is it possible to get @Wickliffe ´s answer translated into c# ?

I tried to translate it right off but that didn’t help. I am new to JS and C# so please don’t flame me.

I did this but get some errors like:

Operator && cannot be applied to operands of type bool and UnityEngine.Vector3

The best overloaded method match for

UnityEngine.Physics.Raycast(UnityEngine.Vector3, UnityEngine.Vector3, float)

has some invalid arguments

Argument #3 cannot convert double expression to type float

// Jump variable
private float distToGround;

if(jump_key && IsGrounded())
{
	Jump();
}
  
void Start () 
{
	//Get the distance to ground.
	distToGround = collider.bounds.extents.y;
}
  
void Jump()
{
	rigidbody.AddForce(Vector3.up * jumpForce);
	animation.Play("jump_pose");
}
  
void IsGrounded()
{
	return Physics.Raycast(transform.position, - Vector3.up, distToGround + 0.1);
}
Carbineer answered 5/1 at 11:23 Comment(4)

in C#, you can change a double (0.5) into a float by adding f. so you want to change your line return Physics.Raycast(transform.position, - Vector3.up, distToGround + 0.1); into return Physics.Raycast(transform.position, - Vector3.up, distToGround + 0.1f); Also, your "if(jump_key..." isn't in any function or method, so it won't compile either. change it to void Update() { if(jump_key && IsGrounded()) { Jump(); } }

Sumikosumma

Also, your IsGrounded method returns a vector3, it should return a bool. Finally, I'm sorry, but you'll have to stop posting your problems on a non-relative thread. I also advise you to search for your errors before posting on a forum, because you'll lose a lot of time if you ask for help every time you get a cast error.

Sumikosumma

Yeah I figured that out, and you are right. so thats why I deleted the post again. But not fast enough as you were quicker to answer it. Thanks again and sorry mate :D

Carbineer

"I'm sorry, but you'll have to stop posting your problems on a non-relative thread." This is actually the first results when Googling "unity C# check if grounded"; So he's probably not the only person whom was wondering this.

Aerolite
H
0

And how would I go about implementing this in a 2D Character for example, I tried this:

var distToGround: float;
  
function Start()
{
    distToGround = collider2D.bounds.extents.y;
}
  
function IsGrounded(): boolean 
{
    return Physics2D.Raycast(transform.position, -Vector2.up, distToGround + 0.1);
}

But the problem is that I can jump even in the air and it doesn’t seem to detect whether I’m grounded or not.

Handel answered 5/1 at 11:24 Comment(0)
F
0

If your game is relatively simple, you could just check if rigidbody.velocity.y is 0 or very close to 0. While your character is in the air, he will usually be moving up or down. That should serve as a quick and hacky solution.

Of course, there is a moment at the top of every jump when your vertical speed component has no intensity, but it is unlikely to even occur in the engine. Plus, it could always be left there as a clever exploit for hardcore fans to play with. :wink:

Flippant answered 2/8, 2015 at 22:31 Comment(0)
F
0

I have a solution that should work even for detailed bodies like a sphere.

In my game, the player is a sphere. I want that sphere to be able to jump even when the center of the sphere is not in contact with the ground (say, if the ball rests in a hole).

Raycasting will not work, even when using a cone, in instances when this hole is larger than the cone.

The strategy is: create a collider without a mesh renderer, and make that collider a trigger. Then, on contact with the desired types of objects (in my case, I have a layer for “groundable” objects), have that invisible collider raise a “grounded” flag on OnTriggerEnter and lower it on OnTriggerExit.

Make the collider’s position the same as the object, minus a small y offset. In my case, I also diminish the radius of the collider to make sure it only sticks out from the very bottom. A custom mesh collider could be resized in the X/Z axis for similar results.

This effectively ensures that all collisions with the ground are only detected downward from the player since the hit box is only exposed under the object; all other collisions will be protected by the “solid” body of the player.

This technique is the most responsive for me. Just be careful that the collider does not rotate with the ball, though, or else jumping won’t work when the ball is facing up.

Feme answered 12/8, 2015 at 16:19 Comment(0)
S
0

@Reg
This is what i use and it works very nicely. My player is a cube.

var end = new Vector3(CapsuleEnd.position.x, CapsuleEnd.position.y, CapsuleEnd.position.z);
var start = new Vector3(transform.position.x, transform.position.y, transform.position.z);
collisions.onGround = Physics.CheckCapsule(start, end, 0.1f, Ground);

CapsuleEnd is a Transform which is parented to the Player gameobject.

Southernmost answered 5/1 at 11:24 Comment(0)
B
0

I know this post is really old, but for those using C# I found a solution that works for me by adapting the provided javascript code! I’m using a variable to store the boolean, just so I can debug it in the editor. Not using it for anything else, currently.

Here’s my code:

//inside update method
if(currentSpeed < maxSpeed && CheckIfGrounded())
rb.AddForce(tPlayer.forward * acceleration * vmovement, ForceMode.Force);
  
if (Input.GetButtonDown("Jump") && CheckIfGrounded())
{
    Jump();
}
public void Jump()
{
    rb.AddForce(tPlayer.up * jumpModifier);
}

public bool CheckIfGrounded()
{
    grounded = Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1f);
    return grounded;
}
Backside answered 5/1 at 11:26 Comment(0)
M
0

Why don’t use Unity’s OnCollisionStay function?

I’ve used “Raycast down” method like one @Wickliffe suggested but it causes issue when player stand on an edge and the raycast miss the edge.

Understand how OnCollisionStay works

With OnCollisionStay, you can accurately detect the ground. Here is how OnCollisionStay works:

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    void OnCollisionStay(Collision collisionInfo)
    {
        // Debug-draw all contact points and normals
        foreach (ContactPoint contact in collisionInfo.contacts)
        {
            Debug.DrawRay(contact.point, contact.normal, Color.white);
        }
    }
}

Attach the script above to your character (your character has CapsuleCollider component), then run the game, look at your character in Scene widow instead of Game Window (because Debug.DrawRay only works in Scene window), you should see a white ray where the character touches things.

Implement the idea

PlayerCollider.cs

public class PlayerCollider : MonoBehaviour
{
    private bool m_IsOnGround;
  
    public bool IsOnGround
    {
        get
        {
            if (m_IsOnGround)
            {
                m_IsOnGround = false;
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    void OnCollisionStay()
    {
        //If it touch things, then it's on ground, that's my rule
        m_IsOnGround = true;
    }
}
//----------Player.cs------------
public class Player : MonoBehaviour
{
        PlayerCollider m_playerCollider;
        void Start()
        {
            //...
            m_playerCollider = GetComponent<PlayerCollider>();
        }

       void Update()
       {
             if (m_playerCollider.IsOnGround)
             {
                   Debug.Log("On Ground");
              }
              else
              {
                    Debug.Log("In air");
               }
        }
}

Want a working example out of the box?


Check this free asset: Ragdoll and Transition to Mecanim (the time I write this answer, the asset is free, guess it will still be free by the time you check it)

The character controller in the asset above uses OnCollisionStay to detect ground.

Masticatory answered 5/1 at 11:12 Comment(1)

Thank you, it works great (y)

Auricula
O
0

This post was flagged by the community and is temporarily hidden.

Orvie answered 6/1 at 17:42 Comment(1)

Perhaps you should just realize that your knowledge of programming in unity doesn't seem to be up to scratch yet. The bottom line is that there have been a multitude of options that are great solutions on this thread. "none of these are working" is so indescript that I'm sure you just do not understand how they work. Please try to learn more about programming in general and the ins-outs- of RigidBodies through youtube and other great sources. Brackeys on youtube is a great start. Maybe we should close this thread at this point???

Backside

© 2022 - 2024 — McMap. All rights reserved.