Clumsy Bird¶
Flappy-style endless flyer with audio, score and restart.
▶ Run in browserUpstream: https://github.com/ellisonleao/clumsy-bird
Licence: this port's own code is offered under GPL-3.0-or-later, not the SimVX Examples Licence the rest of the gallery carries. See ATTRIBUTION.md for the upstream it re-implements, the terms of anything it bundles, and the credit each one requires.
Ports live in the repository only, not in the simvx-examples distribution, because each is a derivative work licensed individually against the game it re-implements. Read it with git clone https://git.simvx.com/simvx/simvx.
Tags: port tier-0
Clumsy Bird (SimVX port)¶
SimVX port of ellisonleao/clumsy-bird, a MelonJS Flappy Bird clone: flap through pipe gaps, score per pipe passed, crash restarts.
Licensing: GPL-3.0-or-later (matching upstream code); all art is CC0 (Kenney
Tappy Plane) or procedural, all audio synthesised. See ATTRIBUTION.md.
Run¶
uv run python examples/ports/clumsy_bird/main.py # interactive
uv run python examples/ports/clumsy_bird/main.py --test # headless capture
uv run simvx export web examples/ports/clumsy_bird/main.py -o /tmp/clumsy_bird.html
Controls¶
Space / Left click: flap (also starts a run, and restarts after a crash)
Esc: quit
What it shows¶
One action, two devices: the root scene declares
input_actions = {"flap": [Key.SPACE, MouseButton.LEFT], "quit": [Key.ESCAPE]}, so keyboard, mouse and touch share one code path. The scene tree registers the table at mount and again after every scene swap.Event-driven input: the flap is handled by
@on_input(action="flap")rather than polled fromon_fixed_update, so no tap is lost on a display that outruns the fixed tick rate.Coroutines: pipe spawning is a generator driven by
start_coroutineandwait, not a hand-rolled timer field.Signals: the bird emits
flappedandcrashed, each pipe pair emitspassed; the play scene connects them and keeps the score.AnimatedSprite2D: a three-frame flipbook drives the wing beat.
Procedural audio: the wing, score, crash and looping theme are synthesised at load with
AudioSynth, so the port ships no sound files.Anchored UI: the score readout, title card, bottom controls strip and game-over panel are
Controls positioned with anchors and margins.Scene flow: the title screen and every restart are
tree.change_sceneswaps.
Source files¶
File |
Summary |
Lines |
|---|---|---|
Clumsy Bird: Flappy-style endless flyer with audio, score and restart. |
61 |
|
Shared constants and asset paths for Clumsy Bird. |
45 |
|
Clumsy Bird scene nodes. |
1 |
|
Procedural audio: built on the engine’s AudioSynth API. |
129 |
|
Player bird: gravity, flap, rotation. |
147 |
|
Scrolling ground: two side-by-side ground sprites that loop, plus a |
64 |
|
Score HUD: anchored Label that displays the current score. |
23 |
|
MenuScene: the title screen shown before a run starts. |
110 |
|
Pipe pair: top + bottom pipe sprites that scroll right to left and emit |
88 |
|
PlayScene: the active game: bird, pipes, ground, HUD, score, restart. |
254 |
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 = false }
8# ///
9
10Run desktop: uv run python examples/ports/clumsy_bird/main.py
11Headless: uv run python examples/ports/clumsy_bird/main.py --test
12Web export: uv run simvx export web examples/ports/clumsy_bird/main.py -o /tmp/clumsy_bird.html
13
14Controls:
15 Space / Click Flap (also starts a run and restarts after a crash)
16 Esc Quit
17"""
18
19# /// script
20# requires-python = ">=3.14"
21# dependencies = ["simvx-core", "simvx-graphics", "numpy", "pillow"]
22# ///
23
24from __future__ import annotations
25
26import sys
27from pathlib import Path
28
29_PORT_DIR = Path(__file__).resolve().parent
30sys.path.insert(0, str(_PORT_DIR))
31
32from config import HEIGHT, WIDTH # noqa: E402
33from nodes.menu import MenuScene # noqa: E402
34from nodes.play_scene import PlayScene # noqa: E402
35
36from simvx.graphics import App # noqa: E402
37
38
39def main():
40 test_mode = "--test" in sys.argv
41 app = App(width=WIDTH, height=HEIGHT, title="Clumsy Bird", visible=not test_mode)
42
43 if test_mode:
44 from simvx.graphics import save_png
45
46 out = _PORT_DIR / "screenshots"
47 out.mkdir(exist_ok=True)
48 # A headless run gets no input, so boot straight into the play scene:
49 # the captures then show the game rather than the title card.
50 frames = app.run_headless(PlayScene(), frames=180, capture_frames=[60, 120, 179])
51 for idx, n in enumerate([60, 120, 179]):
52 save_png(frames[idx], out / f"frame_{n}.png")
53 print(f"Wrote {len(frames)} screenshots to {out}")
54 app.quit()
55 return
56
57 app.run(MenuScene())
58
59
60if __name__ == "__main__":
61 main()