[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.

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
}

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

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.

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;

}