Parallax Scrolling Backgrounds
Scrolling each background plane at a fraction of the camera’s speed so distant layers lag behind near ones, creating a cheap illusion of depth.
Why it matters
Captain Claw’s levels stack several tile planes — far sky, mid scenery, the playfield, and a near foreground. The WWD gives each plane a movementPercent: 100 means it locks to the camera (the action plane), 50 means it scrolls half as fast (looks far away), 0 means it never moves (a fixed backdrop). This single per-plane scalar is what sells parallax, and it’s a few lines of arithmetic per plane per frame.
How it works
Each plane computes its own apparent camera position from the real camera rect, then renders only the tiles that fall in view:
ratioX = movementPercentX / 100.0
paraX = cameraRect.x * ratioX // this plane's scroll offset
startCol = paraX / tilePixelWidth // first visible column
tileX = col * tilePixelWidth - paraX // on-screen x of that tile- Fraction of the camera. A plane at
movementPercent = 40usesparaX = cameraRect.x * 0.4, so when the camera moves 100 px the plane shifts 40 — it appears 2.5x further away. - Cull to the viewport. Only
cameraRect.w / tilePixelWidth + 2columns are drawn; the+2covers the partial tiles at both edges after the float→int truncation ofstartCol. - Wrapping planes. Sky/back planes set
isWrappedand tile infinitely via modulo:tileIndex = col % tilesOnAxisX, so a 16-wide sky repeats forever without storing more data. See the tileset indexing. - Draw order. Planes render back-to-front by render pass (Background → Action → Foreground), so near layers paint over far ones.
Example
Camera at world x = 1000, three planes, tiles 64 px wide:
| Plane | movementPercent | paraX | first col | feels |
|---|---|---|---|---|
| Sky | 0 | 0 | 0 | fixed backdrop |
| Hills | 40 | 400 | 6 | far |
| Action | 100 | 1000 | 15 | player layer |
The sky never shifts, hills drift slowly, the action plane tracks Claw 1:1 — three depths from one camera value.
Pitfalls
- Scrolling sprites by full camera but background by fraction inconsistently. Mixing the two coordinate spaces makes actors slide relative to the ground; actors live on the 100% plane.
- Off-by-one cull. Dropping the
+2padding leaves a one-tile gap flickering at the screen edge asstartColtruncates. - Wrapping without modulo on the negative side. Negative
colcan index out of bounds when the camera moves left of origin; guard or wrap both directions. - Per-frame full-plane redraw. Iterating every tile in the level instead of the visible window murders the framerate on large maps.