Files
nage/workshop/steps/07-swept-aabb/README.md
T
2026-09-04 13:57:52 +02:00

69 lines
2.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.
```sh
bun test workshop/steps/07-swept-aabb
```