Skip to main content

πŸ“ Lesson 2.2: Physics and Collisions

Our cube from Lesson 2.1 walks through walls. This lesson gives objects physical presence: a Rigidbody so they fall and get pushed, Colliders so they bump, and the OnCollision/OnTrigger callbacks that turn a touch into gameplay β€” a coin collected, a hazard triggered.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Explain what a Rigidbody and a Collider each contribute to physics
  • Move a physics object with forces, linearVelocity, or MovePosition β€” in FixedUpdate
  • Distinguish solid collisions from triggers (isTrigger)
  • Handle OnCollisionEnter and OnTriggerEnter with the right parameter types
  • State the rule for which objects need a Rigidbody for callbacks to fire
  • Use isKinematic and understand tags/layers for collision logic

Estimated Time: 60 minutes

Project: A rolling ball that pushes crates (solid collisions) and collects coins that vanish on touch (triggers).

In This Lesson

Rigidbody + Collider

Physics in Unity is a partnership between two components. Neither does the job alone:

ComponentProvidesWithout it…
RigidbodyMakes the object simulated: gravity, mass, velocity, forces. The physics engine now moves it.The object ignores gravity and forces β€” it only moves if you script its Transform.
ColliderDefines the object's shape for contact (Box, Sphere, Capsule, Mesh). This is what actually touches things.Objects pass straight through each other β€” nothing to collide with.

πŸ“– Definition

A Rigidbody hands control of an object's movement to Unity's physics engine. A Collider is an invisible shape that detects contact. A solid, pushable object needs both; a static wall needs only a Collider (the floor and walls don't move, so they don't need a Rigidbody).

graph LR RB["Rigidbody
(mass, gravity, velocity)"] --> SIM["Physics engine
simulates this object"] COL["Collider
(the contact shape)"] --> SIM SIM --> R["Falls, collides,
gets pushed, fires callbacks"] style RB fill:#eff6ff,stroke:#3b82f6,stroke-width:2px style COL fill:#e8f5e9,stroke:#4CAF50,stroke-width:2px style R fill:#f3e8ff,stroke:#8b5cf6,stroke-width:2px

Add a Rigidbody to a cube above a floor, press Play, and it falls and lands β€” no code required. Primitive objects (Cube, Sphere) already come with a matching Collider; empty objects and imported models you add one to yourself.

Moving with Physics

Lesson 2.1 warned: don't set the Transform of a physics object β€” it teleports past colliders. Instead you tell the Rigidbody what to do, and it moves the object through the world, respecting collisions. There are three main ways, and all belong in FixedUpdate (the physics step, Lesson 1.2).

TechniqueWhat it doesBest for
AddForce(...)Applies a push; acceleration builds up momentum.Rockets, jumps, explosions β€” anything with inertia.
linearVelocity = ...Sets the velocity directly (instant, precise speed).Arcade characters that need exact control.
MovePosition(...)Moves toward a target position with collision response.Kinematic movers, platforms, precise stepping.
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class BallController : MonoBehaviour
{
    [SerializeField] private float moveForce = 10f;
    private Rigidbody body;
    private Vector2 input;

    void Awake() => body = GetComponent<Rigidbody>();

    void Update()
    {
        // Read input every frame (Lesson 1.2 rule)…
        input.x = Input.GetAxisRaw("Horizontal");
        input.y = Input.GetAxisRaw("Vertical");
    }

    void FixedUpdate()
    {
        // …but APPLY physics on the fixed step.
        Vector3 force = new Vector3(input.x, 0f, input.y) * moveForce;
        body.AddForce(force);
    }
}

⚠️ Unity 6 name changes

In Unity 6 the Rigidbody property velocity was renamed linearVelocity, and drag / angularDrag became linearDamping / angularDamping. Older tutorials use the old names β€” they still compile (marked obsolete) but prefer the new ones. This course targets Unity 6.

πŸ’‘ Read in Update, apply in FixedUpdate. Notice the split above: input is polled in Update (so no press is missed) and stored, then the stored value drives the physics in FixedUpdate. This is the pattern promised back in Lesson 1.2, now in action.

Collision Callbacks

When two solid colliders touch, Unity calls event functions on both objects' scripts β€” just like the lifecycle messages from Lesson 1.2, but triggered by contact. The three collision messages:

CallbackFires when
OnCollisionEnter(Collision c)The moment two colliders first touch.
OnCollisionStay(Collision c)Each physics step they remain in contact.
OnCollisionExit(Collision c)The moment they separate.
public class Crate : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        // 'collision' describes WHAT we hit and HOW.
        Debug.Log($"Hit by {collision.gameObject.name}");

        // Contact details are available too:
        float impact = collision.relativeVelocity.magnitude;
        if (impact > 5f)
            Debug.Log("That was a hard hit!");
    }
}

πŸ’‘ The Collision object

OnCollisionEnter receives a Collision β€” rich data about the contact: collision.gameObject (what you hit), collision.relativeVelocity (impact speed), and collision.contacts (exact contact points/normals). Note it's collision.gameObject, not the collision itself, that gives you the other object.

Triggers: Detect Without Blocking

Sometimes you want to know something entered an area without physically stopping it β€” a coin to collect, a checkpoint, a damage zone. That's a trigger: tick Is Trigger on the collider, and it stops being solid but still reports overlaps through a separate set of callbacks.

Solid collisionTrigger
Objects physically block each otherObjects pass through
OnCollisionEnter(Collision)OnTriggerEnter(Collider)
Parameter: Collision (rich contact data)Parameter: Collider (just the other collider)
Crates, walls, the player's bodyCoins, checkpoints, damage/pickup zones
public class Coin : MonoBehaviour
{
    // Note: OnTriggerEnter takes a Collider, NOT a Collision.
    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            Debug.Log("Coin collected!");
            Destroy(gameObject);   // remove the coin (Lesson 1.1)
        }
    }
}

⚠️ Two easy mistakes to mix up

(1) Wrong parameter type: OnTriggerEnter takes a Collider; OnCollisionEnter takes a Collision. Swap them and β€” because these are name-matched (Lesson 1.2) β€” your method simply never fires, with no error. (2) Use CompareTag("Player"), not other.tag == "Player": CompareTag is faster and avoids a string allocation.

πŸ’‘ Triggers also have Stay/Exit: OnTriggerStay(Collider) and OnTriggerExit(Collider) mirror the collision versions β€” great for "while inside the damage zone" or "left the safe area."

Who Needs a Rigidbody?

Here's the rule that trips up nearly every beginner: for collision/trigger callbacks to fire, at least one of the two objects in the contact must have a Rigidbody. Two purely static colliders touching produce no callbacks β€” the physics engine isn't simulating either of them.

graph TD A["Coin: Collider (Is Trigger)
NO Rigidbody"] B["Player: Collider + Rigidbody"] B -->|"walks into"| A A -->|"βœ… fires OnTriggerEnter
(because Player has a Rigidbody)"| OK["Callback runs"] style B fill:#eff6ff,stroke:#3b82f6,stroke-width:2px style OK fill:#e8f5e9,stroke:#4CAF50,stroke-width:2px

In the example above, the coin has no Rigidbody, yet the trigger still fires β€” because the player that walked into it does. That's the usual arrangement: the moving thing (player, projectile) carries the Rigidbody; static pickups and zones just have a trigger collider.

πŸ“– Definition: kinematic Rigidbody

Set isKinematic = true (or tick Is Kinematic) and the Rigidbody stops responding to forces and gravity β€” you move it via MovePosition, but it still participates in collisions and, crucially, satisfies the "needs a Rigidbody" rule. Perfect for moving platforms, elevators, and script-driven doors that should still trigger and block.

βœ… Quick decision guide

  • Moves under physics (falls, gets pushed): Rigidbody, not kinematic.
  • Moves by script but must collide/trigger: kinematic Rigidbody + MovePosition.
  • Never moves (walls, floor): Collider only, no Rigidbody.
  • A detection zone: Collider with Is Trigger; give the other party the Rigidbody.

Reacting to the Right Objects

A trigger fires for anything that enters it. Usually you only care about specific things β€” a coin should react to the player, not to a stray crate. Two tools filter contacts:

Tags β€” identify individual objects

A tag is a label you assign in the Inspector ("Player", "Enemy", "Coin"). Check it with CompareTag:

void OnTriggerEnter(Collider other)
{
    if (!other.CompareTag("Player")) return;   // ignore everything else
    Collect();
}

Layers β€” group objects for the physics engine

A layer groups objects so physics can decide, via the Layer Collision Matrix (Project Settings β†’ Physics), which layers even interact. Put projectiles on a "PlayerBullets" layer that only collides with "Enemies," and enemy-to-enemy or bullet-to-bullet contacts are skipped entirely β€” cheaper and simpler than filtering in code.

TagLayer
PurposeIdentify what an object is, in codeControl which groups collide, in the engine
Checked byCompareTag(...) in a callbackThe Layer Collision Matrix (before callbacks)
Use for"Is this the player?""Should bullets ignore other bullets?"
πŸ’‘ Rule of thumb: use tags to identify a specific object inside a callback, and layers to stop unwanted collisions from happening at all. Filtering with layers is more efficient because those contacts are never even generated.

Exercise & Quiz

πŸ‹οΈ Exercise: Roll, Push, and Collect

Objective: Build a physics ball that pushes crates (solid) and collects coins (trigger) β€” using both callback families.

Instructions:

  1. Make a Plane floor. Add a Sphere named Ball with a Rigidbody; tag it Player.
  2. Write BallController (see Section 2) to roll it with AddForce in FixedUpdate.
  3. Add a few Cubes as Crates with Rigidbodies β€” the ball should shove them on contact (no code needed, physics does it). Optionally log hits with OnCollisionEnter.
  4. Add small Cubes as Coins: tick Is Trigger on their colliders, no Rigidbody. Write the Coin script so rolling over one destroys it β€” but only if the toucher is tagged Player.
  5. Verify: crates block/push (solid), coins vanish silently (trigger). Ask yourself why the trigger fires even though coins have no Rigidbody.

Starter Code:

using UnityEngine;

public class Coin : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        // TODO: if 'other' is tagged "Player", destroy this coin.
    }
}
πŸ’‘ Hint

Guard with if (other.CompareTag("Player")) then Destroy(gameObject). The trigger works because the Ball has a Rigidbody β€” the rule only needs one of the pair to have one. Make sure the coin's collider has Is Trigger checked, or you'll get a solid bump instead.

βœ… Solution
using UnityEngine;

public class Coin : MonoBehaviour
{
    [SerializeField] private int value = 1;

    void OnTriggerEnter(Collider other)
    {
        if (!other.CompareTag("Player")) return;

        Debug.Log($"Collected coin worth {value}");
        Destroy(gameObject);
    }
}
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class BallController : MonoBehaviour
{
    [SerializeField] private float moveForce = 10f;
    private Rigidbody body;
    private Vector2 input;

    void Awake() => body = GetComponent<Rigidbody>();

    void Update()
    {
        input.x = Input.GetAxisRaw("Horizontal");
        input.y = Input.GetAxisRaw("Vertical");
    }

    void FixedUpdate()
    {
        body.AddForce(new Vector3(input.x, 0f, input.y) * moveForce);
    }

    // Optional: log solid hits with crates.
    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Crate"))
            Debug.Log($"Pushed {collision.gameObject.name}");
    }
}

Two callback families, two behaviors: crates use solid OnCollisionEnter/physics response; coins use OnTriggerEnter and vanish. The coin needs no Rigidbody because the Ball supplies one for the contact.

🎯 Quick Quiz

Question 1: You want a coin the player passes through but that still detects the touch. How is its collider set up?

Question 2: A trigger between a coin and the player never fires. Both have colliders and the coin's Is Trigger is on. What's the most likely fix?

Question 3: Where should you apply AddForce to a Rigidbody, and why?

Summary

πŸŽ‰ Key Takeaways

  • A Rigidbody makes an object physics-simulated; a Collider gives it a contact shape. Solid, pushable objects need both.
  • Move physics objects via AddForce, linearVelocity, or MovePosition β€” always in FixedUpdate, never by setting the Transform.
  • Solid contacts fire OnCollisionEnter(Collision); triggers (Is Trigger) fire OnTriggerEnter(Collider) β€” mind the different parameter types.
  • Callbacks only fire if at least one object in the contact has a Rigidbody (a isKinematic one counts).
  • Filter contacts with tags (CompareTag in code) and layers (the Layer Collision Matrix, before contact).
  • Unity 6 renames: velocity β†’ linearVelocity, drag β†’ linearDamping.

πŸ“š Additional Resources

πŸš€ What's Next?

Objects now collide and react β€” but our coins were placed by hand. In Lesson 2.3, Instantiating and Destroying Objects, we spawn objects at runtime from prefabs (fire bullets, spawn enemies) and manage their lifecycle cleanly.

πŸŽ‰ Your world has weight now!

Gravity, pushing, collecting, triggering β€” the core verbs of most games. You've built the physical layer everything else sits on.