π 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
Rigidbodyand aCollidereach contribute to physics - Move a physics object with forces,
linearVelocity, orMovePositionβ inFixedUpdate - Distinguish solid collisions from triggers (
isTrigger) - Handle
OnCollisionEnterandOnTriggerEnterwith the right parameter types - State the rule for which objects need a Rigidbody for callbacks to fire
- Use
isKinematicand 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:
| Component | Provides | Without it⦠|
|---|---|---|
| Rigidbody | Makes 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. |
| Collider | Defines 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).
(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).
| Technique | What it does | Best 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 inUpdate(so no press is missed) and stored, then the stored value drives the physics inFixedUpdate. 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:
| Callback | Fires 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 collision | Trigger |
|---|---|
| Objects physically block each other | Objects pass through |
OnCollisionEnter(Collision) | OnTriggerEnter(Collider) |
Parameter: Collision (rich contact data) | Parameter: Collider (just the other collider) |
| Crates, walls, the player's body | Coins, 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)andOnTriggerExit(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.
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.
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:
- Make a Plane floor. Add a Sphere named
Ballwith aRigidbody; tag itPlayer. - Write
BallController(see Section 2) to roll it withAddForceinFixedUpdate. - Add a few Cubes as
Crates with Rigidbodies β the ball should shove them on contact (no code needed, physics does it). Optionally log hits withOnCollisionEnter. - Add small Cubes as
Coins: tick Is Trigger on their colliders, no Rigidbody. Write theCoinscript so rolling over one destroys it β but only if the toucher is taggedPlayer. - 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, orMovePositionβ always inFixedUpdate, never by setting the Transform. - Solid contacts fire
OnCollisionEnter(Collision); triggers (Is Trigger) fireOnTriggerEnter(Collider)β mind the different parameter types. - Callbacks only fire if at least one object in the contact has a Rigidbody (a
isKinematicone counts). - Filter contacts with tags (
CompareTagin code) and layers (the Layer Collision Matrix, before contact). - Unity 6 renames:
velocityβlinearVelocity,dragβlinearDamping.
π Additional Resources
- Scripting Reference β Rigidbody
- Scripting Reference β OnCollisionEnter
- Scripting Reference β OnTriggerEnter
- Manual β Colliders & the collision action matrix
π 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.