Clumsy Bird¶
Flappy-style endless flyer with audio, score and restart.
📄 Docs onlyUpstream: https://github.com/ellisonleao/clumsy-bird
Tags: port tier-0
Run desktop: uv run python ported_games/clumsy_bird/simvx_port/main.py Headless: uv run python ported_games/clumsy_bird/simvx_port/main.py –test Web export: uv run simvx export web ported_games/clumsy_bird/simvx_port/main.py -o ported_games/clumsy_bird/simvx_port/web/index.html
Controls: Space / Click Flap Esc Quit
Source¶
1#!/usr/bin/env python3
2"""Clumsy Bird: Flappy-style endless flyer with audio, score and restart.
3
4# /// simvx
5# tags = ["port", "tier-0"]
6# upstream = "https://github.com/ellisonleao/clumsy-bird"
7# web = { width = 900, height = 600, responsive = true, disabled = true, reason = "Blocked on audio refactor (uses old 'SFX' bus name)." }
8# ///
9
10Run desktop: uv run python ported_games/clumsy_bird/simvx_port/main.py
11Headless: uv run python ported_games/clumsy_bird/simvx_port/main.py --test
12Web export: uv run simvx export web ported_games/clumsy_bird/simvx_port/main.py \
13 -o ported_games/clumsy_bird/simvx_port/web/index.html
14
15Controls:
16 Space / Click Flap
17 Esc Quit
18"""
19
20# /// script
21# requires-python = ">=3.13"
22# dependencies = ["simvx-core", "simvx-graphics", "numpy", "pillow"]
23# ///
24
25from __future__ import annotations
26
27import sys
28from pathlib import Path
29
30_PORT_DIR = Path(__file__).resolve().parent
31sys.path.insert(0, str(_PORT_DIR))
32
33from simvx.core import InputMap, Key, MouseButton # noqa: E402
34from simvx.graphics import App # noqa: E402
35
36from config import HEIGHT, WIDTH # noqa: E402
37from nodes.play_scene import PlayScene # noqa: E402
38
39
40class Game(PlayScene):
41 """Root scene: wraps PlayScene to register InputMap actions in on_ready."""
42
43 def on_ready(self):
44 # InputMap actions MUST live in the root node's on_ready, not main(),
45 # so the web exporter sees them (it skips main()).
46 InputMap.add_action("flap", [Key.SPACE, MouseButton.LEFT])
47 super().on_ready()
48
49
50def main():
51 test_mode = "--test" in sys.argv
52 app = App(width=WIDTH, height=HEIGHT, title="Clumsy Bird", visible=not test_mode)
53
54 if test_mode:
55 from simvx.graphics import save_png
56 out = _PORT_DIR / "screenshots"
57 out.mkdir(exist_ok=True)
58 frames = app.run_headless(Game(), frames=180, capture_frames=[60, 120, 179])
59 for idx, n in enumerate([60, 120, 179]):
60 save_png(out / f"frame_{n}.png", frames[idx])
61 print(f"Wrote {len(frames)} screenshots to {out}")
62 app.quit()
63 return
64
65 app.run(Game())
66
67
68if __name__ == "__main__":
69 main()