Collision detection

10-collision-detection.md · updated 2026-10-15

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:

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 restingAABB min-overlap normal flipping between axes frame to frame; add hysteresis or use a real contact manifold
Character snags on flat floor seamsBox or hull feet catching tile edges; use a capsule
Penetration depth is right, normal points the wrong wayNormal convention not fixed; pick "from A to B" and enforce it in every test
Sphere inside a box reports zero depthClamped closest point equals the centre, so the distance is 0 and the normal is undefined; special-case interior
SAT says "touching" constantlyExact < on floats; use a small epsilon and treat near-zero depth as no contact
Fast object passes clean through a wallDetection is discrete — this is a timestep problem, not a shape problem

Open the demo →

← all notes & demos