jengine-launcher
A physics launcher: materials, ammo and levels as data, structures built as real bodies, and damage from the impulse of a contact.
| Kind | Project package: linked into the builds of the projects that list it |
| Source | packages/jengine-launcher |
| Version | 0.1.0 |
| Entry point | jengine_launcher::LauncherPackage |
| Script namespace | Launcher |
| Listed by | projects/arcade-classics |
Using it
Section titled “Using it”List it in the top-level "packages" array of project.jengine:
{ "packages": [ { "name": "jengine-launcher", "source": { "type": "path", "path": "../../packages/jengine-launcher" } } ]}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 Launcher == nil then return end.
Components
Section titled “Components”| Scene key | Rust type | |
|---|---|---|
launcher_run | LauncherRun | 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 |
|---|---|
Launcher.level(e, index) | launch_level |
Launcher.aim(e, angle, power, gravity) | launch_aim |
Launcher.fire(e, angle, power) | launch_fire |
Launcher.trick(e) | launch_trick |
Launcher.impact(e, a, b, impulse, x, y) | launch_impact |
Scenes that use it
Section titled “Scenes that use it”projects/arcade-classics/assets/scenes/bay.scene
From packages/jengine-launcher/README.md.
Throw something at a structure and knock it down.
Used by projects/arcade-classics (Tumble Bay).
| Module | What it is |
|---|---|
content | Materials, ammo and levels as rows in the project’s data tables. A level’s structure is an ASCII drawing in its row. |
run | The level as real bodies in the world, the shots, the scoring, and what an impact does. |
The package does not simulate
Section titled “The package does not simulate”Rapier does. This package builds the structure out of real bodies, decides what an impact means, and knows when the level is over — which is the part worth reusing. Getting that split wrong is how a physics game ends up with two half-simulations that disagree.
Damage is the impulse, not the speed
Section titled “Damage is the impulse, not the speed”A block breaks when it is hit hard, and “hard” is the solver’s own
per-contact impulse — the number the engine started reporting in Sep 2026
(BUGS #131). The script forwards its on_collision_enter payload with
Launcher.impact, and that payload is the game:
function on_collision_enter(entity, other, impulse, x, y) Launcher.impact(bay, entity, other, impulse, x, y)endBefore the impulse existed, a launcher had to guess from velocities read a frame late, on entities it had to know about in advance. Two things fall out of having the real number: a resting stack reports contacts whose impulses are tiny next to any landing, so a threshold separates “leaning on” from “hit”, and a material’s toughness is one number in a data table rather than a rule in Lua.
A body’s Velocity2D is its velocity both ways since Oct 2026 - the
physics writes it back, and a trick that writes it sets the body’s speed.
Before that it was a seed (BUGS #132), and the engine’s motion step carried
every body by it a second time, so the shot flew well past the arc it
previewed; this package’s tests stepped physics alone and never saw it. They
step as a host does now. The run still decides “has the world settled” from
positions, which says the same thing without trusting any one component.
The structure is an ASCII drawing
Section titled “The structure is an ASCII drawing”A level row carries its blocks as rows of characters — one letter per
material, . for air — so a level is authored in a data table and read by a
person. Each character becomes a rapier body of that material’s size, mass,
friction and toughness; targets are the ones that have to go.
The component
Section titled “The component”launcher_run on one entity, in a "version": 2 scene. The scene holds
what is always there — the sky, the ground and its collider, the sling —
and the level itself is built when the game starts one.
"launcher_run": { "tables": { "materials": "data/materials", "ammo": "data/ammo", "levels": "data/levels" }, "origin": [-520.0, -60.0], "level": 0}Script commands
Section titled “Script commands”| Call | Does |
|---|---|
Launcher.level(e, index) | build that level (-1 rebuilds the current one) |
Launcher.fire(e, angle, power) | let one go: radians, and 0..1 |
Launcher.trick(e) | the shot’s own trick, mid-flight |
Launcher.impact(e, a, b, impulse, x, y) | forward a collision, with how hard it was |
What scripts read
Section titled “What scripts read”LaunchStateevery frame:phase(aiming,flying,won,lost),level,name,score,shots,next(the ammo on the sling and its trick),targets, andblocks— each with its material, size, damage and whether it is a target.LaunchEvents.events:fire,hit,break,settle,win,lose.