Skip to content

Your First Project

  1. Launch the editor: ./run-editor.ps1 from the repo root (see Installation)
  2. On the Start Screen, click Create New Project
  3. Choose a folder; the project takes its name
  4. The editor writes a starter project and opens its scene: a camera, a title, and a square you can move with the arrows, WASD or a gamepad stick. Press Play to try it.

Without the editor, the same starter project comes from the command line:

Terminal window
cargo run -p jengine-builder --bin jengine -- project new projects/my-game --name "My Game"

Both write the files in templates/project/.

my-game/
├── project.jengine # Project settings (JSON)
├── assets/
│ ├── scenes/main.scene # The starter scene
│ ├── scripts/player.lua # The starter player script
│ ├── sprites/ # Images (.png, .jpg)
│ ├── sounds/ # Audio clips (.ogg, .wav, .mp3)
│ ├── prefabs/ # Entity templates (.prefab)
│ └── ui/ # GUI documents (.gui)
{
"name": "My Game",
"default_scene": "scenes/main.scene",
"settings": {
"title": "My Game",
"width": 1280,
"height": 720,
"background_color": [0.1, 0.1, 0.15, 1.0],
"target_fps": 60,
"entry_scene": "scenes/main.scene",
"physics": {
"gravity_x": 0.0,
"gravity_y": -9.81,
"backend": "rapier2d",
"fixed_timestep": 0.016666668,
"length_unit": 100.0
},
"input_actions": [
{ "name": "move_left", "keys": ["A", "Left"] },
{ "name": "move_right", "keys": ["D", "Right"] },
{ "name": "jump", "keys": ["Space", "W", "Up", "Pad0_A"] }
]
},
"packages": [],
"component_definitions": []
}

Scene paths are relative to assets/. gravity_y is multiplied by length_unit, so -9.81 with a length unit of 100 is about 981 units per second squared - one world unit is roughly a centimetre. "packages" lists the Rust packages the project links; most settings have a field in Project Settings in the editor, which is the easier way to change them.

  1. In the Scene Tree panel, right-click and select Add Entity
  2. Name it “Player”
  3. In the Inspector, click Add Component and add:
    • Transform — set position to (0, 0)
    • Sprite — select a texture from assets
    • Script — create a new Lua or JS script

Create assets/scripts/player.lua:

local speed = 200
function init(entity)
log("Player initialized!")
end
function update(entity, dt)
local x, y = Transform.get_position(entity)
if Input.is_key_pressed("D") then
x = x + speed * dt
end
if Input.is_key_pressed("A") then
x = x - speed * dt
end
Transform.set_position(entity, x, y)
end

Lua scripts call the engine through capitalised namespaces - Transform, Input, Physics, Sprite and so on; see the Scripting API.

Or in JavaScript (assets/scripts/player.js), which keeps the flat function names and runs on desktop builds only:

var speed = 200;
function init(entity) {
log("Player initialized!");
}
function update(entity, dt) {
var pos = get_position(entity);
var x = pos[0], y = pos[1];
if (is_key_pressed("D")) {
x += speed * dt;
}
if (is_key_pressed("A")) {
x -= speed * dt;
}
set_position(entity, x, y);
}
  1. Press Ctrl+S to save the scene
  2. Click the Play button in the toolbar
  3. The game runs in the viewport — test your controls!

Open Build Settings and pick a target. Each build streams its compiler output into the Build panel at the bottom of the editor.

  • Windows — a standalone .exe with its assets.
  • Web — an HTML + WASM + JS bundle for any static web server.
  • Android — an APK (debug-signed) or a Google Play bundle, identity from Project Settings → Mobile App. Lua scripts only.

A project with Rust packages gets its own runtime crate generated for each target, so its packages are linked in. The same builds run from the command line: jengine web-build projects/<name>, jengine android-build projects/<name>.

To keep a build small, name asset packs in Project Settings → Asset Pipeline → Asset Packs: each takes asset paths (a later world’s scenes, sprites and music, say), and a build moves them out of the game into packs/<name>-<hash>.jpak. Upload that folder, set its URL, and fetch a pack from a script when it is needed with Packs.download("name") - see Asset packs in the scripting API.