Files
nage/workshop/steps/07-swept-aabb
2026-09-04 13:57:52 +02:00
..
2026-08-27 12:46:42 +02:00
WIP
2026-09-04 13:57:52 +02:00
2026-07-11 17:48:28 +02:00

Step 07 — Swept AABB (the Minkowski trick)

Step 06 handled a moving point vs a box. But in a real game the thing that moves is a box (the player), not a point. This step turns "moving box vs box" into "moving point vs box" so you can reuse step 06 unchanged. That conversion is the single cleverest idea in the whole engine.

The problem

Box A (the player) sits at corner (a.x, a.y) with size a.w × a.h, and moves by v this frame. Box B (a wall) is static. When do they touch?

It's fiddly because both shapes have size. You'd have to track four edges of A against four edges of B. Ugh.

The trick: grow B, shrink A to a point

Watch what "just touching" means on the x-axis. A spans [a.x, a.x + a.w], B spans [b.x, b.x + b.w]. They overlap when:

a.x < b.x + b.w     AND     b.x < a.x + a.w

Rearrange the second one (b.x - a.w < a.x) and you get a statement purely about a.x, the corner of A:

b.x - a.w  <  a.x  <  b.x + b.w

Read that: A's corner a.x behaves exactly like a point sliding inside a wider interval — one that starts a.w earlier and is a.w longer than B. The same happens on y with a.h.

So: dump all of A's size onto B, and A collapses to just its corner point.

inflated = {
  x: b.x - a.w,         // push the left edge out by A's width
  y: b.y - a.h,         // push the top edge out by A's height
  w: b.w + a.w,         // grow width by A's width
  h: b.h + a.h,         // grow height by A's height
}
point = { x: a.x, y: a.y }   // A is now just its corner

This grown box is the Minkowski sum of B with A. And "does this point, moving by v, hit inflated?" is exactly rayVsAABB from step 06. You're done in three lines.

Sanity picture: player box 2 wide with its right edge at x=2, wall left edge at x=5 → real gap is 3. Inflate: inflated.x = 5 - 2 = 3, and the player's corner sits at x=0, so the corner-to-inflated-edge gap is also 3. Same answer, simpler shape. The inflation bakes A's size into the wall so the corner can pretend to be a point.

This is the heart of your real engine's sweptAABB — the inflAABB it builds is this very inflated box, and (ax, ay) is this corner point.

Task

Implement sweptAABB(a, v, b) in swept.ts: build the inflated box, then call the provided rayVsAABB (finished, in given.ts). Return its Hit | null.

bun test workshop/steps/07-swept-aabb