Collision detection
Every collision test answers one question: do these two shapes overlap, and if so, by how much and along which direction? The "how much" is the penetration depth, the "which direction" is the contact normal. Those two numbers are the entire handoff to collision response — get them wrong and nothing downstream can be right.
Proxy shapes, not meshes
Never collide the render mesh. A character mesh is 30,000 triangles; a triangle-vs-triangle test over two of them is nine hundred million tests. So physics uses a cheap stand-in shape that roughly fills the same space:
- Sphere / circle — one centre, one radius. Cheapest test there is.
- AABB (axis-aligned bounding box) — min and max corner. Cannot rotate.
- OBB — a box with an orientation. One rotation away from an AABB.
- Capsule — a segment with a radius. The usual choice for characters: no corners to catch on stairs.
- Convex hull — the shrink-wrap of a point cloud, for props that need their actual silhouette.
Concave shapes are not tested directly; they are decomposed into several convex pieces, or treated as static triangle soup.
The tests worth knowing cold
Sphere–sphere: compare distance to the summed radii. d = |B−A|; overlapping if d < rA + rB. Depth is rA + rB − d, normal is (B−A)/d. Compare squared distances to skip the square root when you only need the yes/no.
AABB–AABB: they overlap only if they overlap on every axis. One axis with a gap means no collision; return early. When they do overlap, the smallest per-axis overlap is the penetration depth and that axis is the normal — the shortest way out.
Sphere–AABB: clamp the centre into the box, then measure. closest = clamp(centre, min, max). Overlapping if |centre − closest| < r. That one clamp handles faces, edges and corners in a single line. Centre inside the box is the one case needing special handling: push out through the nearest face.
SAT, the general idea
If you can find one axis on which their two shadows don't overlap, the shapes are apart. That is the Separating Axis Theorem, and it is just the AABB test freed from axis alignment. For two convex polygons, project both onto each candidate axis (the face normals of both shapes) and look for a gap. Any gap: done, no collision, stop testing. No gap on any axis: they collide, and the axis with the smallest overlap gives the contact normal and penetration depth.
SAT needs a finite axis list, so it wants polygons and polyhedra. GJK is the generalisation to arbitrary convex shapes — it answers yes/no and distance — with EPA run afterwards to recover the depth and normal that GJK alone doesn't give you.
Demo
Live overlap tests with visible SAT axes — drag either shape, switch the shape pair, and watch the projection bars on the right: one red row with a gap in it is a proof of separation.
Gotchas
| Symptom | Cause |
|---|---|
| Objects jitter along one axis when resting | AABB min-overlap normal flipping between axes frame to frame; add hysteresis or use a real contact manifold |
| Character snags on flat floor seams | Box or hull feet catching tile edges; use a capsule |
| Penetration depth is right, normal points the wrong way | Normal convention not fixed; pick "from A to B" and enforce it in every test |
| Sphere inside a box reports zero depth | Clamped closest point equals the centre, so the distance is 0 and the normal is undefined; special-case interior |
| SAT says "touching" constantly | Exact < on floats; use a small epsilon and treat near-zero depth as no contact |
| Fast object passes clean through a wall | Detection is discrete — this is a timestep problem, not a shape problem |