2.4 KiB
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