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

1.8 KiB

Step 08 — Resolve (move to the wall, not through it)

Detection is done. Now for response — actually reacting to the hit. This first half is almost embarrassingly small, but it introduces the idea the whole loop (step 10) is built on: the frame isn't all-or-nothing.

Concept

sweptAABB hands you a Hit with a time between 0 and 1 — the fraction of the frame at which you'd collide. So instead of moving the full displacement v (which would bury you inside the wall), you move only the part of it that happens before impact:

no hit   → newPos = pos + v          (nothing in the way: take the whole move)
hit at t → newPos = pos + v * t       (stop exactly at the contact point)

That's it — you already have add and scale; this is them, gated on the hit.

The leftover that matters

Here's the seed for step 10: if you hit at t = 0.3, you only used 30% of this frame. The other 70% is still owed to the player — they should keep moving for the rest of the frame, just not into the wall. That leftover time is exactly why sliding (step 09) and the loop (step 10) exist. A collision doesn't end the frame; it interrupts it.

Real-engine footnote: production code usually moves to t - EPSILON (a hair short of contact) so floating-point error can't leave the box a sliver inside the wall, where the next frame's sweep would start already-overlapping. Your nage does this with Math.max(0, time - EPSILON). We keep the kata exact so the numbers stay clean — just know that tiny backoff is there for a real reason.

Task

Implement resolve(pos, v, hit) in resolve.ts: return the full move when hit is null, otherwise the position at contact. vec/add/scale and the Hit type are in given.ts.

bun test workshop/steps/08-resolve