{
  "$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"
}