Menu
CoddyTech

Standing on the Ground

Part of the Physics and Gameplay section of Coddy's Unity journey. Lesson 11 of 71.

Every contact has a normal: a direction of length 1, perpendicular to the surface you touched, pointing away from it toward your object. collision.GetContact(0) is the first point of contact, and its normal tells you which side you touched. Standing on flat ground it is (0, 1, 0); against a wall on your right it is (-1, 0, 0); bumping your head on a ceiling it is (0, -1, 0).

This crate tells landings from side bumps:

void OnCollisionEnter(Collision collision)
{
    Vector3 normal = collision.GetContact(0).normal;
    if (normal.y > 0.5f)
    {
        Debug.Log("Landed on " + collision.gameObject.name);
    }
    else
    {
        Debug.Log("Bumped the side of " + collision.gameObject.name);
    }
}

The test is normal.y > 0.5f rather than exactly 1 so that slopes count as ground too: y is 0.5 on a 60 degree slope, steeper than anyone walks up. Two more messages help with anything that lasts: OnCollisionStay runs on every physics step while the contact lasts, and OnCollisionExit once when it ends. An exit has no contact points, since nothing is touching any more, so do not call GetContact there.

challenge icon

Challenge

Easy

Let the player jump only while it stands on something.

  1. Add private bool grounded;. In OnCollisionStay, set it true when collision.GetContact(0).normal.y > 0.5f; in OnCollisionExit, set it false.
  2. In FixedUpdate(), jump only if (jumpPressed && grounded), and set jumpPressed = false; after that if, so a press in the air is dropped.

Press Play and try a double jump: the second press does nothing. Then Check.

Try it yourself

using UnityEngine;

public class Hopper : MonoBehaviour
{
    public float speed = 4f;
    public float jump = 6f;
    private Rigidbody rb;
    private bool jumpPressed;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            jumpPressed = true;
        }
    }

    void FixedUpdate()
    {
        float h = Input.GetAxisRaw("Horizontal");
        rb.linearVelocity = new Vector3(h * speed, rb.linearVelocity.y, 0);
        if (jumpPressed)
        {
            rb.AddForce(Vector3.up * jump, ForceMode.Impulse);
            jumpPressed = false;
        }
    }
}

All lessons in Physics and Gameplay

Practice on your own: Unity playground