Colliders & Rigidbodies
How Unity decides what touches what
Physics questions are the most common Unity questions, and nearly all of them reduce to one thing: which object has a Rigidbody? Get that right and collisions behave. Get it wrong and events silently never fire.
Two components, two jobs
- Collider defines the shape used for collision. It has no motion of its own.
- Rigidbody hands the object to the physics engine, giving it mass, velocity, and gravity.
A collider with no rigidbody is static geometry — walls, floors, level meshes. Unity optimises heavily for these on the assumption they never move.
The collision matrix
This table answers "why is OnCollisionEnter never called?" more often than anything else. At least one of the two objects must have a non-kinematic Rigidbody.
Does a collision event fire?
| Object A | Object B | Result |
|---|---|---|
Collider only | Collider only | Nothing. Neither is in the simulation. |
Collider + Rigidbody | Collider only | Collision fires. The usual player-vs-wall case. |
Collider + Rigidbody | Collider + Rigidbody | Collision fires, both react. |
Kinematic Rigidbody | Collider only | Nothing. Kinematic vs static is ignored. |
Kinematic Rigidbody | Collider + Rigidbody | Collision fires. The dynamic one reacts. |
Any + Is Trigger | Any with Rigidbody | Trigger fires instead of collision. |
Collisions vs triggers
A collision physically stops things. A trigger detects overlap and lets objects pass straight through. Pickups, checkpoints, and damage zones are triggers; walls and crates are collisions.
private void OnCollisionEnter(Collision collision)
{
// Physical impact - you get contact points and impulse
if (collision.gameObject.CompareTag("Ground"))
isGrounded = true;
float force = collision.impulse.magnitude;
if (force > 10f)
PlayCrashSound(force);
}private void OnTriggerEnter(Collider other)
{
// Overlap only - no impact, no impulse
if (other.CompareTag("Player"))
{
other.GetComponent<Health>().Heal(25);
Destroy(gameObject);
}
}CompareTag("Player") rather than other.tag == "Player". The property version allocates a string every call, and this runs on every overlap.Each comes in three flavours: Enter, Stay, and Exit. Stay runs every physics step while contact continues, so keep it cheap.
Moving a Rigidbody
Setting transform.position on a physics object teleports it, skipping collision detection entirely. Fast objects tunnel through walls.
| How to move it | Use when | Note |
|---|---|---|
rb.AddForce() | Realistic acceleration | Mass matters. Feels weighty. |
rb.velocity = v | Direct arcade control | Ignores mass. Most 2D platformers. |
rb.MovePosition() | Kinematic movement | Respects collisions. Moving platforms. |
transform.position | Non-physics objects only | Skips physics. Tunnelling. |
private void FixedUpdate()
{
// Kinematic platform that still pushes things correctly
Vector3 next = rb.position + move * speed * Time.fixedDeltaTime;
rb.MovePosition(next);
}Fast objects tunnel
Physics is sampled in steps. If a bullet travels further in one step than the wall is thick, it is on one side, then the other, and never in between. Nothing is detected.
- Set Collision Detection to Continuous on fast rigidbodies.
- Or skip rigidbodies for bullets and raycast along the path travelled each frame.
- Making walls thicker genuinely helps, unglamorous as it sounds.
private void FixedUpdate()
{
float step = speed * Time.fixedDeltaTime;
// Check the path we are about to travel, not just where we are
if (Physics.Raycast(transform.position, transform.forward, out RaycastHit hit, step))
{
hit.collider.GetComponent<Health>()?.TakeDamage(damage);
Destroy(gameObject);
return;
}
transform.position += transform.forward * step;
}Collider shapes have costs
| Collider | Cost | Note |
|---|---|---|
Sphere | Cheapest | One distance check. |
Capsule | Cheap | The standard character shape. |
Box | Cheap | Fine for most props. |
Mesh (convex) | Expensive | Required for a moving mesh collider. |
Mesh (concave) | Most expensive | Static geometry only. |
Layers keep physics cheap
Every pair of layers can be switched on or off in the collision matrix. Bullets do not need to test against other bullets, and UI never needs physics at all. Turning off pairs you do not need is the cheapest optimisation in the engine.
// Restrict a raycast to a single layer
[SerializeField] private LayerMask groundLayer;
bool grounded = Physics2D.OverlapCircle(feet.position, 0.2f, groundLayer);What to take away
- At least one object in a pair needs a non-kinematic Rigidbody, or nothing fires.
- Colliders without rigidbodies are static — never move them.
- Triggers detect overlap; collisions apply force.
- Move physics objects in
FixedUpdatewith velocity, forces, orMovePosition. - Use Continuous detection or raycasts for anything fast.
- Prefer primitive colliders and switch off unneeded layer pairs.