- Home /
[2D] Check if player is on the ground
The example script for the 2D game worked for me, however, I noticed a problem. When the player is on the edge of a platform the linecast thinks that the player is not grounded.
Here is the part of the code that deals with ground checking:
grounded = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Ground"));
I suppose that it casts only one line from groundCheck (of type Transform). How do I make it cast more, so it covers the whole bottom part of the player?
Or, if I am wrong, what should I do?
Thanks.
Here is an article I found which explains how to use Physics.OverlapCircle to check if object is grounded. $$anonymous$$aybe it might help someone who is struggling to get this
Answer by JimboFBX · Jan 07, 2014 at 11:54 AM
You could try doing an additional check to see if the user has any velocity in the y direction. If they don't (and presuming you have gravity) then you can be pretty certain they are grounded. I say additional, because if you're walking up or down a hill you wouldn't have zero velocity but you would be grounded.
rigidbody2D.velocity
Not sure of your use case, but you might want to do a rolling average just to prevent any sort of fluke instances where the velocity at the peak of a jump happens to hit exactly zero or something. You'd be a couple frames off but it would prevent this kind of issue. The advantage with this approach is that if you have any sort of complicated animations where the width of the character changes, this pretty much simplifies all that.
Another option is to simply logically OR together 2 or more linecasts that cover the edges of what you want to consider grounded. You could also add two or more objects with trigger colliders and place them as children at the feet of the parent object, although simply adding more line casts sounds like the easiest route since you're already over halfway there.
Checking only velocity is a bad idea. When you are jumping, you're starting with some positive velocity. Then, physics starts to work and velocity decreases. Thus, in the highest point, your velocity will reach 0 value. Thus, even though you are above the ground, you are premitted to jump again which is obviously wrong... Using OverlapCircle
as @Ryanrc suggests is better idea.
Answer by Ryanrc · Jan 05, 2015 at 10:30 AM
bool isGrounded = false;
public Transform GroundCheck1; // Put the prefab of the ground here
public LayerMask groundLayer; // Insert the layer here.
void Update() {
isGrounded = Physics2D.OverlapCircle(GroundCheck1.position, 0.15f, groundLayer); // checks if you are within 0.15 position in the Y of the ground
}
Answer by Acidic9 · Apr 28, 2017 at 05:43 AM
I found a method which I haven't exactly found online yet which seems to be the best for most cases.
It looks like a lot of code, but it's your best answer. It's just because there are a lot of comments in the code and I provided everything you need (including the jump function)
This will still work for a 3D object.
Here is the code:
public float jumpForce = 50000f;
private Rigidbody rb; // Reference to rigidbody
private environmentLayerMask; // Layer for raycast to hit
private float lastDistance; // The last distance between the player and the environment (directly down)
// Use this for initialization
void Awake() {
rb = GetComponent<Rigidbody2D>();
environmentLayerMask = LayerMask.GetMask("Environment");
}
// Update is called once per frame for physics
void FixedUpdate() {
// Check if the user is attempting to jump
float jumpAxis = Input.GetAxisRaw("Jump");
if (jumpAxis != 0 && rb.velocity.y <= 0) {
// Raycast from the feet of the player directly down (or the origin, doesn't matter)
RaycastHit2D hit2D = Physics2D.Raycast(rb.position - new Vector2(0f, 0.5f), Vector2.down, 0.2f, environmentLayerMask);
// If the raycast hit something
if (hit2D) {
// Check if the distance of the object hit is less than the last distance checked
if (hit2D.distance < lastDistance) {
// Update the last distance if the object below is less than the last known distance
lastDistance = hit2D.distance;
} else {
// If the hit distance is not less than the lass distance, then jump (he isn't going to go any lower)
lastDistance = 100f;
Jump(jumpAxis * jumpForce * Time.deltaTime);
}
}
}
}
void Jump(float force) {
if (force < 0) {
return;
}
rb.AddForce(new Vector2(0, force));
}
To simplify the code:
IF player is holding space THEN
IF the distance to the ground below is not decreasing THEN
jump();
ELSE
// the distance to the ground below is still decreasing
update the last known distance to the ground below
END
END
Doesn't Jump for me :/ There were a few things I did change due to errors, however
public float jumpForce = 50000f;
private Rigidbody2D rb; // Reference to rigidbody
private Layer$$anonymous$$ask elm; // Layer for raycast to hit
private float lastDistance; // The last distance between the player and the environment (directly down)
// Use this for initialization
void Awake() {
rb = GetComponent < Rigidbody2D > ();
elm = Layer$$anonymous$$ask.Get$$anonymous$$ask("Environment");
}
// Update is called once per frame for physics
void FixedUpdate() {
// Check if the user is attempting to jump
float jumpAxis = Input.GetAxisRaw("Jump");
if (jumpAxis != 0 && rb.velocity.y <= 0) {
// Raycast from the feet of the player directly down (or the origin, doesn't matter)
RaycastHit2D hit2D = Physics2D.Raycast(rb.position - new Vector2(0f, 0.5f), Vector2.down, 0.2f, elm);
// If the raycast hit something
if (hit2D) {
// Check if the distance of the object hit is less than the last distance checked
if (hit2D.distance < lastDistance) {
// Update the last distance if the object below is less than the last known distance
lastDistance = hit2D.distance;
} else {
// If the hit distance is not less than the lass distance, then jump (he isn't going to go any lower)
lastDistance = 100f;
Jump(jumpAxis * jumpForce * Time.deltaTime);
}
}
}
}
void Jump(float force) {
if (force < 0) {
return;
}
rb.AddForce(new Vector2(0, force));
}
Answer by lovefilm · Jan 05, 2015 at 10:17 AM
function OnCollisionEnter2D(coll: Collision2D) { if (coll.gameObject.tag == "Ground") { Debug.Log(coll.gameObject.name); blnCollisionGround= true;
} }
Use OnCollisionEnter2D, and then at command jump, give condition blnCollisionGround:
if (Input.GetButtonDown("Jump") && blnCollisionGround) { velocity.y= runJump; blnCollisionGround= false;
}