This commit is contained in:
Schluffe
2026-09-04 13:57:52 +02:00
parent 71cfa64793
commit e961887953
24 changed files with 340 additions and 360 deletions
+19 -18
View File
@@ -1,15 +1,15 @@
# Step 08 — Resolve (move *to* the wall, not through it)
# 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.**
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:
`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)
@@ -22,21 +22,22 @@ That's it — you already have `add` and `scale`; this is them, gated on the hit
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.
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.
> _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`.
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`.
```sh
bun test workshop/steps/08-resolve