You will create a complete 2D platformer, play it, change it without opening
the editor, catch the change breaking the level with an automated test,
take a screenshot, and export a finished build. Every step uses the
rusting CLI, so the same loop works in scripts, CI, and for coding agents.
In the commands below, rusting means ./target/debug/rusting from the
engine checkout (build it with cargo build --bin rusting). Add --json to
any command for machine-readable output.
1. Create the game
mkdir -p ~/Code/games && cd ~/Code/games
rusting new . "Coin Run" --template starterThe starter template is a finished game made only of scene data. Nothing in
src/main.rs is game-specific:
- Player: a kinematic body with
rusting.platformer_controller. It reads theplayer.left,player.right, andplayer.jumpactions (A/D or the arrow keys, and Space). - Level: a
rusting.tile_map. Each text row is a row of tiles; solid runs become box colliders. - Coin 1 to Coin 5 and Flag:
rusting.pickupcomponents. Each coin adds 1 to thecoinscounter and vanishes with arusting.burst_emittersparkle. The flagrequiresthecoinscounter to be complete. - Score: a
rusting.counter(target 5) and arusting.hudline with the textCoins {coins}/5. - Win: a HUD element that appears once the flag is collected.
List the entities and their IDs:
rusting scene query "Coin Run/scenes/main.rscene" --component rusting.pickupMatches: 6
23ed39b2-5570-4c30-96cb-307ed4f6c346 Coin 4
...
9b072a85-b675-47d9-ae11-a1c0f9d9fd61 Flag
Revision: 848322f96b4e1562Your IDs will differ: every new project gets fresh ones. Add --json to see
every component’s fields.
2. Play it
rusting run "Coin Run"Collect the five coins, then touch the flag. The first build takes a few minutes.
3. Test that the level can be won
The engine repository has a scenario that plays the level:
samples/starter_game/win.scenario.json. Copy it into the project:
cp <engine>/samples/starter_game/win.scenario.json "Coin Run/"
rusting test "Coin Run" "Coin Run/win.scenario.json"Scenario "coin run: collect every coin, then win": passed after 240 ticks, seed 1The scenario holds player.right from tick 1, presses player.jump at four
ticks, and checks the game along the way:
{"tick": 25, "expect": {"entity": "Coin 1", "path": "/components/rusting.pickup/collected", "equals": true}},
{"tick": 190, "expect": {"entity": "Score", "path": "/components/rusting.counter/value", "equals": 5}},
{"tick": 235, "expect": {"entity": "Win", "path": "/components/rusting.counter/value", "equals": 1}}A path is a JSON pointer into the entity as scene query --json shows it,
with component fields parsed. The run has no window and one tick per
update, so it gives the same result on every machine.
4. Change the game with a scene patch
Make the player jump higher. Put the Player’s ID in jump.json:
{
"operations": [
{
"op": "set",
"id": "PLAYER-ID",
"path": "/components/rusting.platformer_controller/jump_speed",
"value": 10.0,
"expected": 8.0
}
]
}Preview it, then apply it:
rusting scene patch "Coin Run/scenes/main.rscene" jump.json --dry-run
rusting scene patch "Coin Run/scenes/main.rscene" jump.jsonPatched a23c9aec82c1cca7 -> 840dfb80d693e5e0: 0 created, 0 deleted, 1 field changes
Player/components/rusting.platformer_controller/jump_speed: 8.0 -> 10.0A patch applies every operation or none. expected makes it fail with
SCENE_CONFLICT if someone changed the value since you read it; a top-level
"expected_revision" does the same for the whole file. Other operations
are create, duplicate, reparent, remove (a component), and delete
(an entity). See the README.
If the editor has the scene open, it reloads the file behind an Undo step.
5. Let the test catch the regression
Run the scenario again:
rusting test "Coin Run" "Coin Run/win.scenario.json"error [SCENARIO_FAILED]: tick 235 step 13: `Win` /components/rusting.counter/value is 0, expected 1 ± 0The higher jump changes the player’s path, and the recorded inputs no
longer win the level. The failure names the first failing tick and step and
the value it saw. Put the jump back by applying the patch in reverse
("value": 8.0, "expected": 10.0) and the test passes again.
6. Restyle it
Presets write a look as ordinary scene data (sun, ambient and sky light,
tone mapping, background, camera, and HUD style): daylight,
golden_hour, night, and flat_toy.
rusting preset apply "Coin Run/scenes/main.rscene" golden_hour --dry-runThe dry run lists each field it would change. Presets are tuned for lit 3D scenes, so check the result on 2D unlit art before you keep it.
7. Take a screenshot
rusting capture "Coin Run/scenes/main.rscene" shot.png --tick 60 --size 960x540 --pick 367,305Pick [367,305]: 15dd1915-... Coin 1
Output: shot.pngcapture simulates 60 ticks and renders the active camera with the HUD.
Each --pick X,Y names the object under that pixel. Game code does not run
during a capture. A scenario step {"tick": 240, "capture": "shots/win.png"}
takes a screenshot during a test instead, with game code running.
8. Export
rusting export "Coin Run" ~/exportsExported: /home/you/exports/coin_run_export
Game exit code: 0, timed out: falseexport builds a release, copies the executable, cooked scene, assets,
license, and a README into a new folder, then runs the copy for 60 headless
ticks from a fresh temporary folder to prove it starts. An existing export is
never overwritten; the next one gets _2.
What you learned
- Built-in gameplay components: platformer controller, tile map, pickups, counters, HUD, particle bursts.
scene queryandscene patchfor exact, reviewable edits.- Scenarios as regression tests.
captureandexport.
Next, Tutorial 3 adds your own Rust systems and components to this game.