Skip to content

Two games for the joystick and the mouse, and png_to_bitmap.py - #11

Merged
diegoparrilla merged 3 commits into
release/v1.1.0from
epic/14-playable-example
Sep 30, 2026
Merged

diegoparrilla merged 3 commits into
release/v1.1.0from
epic/14-playable-example

Conversation

@diegoparrilla

Copy link
Copy Markdown
Contributor

What

Two small games written against the public APIs only (fb_*, font_*, palette_*, ikbd_*, audio_*), as the reference for starting a game:

  • Arena (rp/src/game_arena.c), the joystick game. Steer with joystick 1 or the cursor keys; fire (or space) shoots the way you last moved. Grab gems, shoot enemies, stay away from them: three lives.
  • Zap (rp/src/game_zap.c), the mouse game. Enemies and gems wander the arena. The left button zaps an enemy under the crosshair, the right one takes a gem, and the wrong button costs points. Sixty seconds a round. The HUD lights L and R while the buttons are held, so it doubles as a button test.

Around them:

  • Demo menu: they are entries 6 and 7 (demo_games.c). ESC goes back to the menu, which gets its keyboard mode and its music back: the music moves from emul.c into demo_menu_music(). The menu panel is now centred.
  • Shared kit: they share game_kit.c:
    • a tiled arena under a HUD line;
    • sprites drawn again only where they moved (the floor restored under the last frame's sprites);
    • labels;
    • a tune and sound effects computed as they play, as 8-bit PCM on both outputs.
  • Art: indexed PNGs in rp/src/assets on one palette, turned into rp/src/include/art_*.h by convert.sh.
  • examples/mini_game: builds a firmware that boots straight into Arena (apply.sh, as hello_text). Swapping three calls runs Zap instead.
  • tools/png_to_bitmap.py: turns an indexed PNG (at most 16 colours, one of them transparent) into FB_BITMAPs for fb_blit() / fb_blit_key(). It outputs a whole image or a sheet cut into tiles, with the palette as ST words for palette_set(). It's plain Python with no extra packages.
  • Debug knobs: debug builds of Arena take keys to vary the scene: enemy count and size, freeze, full redraws. Scripts send them with tools/dev/swd.py key.

Tests

  • Host tests: make -C tests/host test runs a new test_png_to_bitmap.py. It covers:
    • every PNG filter at 1-, 2-, 4- and 8-bit depth;
    • the palette as ST words and the key colour;
    • tiles in order;
    • the images the tool refuses;
    • headers that compile against fb_blit.h and give back every pixel.
  • Builds: the menu firmware and the example (via apply.sh in a clean copy) both build without warnings in debug and release.
  • Mega STE, TOS 2.06, DMA sound, debug build:
    • Arena on its own, as the example.
    • Both games from the menu, including a full Zap round.
    • ESC back to the menu, with keyboard mode and music restored.
    • No IKBD errors and no late audio slices.
  • Mega ST, YM sound, release build:
    • Both games from the menu (512 mouse packets, 30 joystick packets).
    • No IKBD errors and no late audio slices.

Converts a PNG saved with a palette of at most 16 colours into a C header
for fb_blit() / fb_blit_key(): one FB_BITMAP, or with --tile an array of
them cut from a sheet (tiles, animation frames), the palette as ST colour
words for palette_set(), and the fully transparent entry as NAME_KEY. A
pixel's palette index is its colour, so an app's images share the palette
it sets.

Plain Python: it reads indexed PNGs itself (zlib, every filter, 1 to 8
bits per pixel) and refuses what the ST cannot show (a truecolour image,
a colour past 15, more than one transparent entry, half transparency).
The host test writes PNGs with every filter and depth, checks the palette
and the key, and compiles the headers with fb_blit.h to read every pixel
back through FB_BITMAP.
Two small games on the public APIs only, the reference for starting a
game. Arena: steer with joystick 1 or the cursor keys, fire shoots the
way you last moved; grab gems, shoot enemies, three lives. Zap: enemies
and gems wander the arena, the left button zaps an enemy under the
crosshair and the right one takes a gem, sixty seconds a round; the HUD
lights L and R while the buttons are held.

They share game_kit.c: an arena of 16x16 tiles under a HUD line, sprites
drawn over it and restored under the next frame (a few tile blits per
sprite, not the whole arena), labels, and a tune and sound effects
computed as they play, as 8-bit PCM on both outputs. The art is indexed
PNGs in rp/src/assets on one palette, converted by png_to_bitmap.py.
Debug builds of Arena take keys that vary the scene (enemy count and
size, freeze, full redraws).

They are entries 6 and 7 of the demo menu (demo_games.c): ESC goes back
to the menu, which gets its keyboard mode and its music back. The menu's
music moves from emul.c into the dispatcher (demo_menu_music()) for that,
and the menu panel is centred. examples/mini_game builds a firmware that
boots straight into Arena (Zap with three calls changed); hello_text's
apply.sh also removes the new demo_games.c.
README "Starting a game" and the example's README walk through what a
game needs: an input mode, keys held down, the joystick's and the mouse's
press latches, drawing only what moved, art from indexed PNGs, and sound
computed as it plays. CLAUDE.md, the skill, the dev tools README (menu
entries 6 and 7, Arena's knobs from swd.py key) and the changelog follow.
@diegoparrilla
diegoparrilla merged commit a1798b2 into release/v1.1.0 Sep 30, 2026
2 checks passed
@diegoparrilla
diegoparrilla deleted the epic/14-playable-example branch September 30, 2026 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant