Skip to content

jengine-launcher

A physics launcher: materials, ammo and levels as data, structures built as real bodies, and damage from the impulse of a contact.

KindProject package: linked into the builds of the projects that list it
Sourcepackages/jengine-launcher
Version0.1.0
Entry pointjengine_launcher::LauncherPackage
Script namespaceLauncher
Listed byprojects/arcade-classics

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.

Scene keyRust type
launcher_runLauncherRunpackage only: needs a v2 scene

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.

CallFlat 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
  • 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).

ModuleWhat it is
contentMaterials, ammo and levels as rows in the project’s data tables. A level’s structure is an ASCII drawing in its row.
runThe level as real bodies in the world, the shots, the scoring, and what an impact does.

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.

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)
end

Before 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.

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.

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
}
CallDoes
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
  • LaunchState every frame: phase (aiming, flying, won, lost), level, name, score, shots, next (the ammo on the sling and its trick), targets, and blocks — each with its material, size, damage and whether it is a target.
  • LaunchEvents.events: fire, hit, break, settle, win, lose.