{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreidaixhgyep4tyd4xaa4ax4kisirltqbnsyo7oxrqkfxurlrhe3l6a",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpo6x25lrad2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreigjv6wtivwjqpwxjjsbb4n2hulny2n6n5qtyj5s6xcpw5fcuvasca"
},
"mimeType": "image/webp",
"size": 45276
},
"path": "/codenameone/game-builder-tutorial-1-build-a-2d-platformer-dukes-coffee-run-45b0",
"publishedAt": "2026-07-02T13:33:16.000Z",
"site": "https://dev.to",
"tags": [
"java",
"mobile",
"android",
"ios",
"Game Builder",
"codenameone.com",
"2 (board game)",
"3 (3D dungeon)",
"getting-started guide",
"Codename One initializr",
"duke_run.png",
"coffee.png",
"exception.png",
"duke_sheet_raw.png",
"Tutorial 2 — a turn-based board game",
"@Override"
],
"textContent": "Most game tutorials make you hand-place every sprite in code. The Game Builder flips that around: you **draw** the level visually, tag objects with the numbers your game needs (`lives`, `value`, `speed`), and the editor saves it as a small data file that the runtime plays. Your code shrinks to the part that's actually _yours_ — the rules.\n\n**What is Codename One?** Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at codenameone.com.\n\nThis is the first of three tutorials. We'll build **Duke's Coffee Run** : a side-scroller where Java's mascot **Duke** dashes right across a grassy floor, collects **coffee cups** for points, dodges roaming **exception monsters** , and reaches a flag to win. Along the way we'll do the part most tutorials quietly skip — **bringing in real art** , including slicing an animated sprite sheet for Duke. By the end you'll have a _running game_ and understand every moving part: the assets, the level file, the generated companion class, the built-in arcade behavior, and where your own logic goes. Tutorials 2 (board game) and 3 (3D dungeon) continue the story.\n\n## Why a game builder?\n\nA Codename One game is a `GameView` holding a `Scene` of sprites, driven by a `GameInput`. Building a level purely in Java means dozens of `new Sprite(...)` / `setX`/`setY` calls that you can't _see_ until you run them, and that you must recompile to tweak. The builder replaces that with a visual editor and a plain-data level file, so:\n\n * **Designers and developers** can both touch the level — it's data, not code.\n * You **iterate visually** — drag a coffee cup, hit play, repeat — with no rebuild.\n * The same file ships to **2D, isometric board, and 3D** runtimes unchanged.\n\n\n\n## Step 0 — Create the project and scaffold a scene\n\nThe builder attaches to a **Java 17** Codename One project. Creating that project and installing the toolchain are covered in the getting-started guide — generate one from the Codename One initializr (pick Java 17) and you're ready. Then scaffold a scene and open the editor:\n\n\n\n mvn cn1:create-game-scene -DclassName=com.example.coffeerun.CoffeeRun\n mvn cn1:gamebuilder\n\n\n`create-game-scene` writes an **empty** `CoffeeRun.game` level and a `CoffeeRun.java` companion into your project (2D is the default; pass `-Dmode=board` or `-Dmode=3d` for the other tutorials), and `cn1:gamebuilder` opens the editor on it. _Empty_ is the honest word: you get a grid, four layers, and the starter art packs — but no level laid out, and no Duke. Three panels matter: the **Hierarchy** (layers and objects), the **Asset Library** (the art you stamp down), and the **Inspector** (the selected object's transform and behavior values). The layers — Background, Terrain, Items, Actors — set draw order (Background paints first) and let you hide or lock parts of the level while you work.\n\n## Step 1 — Get your art in\n\nA real game starts with art, so let's do that first instead of pretending sprites appear by magic. An _asset_ is a reusable definition — an id (`grass`, `coffee`, `player`), a kind (a grid **tile** or a freely placed **actor**), default properties, and a pointer to its **art file**. Each art file is one of three **formats** :\n\n * **Image** — a static `.png`/`.jpg` → a `Sprite` (ground, coffee cups, mountains).\n * **Sprite sheet** — one image of equal frames in a grid plus a frame size and rate → an `AnimatedSprite` that plays the frames (Duke's run cycle).\n * **Mesh** — a glTF/`.glb` model → a `Model` in a 3D level (Tutorial 3).\n\n\n\nThe starter packs already include `grass`, `coffee`, an `exception` monster and even a Duke `player`, all as real image files you can open and replace — enough to build the whole game without importing anything. But the point of a tutorial is the real workflow, so we'll **bring in our own animated Duke**.\n\n**Download the assets** used here and drop them in your project's `src/main/resources/games/assets/` folder:\n\n * duke_run.png — Duke's 5-frame run cycle (already sliced and ready)\n * coffee.png and exception.png — the collectible and the enemy\n * duke_sheet_raw.png — the _raw_ AI-generated sheet, to show what \"integrating an asset\" really involves\n\n\n\nThat raw sheet is what you usually start with — multiple animations, labels, padding, a solid background:\n\nA `SpriteSheet` in Codename One needs a clean grid of **equal-sized frames** with a transparent background, so a sheet like this needs a one-time prep pass: crop out the RUNNING frames, make the black background transparent, and lay them out evenly. The result is a tidy horizontal strip:\n\nNow wire it up as an asset. Sprite sheets carry a little metadata (frame size + rate), so add an entry to a pack JSON (or edit the bundled `/gamebuilder-packs.json`):\n\n\n\n { \"id\": \"player\", \"name\": \"Duke\", \"kind\": \"actor\",\n \"type\": \"sheet\", \"source\": \"duke_run.png\",\n \"frameW\": 269, \"frameH\": 312, \"frames\": 5, \"fps\": 12,\n \"w\": 42, \"h\": 48, \"unique\": true,\n \"defaults\": { \"lives\": 3, \"jumpHeight\": 110 } }\n\n\n`type: \"sheet\"` plus `frameW`/`frameH` tell the runtime to slice `duke_run.png` into frames and play them as an `AnimatedSprite`; `w`/`h` are the on-screen size (the big 269×312 frames are scaled down to a 42×48 sprite). Static images (`coffee.png`, `exception.png`) need no metadata — `type` defaults to image. Plain images you can also bring in with the Asset Library's **Import** button, which copies the file into `games/assets/` and adds it to a _Custom_ pack for you; sheets and meshes use the file-plus-pack-entry route above. With the art in place, the **Asset Library** now shows Duke, coffee and the exception monster ready to stamp down.\n\n## Step 2 — Paint the ground\n\nSelect the **Terrain** layer, pick the **Grass** tile, and drag across the bottom row. _Why a tile layer?_ Tiles are a compact grid of `assetId`-per-cell — perfect for a floor of identical blocks — and the runtime batch-renders them, so a long floor costs almost nothing. Leave a gap or raise a few tiles to make a ledge to jump.\n\n## Step 3 — A parallax background (clouds and mountains)\n\nThe bottom **Background** layer scrolls _slower_ than the rest — a real parallax effect that gives the scene depth. Select it, then paint **Mountain** along the horizon and a few **Cloud** tiles up in the sky. This isn't a painted-on trick: every layer carries its own **parallax factor** (the Background layer ships at `0.4`), and the renderer offsets each layer by `camera × factor`, so distant tiles drift behind the action as the camera follows the player. A parallax layer is decoration — its tiles never block the player, unlike the solid Terrain.\n\n## Step 4 — Place Duke and give him behavior\n\nSelect the **Actors** layer, pick **Duke** , and click where he starts. _Actors_ are freely positioned objects (not grid-snapped like tiles), because a character lives at an arbitrary point and moves smoothly. Give him the **Name** `player` (the editor turns each named object into a field you can use in code — and the built-in arcade behavior treats the `player` asset as the hero), then open the Inspector's **Behavior** section and set the numbers your code reads: `lives = 3` and `jumpHeight = 110`. Use **Add property** for any custom field — these are just typed key/value pairs stored with the object. Because Duke is a sprite sheet, he runs as an animation the moment you press Live.\n\n## Step 5 — Scatter coffee cups to collect\n\nPick **Coffee** and stamp three above the floor. Give each a `value` (say `10`) in its Behavior section — that's the score it's worth. Coffee is the _task_ : the reason to dash across the level. The built-in arcade behavior collects a cup on contact and adds its `value` to the score, so you can test immediately.\n\n## Step 6 — Add an exception monster and a goal\n\nA game needs stakes and an end. Pick **Exception** — a roaming bug-monster — and place it on the floor to the right; give it `speed = 45` so it patrols. Then pick **Flag** and place it at the far edge — reaching it is winning. Now the level has a loop: _dash right, grab coffee, time your jump past the exception, touch the flag._\n\n## Step 7 — Scene-wide rules\n\nDeselect everything to edit the whole level. In the Inspector set **Gravity** (try `9.8`) and the **Background** (Sky). Gravity is a level property the platformer physics reads — raise it for a heavier, snappier feel, lower it for floaty moon-jumps.\n\n## Step 8 — Play it\n\nPress **Live**. Move with the **arrow keys** , **Up / Space** to jump. Duke's run cycle animates, gravity and tile collision are simulated, coffee cups add to the SCORE, and the exception monster patrols. **Stop** returns to editing — playing never mutates your level.\n\nThe same level, live in the preview — Duke dashes right, hops the exception monster, grabs the coffee:\n\n## What just got saved? The `.game` file\n\n**Save** writes the level to `src/main/resources/games/CoffeeRun.game`. It's plain JSON — readable, diff-able, and yours to ship as a resource. (Codename One's resource namespace is flat, so at runtime you load it as `/CoffeeRun.game`, not `/games/CoffeeRun.game` — the `games/` folder just keeps your sources tidy.) A trimmed version:\n\n\n\n {\n \"mode\": \"2d\", \"cols\": 26, \"rows\": 16, \"tileSize\": 32,\n \"props\": { \"gravity\": 9.8, \"background\": \"Sky\" },\n \"layers\": [\n { \"name\": \"Background\", \"kind\": \"entity\", \"band\": 0, \"parallaxX\": 0.4, \"parallaxY\": 0.6 },\n { \"name\": \"Terrain\", \"kind\": \"tile\", \"band\": 1, \"tiles\": { \"0,14\": \"grass\", \"1,14\": \"grass\" } },\n { \"name\": \"Items\", \"kind\": \"entity\", \"band\": 2 },\n { \"name\": \"Actors\", \"kind\": \"entity\", \"band\": 3 }\n ],\n \"elements\": [\n { \"id\": \"e1\", \"assetId\": \"mountain\", \"name\": \"mountain\", \"layer\": \"Background\",\n \"x\": 300, \"y\": 361, \"scaleX\": 1.45, \"scaleY\": 1.45, \"scaleZ\": 1.45 },\n { \"id\": \"e2\", \"assetId\": \"player\", \"name\": \"player\", \"layer\": \"Actors\", \"x\": 64, \"y\": 416,\n \"props\": { \"lives\": 3, \"jumpHeight\": 110 } },\n { \"id\": \"e3\", \"assetId\": \"coffee\", \"layer\": \"Actors\", \"x\": 160, \"y\": 384, \"props\": { \"value\": 10 } },\n { \"id\": \"e4\", \"assetId\": \"exception\", \"name\": \"enemy\", \"layer\": \"Actors\", \"x\": 352, \"y\": 416, \"props\": { \"speed\": 45 } },\n { \"id\": \"e5\", \"assetId\": \"flag\", \"name\": \"flag\", \"layer\": \"Actors\", \"x\": 768, \"y\": 416 }\n ]\n }\n\n\nThe `player` element points at the `player` asset (our Duke sprite sheet); the named objects (`mountain`, `player`, `enemy`, `flag`) become the generated fields below; the `Background` layer's `parallaxX`/`parallaxY` are what make it drift.\n\nNothing is hard-coded into Java. To re-edit, run `mvn cn1:gamebuilder` again — the editor rewrites this file and _preserves your code_.\n\n## Loading and showing the game\n\n`create-game-scene` generated the companion class. The part between the `DO NOT EDIT` markers loads the `.game` resource **and wires every object you named in the editor to a field** — because you named the player `player`, the editor generated a `player` field and even seeded `lives` from its property. The constructor and `onUpdate` are yours:\n\n\n\n public class CoffeeRun extends GameSceneView {\n public CoffeeRun(AssetCatalog catalog) {\n super(loadLevel(), catalog); // realizes the level into a Scene of Sprites\n initScene();\n setArcadeBehavior(true); // built-in run/jump/gravity/patrol/pickups (2D)\n }\n\n //-- GAMEBUILDER GENERATED - DO NOT EDIT BELOW\n /// The \"player\" object you placed in the editor (Duke).\n protected Sprite player;\n /// The \"enemy\" object you placed in the editor.\n protected Sprite enemy;\n /// The \"flag\" object you placed in the editor.\n protected Sprite flag;\n private static GameLevel loadLevel() {\n try {\n return GameLevel.load(Display.getInstance().getResourceAsStream(CoffeeRun.class, \"/CoffeeRun.game\"));\n } catch (java.io.IOException err) {\n throw new RuntimeException(\"failed to load level /CoffeeRun.game\", err);\n }\n }\n\n private void initScene() {\n player = findByName(\"player\");\n enemy = findByName(\"enemy\");\n flag = findByName(\"flag\");\n if (player != null) {\n setLives(elementOf(player).getInt(\"lives\", 3));\n }\n }\n //-- GAMEBUILDER GENERATED - DO NOT EDIT ABOVE\n\n @Override\n protected void onUpdate(double deltaSeconds) {\n // your game logic — see below\n }\n }\n\n\nYou don't write `findByName`/`setLives` yourself — the editor regenerates that block every time you save, so renaming or adding objects in the Inspector just updates your fields. The `setArcadeBehavior(true)` call is the important one: for a 2D scene the editor turns on the same gravity, run, jump, patrol and pickup behavior you play-tested in the preview, so the generated game is **playable before you write a line of logic**. (More on opting out below.)\n\n`GameSceneView` is a `GameView` (a Codename One `Component`), so you show it like any other and `start()` its loop:\n\n\n\n public class CoffeeRunApp {\n public void start() {\n Form f = new Form(\"Duke's Coffee Run\", new BorderLayout());\n CoffeeRun game = new CoffeeRun(StarterAssets.catalog()); // your AssetCatalog\n f.add(BorderLayout.CENTER, game);\n f.show();\n game.start(); // begins the game loop\n }\n }\n\n\nThat's a complete, playable game already — the floor, coffee, exception monster and flag are live sprites, and because the constructor called `setArcadeBehavior(true)` Duke runs (animated), jumps and falls, the exception patrols, and coffee scores on contact. Everything after this is _your_ rules.\n\n## Preview, runtime, and the arcade behavior\n\nOne thing worth being precise about, because it trips people up: **the editor's Live preview and a shipped`GameSceneView` are two different runtimes.** The preview is a play-test simulator built into the editor; a shipped game is your companion class. So a bare `GameSceneView` does _nothing_ automatically except realize the level and call your `onUpdate` each frame — it has no built-in gravity or movement of its own.\n\nWhat makes them match is `setArcadeBehavior(true)`. It enables, at runtime, the exact behavior the preview shows:\n\n * **Gravity** from the level's `gravity` property and **run** (Left/Right) at `walkSpeed`.\n * **Jump** (Up / Space / Fire) using the player's `jumpHeight`, with **tile collision** against the solid Terrain (parallax background layers don't block).\n * **Enemy patrol** for every `exception`/`slime`/`enemy*`/`npc*`, turning at walls.\n * **Pickups** : touching a `coffee`/`coin`/`gem`/`star`/`token` scores its `value`; touching an enemy costs a life.\n * A **follow camera** that scrolls to keep the player in view (which is what makes your parallax background drift).\n\n\n\nEvery one of those is a `protected` method you can override — `updatePlayer`, `updateEnemies`, `onPickup`, `onPlayerHit`, `isCollectible`, `isEnemy`, `isSolidAt` — so \"overriding defaults\" means _override the hook_ (or call `setArcadeBehavior(false)` and do everything yourself). And `GameSceneView` hands you the small helpers every loop needs, so you never re-roll them: `findByAsset(id)`/`findByName(name)` locate a sprite, `findAllByAsset(id)` returns every match, `elementOf(sprite)` reads its editor properties, `overlaps(a, b)` is a null-safe collision test, and `addScore`/`getScore`/`loseLife`/`getLives`/`isGameOver` track game state.\n\n## Your rules — winning, power-ups, and death\n\nThe arcade behavior already runs Duke's movement, the coffee pickups and the exception monster, so your `onUpdate` only has to add what the engine _can't_ know: the win condition. Reaching the flag wins:\n\n\n\n private boolean won;\n\n @Override\n protected void onUpdate(double deltaSeconds) {\n if (player == null || won) {\n return;\n }\n if (overlaps(player, flag)) { // reach the goal you named \"flag\"\n won = true;\n youWin(getScore());\n }\n }\n\n\nEverything else is a one-method override of an arcade hook — that's the whole point of \"overriding defaults.\" A **power-up** is just an `onPickup` that branches on the asset id; return `false` to keep an item in the world, `true` to consume it:\n\n\n\n @Override\n protected boolean onPickup(Sprite item) {\n GameElement el = elementOf(item);\n switch (el.getAssetId()) {\n case \"gem\" -> { addScore(el.getInt(\"value\", 50)); return true; } // bonus\n case \"star\" -> { invincibleUntil = time() + 5; return true; } // power-up\n default -> { return super.onPickup(item); } // coffee: default scoring\n }\n }\n\n\n**Hit, HP and respawn** live in `onPlayerHit`. The default costs one life and respawns at the start; override it for hit points, invulnerability frames, a checkpoint, or a death screen:\n\n\n\n private int hp = 3;\n private double checkpointX, checkpointY;\n\n @Override\n protected void onPlayerHit(Sprite enemy) {\n if (time() < invincibleUntil) {\n return; // a star makes you immune\n }\n if (--hp > 0) {\n player.setPosition(checkpointX, checkpointY); // back to the last savepoint\n } else {\n loseLife(); // out of HP: lose a life (GameSceneView counts them)\n hp = 3;\n if (isGameOver()) {\n showDeathScreen();\n }\n }\n }\n\n\nA **checkpoint** is just a collision branch in `onUpdate` that records where to respawn — `if (overlaps(player, flag)) { checkpointX = player.getX(); ... }`. None of this is framework ceremony: it's reading the numbers you set in the editor and calling the same `loseLife`/`isGameOver`/`addScore` helpers the engine uses.\n\nBecause you edit `onUpdate` and overrides — not the generated block — re-running `cn1:gamebuilder` to tweak the level **keeps this logic intact**.\n\n## Physics, effects and overriding defaults\n\nThe starter movement is lightweight arcade physics. When you want more, the gaming API has the real thing — here are concrete drop-ins.\n\n**Real rigid-body physics (Box2D).** Swap the arcade jump for an actual physics world: gravity, stacking, slopes, and bounce. A `PhysicsBody` linked to your sprite writes its transform back into the sprite on every `step`:\n\n\n\n import com.codename1.gaming.physics.PhysicsWorld;\n import com.codename1.gaming.physics.PhysicsBody;\n import com.codename1.gaming.physics.BodyType;\n\n private PhysicsWorld physics;\n private PhysicsBody body;\n\n private void enablePhysics() { // call once, after the level loads\n physics = new PhysicsWorld(0, (float) (getLevel().getDouble(\"gravity\", 9.8) * 100));\n physics.createBox(0, getHeight() - 16, getWidth(), 16, BodyType.STATIC); // the floor\n body = physics.createBox((float) player.getX(), (float) player.getY(), 24, 32, BodyType.DYNAMIC);\n body.setLinkedSprite(player); // step() drives the sprite\n }\n\n // in onUpdate:\n physics.step((float) deltaSeconds);\n if (getInput().wasKeyPressed(Display.GAME_FIRE)) {\n body.applyLinearImpulse(0, -400); // jump\n }\n\n\n**Sound and effects.** Add a little juice by overriding `onPickup` — the gaming API ships a low-latency `SoundPool`:\n\n\n\n private final SoundPool sound = SoundPool.create(8);\n private SoundEffect sipSfx; // sipSfx = sound.load(\"/sip.wav\");\n\n @Override\n protected boolean onPickup(Sprite item) {\n sound.play(sipSfx); // slurp!\n int shake = (int) (Math.random() * 5 - 2); // a one-frame screen-shake\n getScene().setCamera(shake, shake);\n return super.onPickup(item); // keep the default scoring\n }\n\n\n**Opting out entirely.** The arcade behavior is a convenience, not a constraint. Don't want it? Call `setArcadeBehavior(false)` (or delete it from the constructor) and drive everything from `onUpdate` — poll `getInput().isGameKeyDown(Display.GAME_RIGHT)` and move the `player` sprite yourself. You can also keep the behavior but replace one piece by overriding a single hook (`updatePlayer`, `updateEnemies`, …) — the rest still runs.\n\n## Menus, HUD and pause\n\nThis is the part most game engines make painful and Codename One makes trivial: **the menus**. A `GameSceneView` is an ordinary Codename One `Component`, so the _entire_ Codename One UI toolkit — `Form`, `Toolbar`, `Dialog`, layouts, CSS theming, animations — is right there around your game. The level select, the pause screen, the settings page, the score HUD: all of it is the same UI API you'd use for any app, not a bespoke game-UI framework you have to learn.\n\nA **score/lives HUD** is just a label laid over the game:\n\n\n\n Form f = new Form(\"Duke's Coffee Run\", new BorderLayout());\n Label hud = new Label(\"Score 0 Lives 3\");\n f.add(BorderLayout.NORTH, hud).add(BorderLayout.CENTER, game);\n // update it each frame from onUpdate via callSerially:\n hud.setText(\"Score \" + getScore() + \" Lives \" + getLives());\n\n\nA **pause menu** is a one-line `Dialog` over the frozen game:\n\n\n\n game.stop(); // freeze the loop\n Command resume = new Command(\"Resume\");\n Command quit = new Command(\"Quit to menu\");\n if (Dialog.show(\"Paused\", \"Score: \" + game.getScore(), resume, quit) == resume) {\n game.start();\n } else {\n showLevelMenu();\n }\n\n\nAnd a **level-select screen** is a themed `Form` with a real toolbar — the kind of polish that's a slog elsewhere:\n\n\n\n Form menu = new Form(\"Duke's Coffee Run\", BoxLayout.y());\n menu.getToolbar().addCommandToRightBar(\"Settings\", null, e -> showSettings());\n for (String level : new String[] {\"CoffeeRun\", \"Caverns\", \"SkyRun\"}) {\n Button play = new Button(level);\n play.addActionListener(e -> startLevel(level)); // loads /<level>.game\n menu.add(play);\n }\n menu.show();\n\n\nBecause it's all standard Codename One UI, your menus inherit your app's theme, your fonts, right-to-left support, accessibility and the simulator's live preview — for free.\n\n## The finished game and where to go next\n\nYou now have **Duke's Coffee Run** : real animated art, a drawn level, an enemy, a goal, and a handful of lines of rules. Natural next steps:\n\n * **More levels** — ship many `.game` files and load whichever the player picks from the level-select screen above; the companion pattern is identical.\n * **Use Duke's idle animation** — you also downloaded `duke_idle.png`; swap Duke's sheet to it when he's standing still, the same way you wired the run cycle.\n * **Smarter enemies** — read a `patrol` range or `behavior` property per exception monster and branch in `onUpdate`.\n * **Hazards** — the **Spike** tile and a `damage` property make instant-death floors.\n\n\n\nNext: Tutorial 2 — a turn-based board game, where Duke trades the coffee dash for a strategy board — and the same data-plus-`onUpdate` pattern drives game _rules_ instead of arcade motion.",
"title": "Game Builder Tutorial 1: Build a 2D Platformer — Duke's Coffee Run"
}