What it does
A scrolling game's difficulty lives in its schedule: what arrives, at what distance, and how the ground changes
under it. This pack makes that schedule plain data. A route stage holds named sections and checkpoint distances, and
you advance it by the distance travelled each frame, by the player's speed for a racer or by a constant for a game
that pulls the player along. It calls you back as each section is entered and each checkpoint passed, can loop for
laps (carrying the overshoot into the next lap and firing what it passes there), and rewindToCheckpoint() is the
crash-and-resume rule.
An obstacle schedule is a list of entries, each a single placement at a distance or a repeat every so many units
across a range. Because entries fire by distance rather than time, the pattern is identical however fast the player
goes. upTo(distance) hands back what is now due, seek() replays from a checkpoint, and add() drops in more
entries without anything already fired firing again.
The ground is a terrain envelope: a floor and an optional ceiling as heightfields, with the questions a side-scroller
asks of them. floorAt, ceilAt and gapAt give heights and the space between, hits(x, y, r) says whether a
ship touched the floor or the ceiling, slice returns the points to draw, and crater() lets a bomb dig into the
floor. generateCavern builds that envelope for you from sections with floor and roof bands, rough or smooth, with
open sky wherever a section has no roof, and the same options and seed always make the same cavern.
Features
- Route stages — named sections, checkpoints, a distance that only advances, and callbacks on each section, each checkpoint and the finish.
- Laps and rewinds — looping routes count laps, and a crash rewinds to the last checkpoint passed.
- Obstacle schedules by distance — single placements or repeats over a range with an optional cap, each carrying your own data, so the same plan plays the same at any speed.
- Seek and add — jump the schedule back to a checkpoint, or add entries mid-run without refiring the past.
- Terrain envelopes — floor and ceiling heights, the gap between them, a floor-or-ceiling hit test with a radius, slices for drawing and craters dug by bombs.
- Seeded caverns — sections with floor and roof bands and a roughness, laid as one floor and ceiling pair with a ready envelope.
- Headless — pure maths with no other packs required, so stages can be tested without a screen.
Good for
- Horizontal scrolling shooters that fly over hills, through cities and into caverns, in the style of Scramble.
- Cave flyers, where the ship threads a tunnel whose floor and roof close in section by section.
- Buggy and patrol games over rough ground with craters, in the style of Moon Patrol.
- Wave plans for any scroller, where enemies and pickups arrive at set distances along the stage.
- Racers with checkpoints and laps, using the route stage and its callbacks without the terrain.
Make it yours
The pack has no options of its own; each piece takes its own plain-data definition. A stage is its list of sections
(each with a name, a length and data for your game), its checkpoint distances and whether it loops. A schedule is its
entries, each with a type string your game interprets and optional data copied onto every placement. A cavern's
sections each take a floor band (default 40 to 120 above baseY), an optional ceiling band below topY, and a
rough value from 0 to 1 (default 0.35); step sets the sample spacing (default 48) and seed or your own rng the
randomness. You can also skip the generator and hand createEnvelope floor and ceiling polylines you drew yourself.
Two different uses: a six-section side-scrolling shooter whose stage pulls the ship along at a constant speed, with rockets every 300 units, fuel tanks at fixed distances and a cavern that closes to a tight tunnel near the end; and a looping race circuit with three checkpoints, advanced by the player's own speed, where only the route stage and its lap count are used.
A quick look
A stage, its obstacle plan and its cavern, stepped together:
const stage = game.route.createRoute({ sections: [{ name: 'mountains', length: 4000 }, { name: 'city', length: 3000 }], checkpoints: [3500] });
const plan = game.route.createSchedule([{ type: 'rocket', every: 300, from: 500, to: 3800 }, { type: 'fuel', at: 1200 }]);
const cave = game.route.generateCavern({ baseY: 540, seed: 7, sections: [{ name: 'hills', length: 4000 }, { name: 'tunnel', length: 3000, ceiling: [60, 160] }] });
step(dt => {
stage.advance(speed * dt);
for (const due of plan.upTo(stage.distance)) spawn(due);
if (cave.envelope.hits(ship.x, ship.y, 8)) crash();
});
Works with
scrolling-shooter— a whole scrolling shooter's rules, built on this pack.pseudo-road— the pseudo-3D road of into-the-screen racers, which this pack does not draw.
Good to know
- Floors and ceilings are heightfields, one height per x, so there are no overhangs; that is what keeps every query a quick search.
- The schedule fires by distance travelled. A game that pauses the scroll simply stops calling
advance. - It moves and draws nothing: your game spawns what the schedule says is due and draws the slices it returns.






