The pack
pseudoRoad
function pseudoRoad(): Pack<{ pseudoRoad: PseudoRoadApi }>
The pseudo-road pack: the pseudo-3D road of the classic racers. Pure logic, so it runs headless.
Helpers
createRoad
function createRoad(o: RoadOptions): PseudoRoad
Creates a PseudoRoad from the given options.
Classes
PseudoRoad
class PseudoRoad
The segment-projected road of every arcade racer of the era: no 3D, just a strip of trapezoids whose far edge is shifted sideways by the accumulated curve. Curves are a LATERAL SHIFT PER SEGMENT, not real geometry, which is exactly why the road can bend without the camera ever turning.
for (const q of road.view(position, playerX)) {
layer.seg(q.x1 - q.w1, q.y1, q.x2 - q.w2, q.y2, style(q.fog));
layer.seg(q.x1 + q.w1, q.y1, q.x2 + q.w2, q.y2, style(q.fog));
}
readonly segments: RoadSegment[]— All road segments, in track order.readonly segmentLength: number— The length of one segment, in world units.readonly roadWidth: number— The half-width of the road surface, in world units.readonly lanes: number— The number of lanes across the road.readonly camDepth: number— The camera depth derived from the field of view.readonly netHill: number— Net height the pieces climbed before the loop was levelled.camHeight: number— The camera height above the road, in world units.drawDistance: number— The number of segments drawn ahead of the camera.constructor(o: RoadOptions)get length(): number— The total length of the road, in world units.segmentAt(z: number): RoadSegment— Segment at a world z, wrapping round the track.curveAt(z: number): number— Returns the curvature of the segment at worldz.heightAt(z: number): number— Road height at a world z, interpolated between segments.laneX(lane: number): number— Lateral offset of a lane centre, -1..1 across the road.centrifugal(z: number, speed: number, maxSpeed: number, strength = 0.3): number— Sideways push from taking a corner at speed. Positive is to the right — subtract it from the player's lateral position, and a fast corner starts throwing the car off the road, which is the whole skill of these games.view(rawZ: number, playerX: number, screen: { w: number; h: number }, opts: RoadViewOptions = {}): RoadQuad[]— Project the visible road.camZis the camera's world z,playerXthe lateral position in road half-widths (-1 left edge, +1 right edge).ahead(z: number, from = this.view0.camZ): number— How far along the roadzis fromfrom(default: the camera's z at the lastview()), signed and wrapped the short way. Positive is ahead. The gameplay counterpart toplace():placeanswers "where do I draw this" and returns null when it is not on screen, which is the wrong answer to "how close is it".visible(z: number): boolean— Is the road atzon screen this frame? Call afterview().distanceAtScreenY(y: number): number— How far up the road the point drawn at screen rowyis — the projection run backwards.Infinityabove the horizon. This is how far in front of the camera your own car is. A chase view draws the player some way up the screen, and the road distance that corresponds to is nearly a thousand units on a typical setup. Collisions measured from the CAMERA are therefore that far out.contact(z: number, offset: number, camZ: number, lateral: number, o: { atY?: number; reach?: number; halfWidth?: number; blind?: boolean } = {}): boolean— Is a thing standing at (z,offset) touching the player? PassatY: the screen row your own car is drawn at. The camera is BEHIND the car in a chase view, and the road distance between them is large — around a thousand units at a typicalcamHeight. Measure contact fromcamZand every hit lands that far out: things register while they are still up the road, or, worse, once they are already past you and below the bottom of the screen where you cannot see them at all. That is the classic "crashed into nothing". WithatYthe window is symmetric about your car,reacheither side, and the geometry then guarantees contact happens while the thing is on screen. Call afterview().place(z: number, offset: number, height = 0): { x: number; y: number; scale: number } | null— Where a thing sitting on the road at worldz, lateraloffset(-1..1),heightworld units up, lands on the screen. Call it afterview()— it reads that frame's projection so the object rides the same curve as the road under it. Returns null when it is not in view.
Types
PseudoRoadApi
type PseudoRoadApi = typeof kit
game.pseudoRoad — the pseudo-3D road and its projection, as one namespace.
RoadOptions
interface RoadOptions
The options that configure a PseudoRoad. Passed to createRoad().
pieces: RoadPiece[]— The pieces that make the track, in order.segmentLength?: number— Length of one segment. Default 200.roadWidth?: number— Half-width of the road surface in world units. Default 1000.lanes?: number— The number of lanes across the road. Default 3.fov?: number— Vertical field of view, degrees. Default 100 (a 1.0 camera depth).camHeight?: number— Camera height above the road. Default 1000.drawDistance?: number— Segments drawn ahead. Default 200.closeLoop?: boolean— Level the track's height so its end meets its start. Default true.segmentAt()wraps, so a road is always a LOOP — and a loop whose hills do not sum to zero has a CLIFF at the finish line. Leave this on unless the road is a one-way route you never drive off the end of.
RoadPiece
interface RoadPiece
One stretch of track — a straight, a corner or a hill. Listed in RoadOptions.pieces.
length: number— How far this piece runs, in world units.curve?: number— Curvature per segment: negative left, positive right. Default 0.hill?: number— Height climbed (+) or dropped (-) over the piece. Default 0.name?: string— A name copied to each segment of the piece — use it to style or find a stretch.ease?: boolean— Ease the curve and hill in and out instead of snapping. Default true.
RoadQuad
interface RoadQuad
One projected segment, ready to draw as lines.
index: number— The index of the source segment.name: string— The name of the piece the segment came from.curve: number— The curvature of the segment.x1: number— Near edge: centre x, y, half-width, all in screen units.y1: number— The screen y of the near edge.w1: number— The half-width of the near edge, in screen units.x2: number— Far edge.y2: number— The screen y of the far edge.w2: number— The half-width of the far edge, in screen units.fog: number— 0 near … 1 at the draw distance — fade width, glow and alpha with it.looped: boolean— Segments this far along the road, for texturing rumble strips.
RoadSegment
interface RoadSegment
One segment of a built road. Made by the PseudoRoad constructor from the pieces.
index: number— The position of this segment in the track, from 0.z: number— World z at the START of this segment.curve: number— The lateral shift this segment applies: negative left, positive right.y: number— World height at the start of this segment.name: string— The name of the piece this segment came from, or an empty string.
RoadViewOptions
interface RoadViewOptions
Options for PseudoRoad.view().
occluded?: boolean— Also project the in-front segments hidden behind a crest, forplace()only (they are not returned andvisible()still says no). A painter that draws the road and its sprites far to near needs them: a tree standing beyond a hill keeps its top above the crest instead of popping in. Default false.fov?: number— Field of view for this view only (degrees, asRoadOptions.fov). Default the road's own.smoothCurves?: boolean— Lay curves continuously: the lateral offset of every point ahead is the exact double integral of the curvature from the camera (the camera looks along the road there), so a point a fixed distance ahead stays put as the camera crosses segment boundaries. The classic per-segment accumulation (the default) shifts the whole road by up to a segment's curve at each boundary: in a long bend a car held a fixed distance ahead judders sideways by a pixel or two every segment. Default false (the classic look).eyeAt?: number— Road position (raw z, as passed toview) the camera height is measured above. Default the camera's own z. A chase camera measures it above the vehicle it follows, so the vehicle stays put on screen on a slope instead of riding up and down with the grade between the camera and the vehicle.


