Clumsy Bird

Flappy-style endless flyer with audio, score and restart.

📄 Docs only

Upstream: 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()