# Step 10 — moveAndSlide (the whole engine, in one loop) This is it. Every function you've written since step 01 gets tied together here into the exact loop your real `nage` engine runs. It's the hardest step, so I'll give you the skeleton — you write the code. ## The idea A single collision doesn't end the frame (step 08's "leftover that matters"). You hit a wall at `t = 0.3`, slide, and **70% of the frame is still owed** — during which you might hit *another* wall, slide again, and so on. So moving is a small **loop**: sweep → stop at the nearest hit → slide → repeat with the leftover time. We loop a **maximum of 4 times** (your engine's cap) — enough to handle a corner (hit a wall, slide, hit the perpendicular wall, slide, stop) without ever risking an infinite loop. ## The algorithm You're given the moving `box`, its full-frame displacement `v`, and a list of static `walls`. Track a running `pos`, a running `vel`, and `timeLeft` (fraction of the frame remaining, starts at `1`). ``` pos = { box.x, box.y } vel = { v.x, v.y } timeLeft = 1 repeat up to 4 times, while timeLeft > 0: move = vel * timeLeft // what's left to travel this frame find the NEAREST hit: for each wall, sweptAABB(box-at-pos, move, wall); keep the hit with the smallest .time if no hit: pos = pos + move // clear path: take the rest of the move stop else: pos = pos + move * max(0, hit.time - EPSILON) // advance to just before contact vel = slide(vel, hit.normal) // redirect along the wall timeLeft = timeLeft * (1 - hit.time) // consume the used fraction return pos ``` Two things worth understanding, not just copying: - **`move = vel * timeLeft`.** `vel` is a *full-frame* displacement (how far you'd go in a whole frame at this velocity). You only have `timeLeft` of the frame left, so the actual travel is `vel * timeLeft`. `sweptAABB`'s returned `time` is then a fraction *of that sub-move*, which is why `pos + move * time` is correct. - **The `EPSILON` backoff** (`max(0, hit.time - EPSILON)`). Stop a hair *short* of the wall. If you land exactly on it, floating-point error can leave you a sliver inside — and next iteration's sweep would start already-overlapping, reporting a garbage negative-time "collision" that makes you stick or jitter. That tiny gap is exactly the `Math.max(0, time - EPSILON)` in your real `moveAndSlide`. Now you know *why* it's there. `EPSILON` is provided in `given.ts`. > **Moving-vs-moving (why your real engine has `velocity - otherVel`).** Here the > walls are static, so we sweep with plain `vel`. When the *other* body also moves, > you sweep in its frame of reference by using the **relative** velocity > `vel - otherVel` — then the exact same loop works, because from the other body's > point of view it's standing still. That's the only difference between this kata > and the full engine. The loop itself doesn't change. ## Task Implement `moveAndSlide(box, v, walls)` in `moveAndSlide.ts`. Everything you need — `sweptAABB`, `slide`, the vector ops, `EPSILON` — is finished in `given.ts`. ```sh bun test workshop/steps/10-move-and-slide ```