Install · Emulators · Devices · Verification · Building · Development · Design

Development guide

How the code is organised, how to write a new game, and how to test it.

Architecture

games/NNN-slug/src/<pkg>/<Canvas>.java   the game: a subclass of gamekit.Game
games/NNN-slug/src/<pkg>/<Name>MIDlet.java   generated by tools/scaffold.py
games/NNN-slug/game.properties           metadata: the single source of truth for docs and the website
common/src/gamekit/                      shared framework compiled into every JAR
emu/core/                                MIDP subset used by the tests and the browser demos

Each JAR contains only its own game package plus gamekit, so every game builds and installs on its own.

gamekit

ClassPurpose
GameAbstract full-screen Canvas with the game loop (tickMs = 50 ms, 20 updates per second), title menu, help pages, pause menu, game-over screen, input latching, best-score persistence and crash capture.
GameMIDletBase MIDlet; the generated subclass just creates the game canvas.
GfxFonts (SMALL, SMALL_B, MEDIUM, LARGE), anchors, panels, bevels, bars, text wrapping, discs/rings, colour mixing and shading, palette sprites, time() formatting.
SfxShort tones through Manager.playTone with a global on/off switch.
RndSmall fast PRNG: nextInt, range, chance, shuffle.
FMathInteger maths: 256-unit angles, sin/cos scaled by 1024, atan2, sqrt, dist, clamp, sign, bits.
TileMap, BodyTile collision for platformers.
CardsCard faces and pips drawn at any scale.

The Game lifecycle

Game owns the states TITLE, HELP, PLAY, PAUSE and OVER. A game implements:

protected String name();                 // title shown on the title screen
protected String[] help();               // help pages; a line starting "- " is a heading
protected void newGame();                // reset state for a new round (mode is already set)
protected void update();                 // one 50 ms tick of game logic
protected void draw(Graphics g);         // draw the play screen (W x H)

Optional hooks: modes() (difficulty or variant names, picked on the title menu), drawTitleArt(), accent() (theme colour), hasScore(), lowerIsBetter(), formatScore(), footer(), pauseKeys().

Inside update() read input from the bit sets held, pressed (pressed since the last tick) and tapped:

BitKeys
K_UP, K_DOWN, K_LEFT, K_RIGHTjoystick, or 2 / 8 / 4 / 6
K_FIREjoystick centre, or 5
K_STAR, K_POUND, K_SOFT*, #, soft keys
K_NUM0 << ndigit n (digit(n), digitPressed())

Note that 2/4/5/6/8 set both a direction bit and a digit bit. Games that use digits as commands should check digitPressed() before the direction bits, or avoid 2/4/6/8 for commands.

End a round with endGame(won) after setting score and optionally headline. The framework shows the result, stores a best score in RMS and offers again or menu.

Rules for phone-safe code

Adding a game

  1. Pick the next id and a slug, for example 101-hex-hop.
  2. Create games/101-hex-hop/game.properties:
    game.id=101
    game.slug=hex-hop
    game.name=Hex Hop
    game.jar=HexHop
    game.midlet=hexhop.HexHopMIDlet
    game.canvas=hexhop.HexGame
    game.category=Puzzle
    game.version=1.0.0
    game.summary=One line for tables and cards.
    game.description=A paragraph for the README and the website.
    game.objective=What winning means.
    game.controls=2/4/6/8:Move|5:Jump|*:Pause menu
    game.modes=Easy|Hard
    game.tags=puzzle,hexagons
    game.featured=false
    game.demo.script=w500 shot:title 5 w300 gif:start rand:2500 gif:stop:demo shot:play
  3. Write games/101-hex-hop/src/hexhop/HexGame.java extending gamekit.Game.
  4. Generate the MIDlet and build file, then build and test:
    python tools/scaffold.py 101
    python tools/build_all.py 101
    python tools/run_tests.py 101
    python tools/microemu_check.py 101
  5. Look at the screenshots in games/101-hex-hop/build/test/<size>/ for every size.
  6. python tools/build_all.py --target media 101, then python tools/gen_docs.py and python tools/validate.py.

Test scripts

game.demo.script (media) and the optional game.test.script (tests) drive the harness:

TokenMeaning
0-9, *, #tap a key
U D L R Fjoystick up/down/left/right/fire
SL SRsoft keys
wNNNwait NNN ms
hK:NNNhold key K for NNN ms
rand:NNNrandom keypad input for NNN ms
shot:namesave a screenshot
gif:start / gif:stop:namerecord an animated GIF
defaultthe standard test sequence

Browser demos

emu/web implements the runtime's backend on top of window.j2me (see website/src/assets/js/j2me-runtime.js). The web Ant target compiles the game, gamekit, emu/core and emu/web with javac, then TeaVM turns the classes into one game.js. The same game code runs in the browser and on phones; only the MIDP implementation underneath differs, which is why the website labels these as Browser Demo and links to the original Java ME files.

Releasing

tools/release.py creates one GitHub Release per game (game-NNN-v1.0.0) with the JAR and JAD attached, and the milestone releases with the all-games ZIP. It needs the GitHub CLI (gh) logged in with permission to the repository. Re-running it skips releases that already exist.

Source: docs/development.md