jengine-scaler
Pseudo-3D sprite scaler - a road bent at projection time, and a hill that hides the road behind it.
| Kind | Project package: linked into the builds of the projects that list it |
| Source | packages/jengine-scaler |
| Version | 0.1.0 |
| Entry point | jengine_scaler::ScalerPackage |
| Script namespace | Scaler |
| Listed by | projects/arcade-classics, projects/car-classics |
Using it
Section titled “Using it”List it in the top-level "packages" array of project.jengine:
{ "packages": [ { "name": "jengine-scaler", "source": { "type": "path", "path": "../../packages/jengine-scaler" } } ]}Then rebuild the editor viewport for the project (jengine viewport-build projects/<name>, or the
editor’s Rebuild viewport banner). Native and web builds link it on their own.
A scene that uses its components must be a v2 scene ("version": 2): a v1 scene has no field
for a package component and keeps it as inert custom data.
A script can check that the package is linked with if Scaler == nil then return end.
Components
Section titled “Components”| Scene key | Rust type | |
|---|---|---|
cd_drive | Drive | package only: needs a v2 scene |
Script commands
Section titled “Script commands”Commands run in every runtime and return nothing; a package publishes what scripts read onto components (see the notes below). The flat name also works.
| Call | Flat name |
|---|---|
Scaler.start(e) | cd_start |
Scaler.next(e) | cd_next |
Scaler.quit(e) | cd_quit |
Scaler.gear(e, gear) | cd_gear |
Scaler.input(e, steer, throttle, brake) | cd_input |
Scenes that use it
Section titled “Scenes that use it”projects/arcade-classics/assets/scenes/coral.sceneprojects/car-classics/assets/scenes/outrun.scene
From packages/jengine-scaler/README.md.
A pseudo-3D sprite scaler - the road of OutRun and Pole Position - and the
two arcade games built on it: Coral Drive (projects/arcade-classics, three
stages against the clock) and Sunset Coast (projects/car-classics, an
OutRun: fifteen stages in a pyramid of forks, five goals).
What it is here to prove
Section titled “What it is here to prove”Many drawables a frame, each with its own scale. Every sprite’s size comes
from its own distance, and the road itself is six hundred more - all written
straight into a package-owned pool of BulkEntity sprites (and, for the sky
and tunnels, Mesh2D quads), so nothing crosses the script bridge per
drawable. And a first-person view can be computed rather than drawn: the
projection, the hill clip and the scanline rasteriser are pure functions over a
segment table, tested as geometry rather than squinted at.
The ideas, in the order they matter
Section titled “The ideas, in the order they matter”- The road is straight in world space and bent at projection time
(
road). A segment’scurveis not a position: walking outward from the camera each slice shifts by an accumulateddxthat grows by its curve, so a constant curve bends the road quadratically, as a real corner looks. - A hill hides the road behind it - a row is drawn only if it rises above
every row already drawn (the classic
maxyclip). - One sprite strip per screen scanline (
view), not polygons: the count is bounded by the view’s height, so a longer view costs nothing. - Every corner has a top speed, and it is one division (
run): centrifugal force goes with the square of speed, so a stage’s fastest possible time is a sum, andcontentrefuses a stage nobody could finish on its clock, or whose crest hides the road just ahead. - A fork is two segment tables projected from the car (
fork). The road widens to two roads side by side, splits, each branch moves out (shift) and bends away (an extra curve eased in and out), and whichever side of the gap the car is on at the end is the branch taken; the run then rebases onto it and the branch not taken stays on screen as a ghost until it has gone. The bend’s tail belongs to the next stage and depends on which side it was entered from, so the refusal checks every way the route can drive a stage: each side it can be entered from, times each branch it leaves by. - Tunnels are meshes - walls, roof and lamps as exact trapezoids between projected rows, a rock face round the mouth from outside - sorted against the scenery by distance.
Component ("version": 2 scenes)
Section titled “Component ("version": 2 scenes)”cd_drive, on an entity with a transform (the centre of the view):
"cd_drive": { "tables": { "stages": "data/cd_stages" }, "which": 0, "sheet": "atlases/coral.png", "props": ["cd_palm", "cd_sign", "cd_rock", "cd_bush"], "cars": ["cd_car_a", "cd_car_b", "cd_truck"], "aspects": [], "car_aspects": [], "route": false, "crashes": false, "z_base": -30}route: true drives on through each stage’s fork into the next, the clock
topped up by that stage’s seconds. crashes: true flips the car when it
meets a solid prop off the road, or another car at speed (Phase::Crashed for
three seconds, then it is put back on the road). aspects give each art’s
width over height. Everything the package draws sits between z_base - 70
(the sky) and z_base plus the number of sprites in view.
A stage row
Section titled “A stage row”A recipe - s:N straight, c:CURVE:N a bend, h:HEIGHT:N a hill, ch:C:H:N
both, t:N / tc:C:N a tunnel - plus its clock, traffic and colours. Every
field Sunset Coast added defaults off, so a Coral Drive row reads as it always
did: srgb (colours decoded from sRGB), theme (for the game’s backdrop),
sky_top / sky_mid / sky_low (a gradient the package draws), haze /
haze_amount, tunnel_* colours, scenery (rules - an art, how often, how
far out, which side, solid or not, and curves for chevrons on the outside of
a bend), left / right (the route), fork_slices, and gate /
goal_gate / fork_sign arts.
Script commands
Section titled “Script commands”| Call | Does |
|---|---|
Scaler.start(e) | begin the selected stage (a route from its first) |
Scaler.input(e, steer, throttle, brake) | one frame of driving, -1..1 each |
Scaler.gear(e, g) | 0 no gearbox, 1 low (pulls hard, tops out at 62%), 2 high |
Scaler.next(e) | move to the next stage |
Scaler.quit(e) | abandon the drive (it stops ticking until the next start) |
The package publishes a cd_drive custom bag each frame: the phase, clock,
speed, the car’s offset and lean, the stage and its theme, the palette blend
between stages, the road ahead (curve, hold_ahead, the fork’s distances and
its branches’ names), the nearest cars, and the frame’s events (extended,
fork, crashed, recovered, checkpoint, finished, timeup, …).
tests/coral_scene.rs and tests/sunset_route.rs load the two games’ real
stage tables and scenes through the refusal, and the latter drives all five of
Sunset Coast’s goals through the commands its Lua sends.