- Odin 81.3%
- GLSL 5.8%
- HTML 5.4%
- Nix 5.3%
- Shell 2.2%
| assets | ||
| shaders | ||
| src | ||
| web | ||
| .gitignore | ||
| build_web.sh | ||
| flake.lock | ||
| flake.nix | ||
| ols.json | ||
| README.md | ||
| src.o | ||
The Bird that Flaps, Odin port
An Odin and raylib port of ../../godot/the-bird-that-flaps.
The port includes the original gameplay, title and game-over screens, animated bird, moving pipe variant, slowdown and speedup power-ups, sound effects, score persistence, virtual 456x256 rendering, and aspect-preserving window scaling.
Controls
- Space or left mouse button: flap
- Enter: start or retry
- R: reset the game
- Escape: quit
Requirements
- Odin
- raylib 6.0
The code uses Odin's official vendor:raylib bindings.
Run
On NixOS, enter the development shell first:
nix develop
odin run ./src
On other systems with Odin and raylib installed:
odin run ./src
Web build
The Nix development shell includes Emscripten and a web build of raylib:
nix develop
./build_web.sh
This creates:
build/web/: files for local hostingbuild/the-bird-that-flaps-web.zip: the archive to upload to itch.io
Preview it locally with:
python3 -m http.server 8000 -d build/web
Then open http://localhost:8000. Browsers do not allow the build to run by opening index.html directly.
On itch.io, set Kind of project to HTML, upload build/the-bird-that-flaps-web.zip, and choose an embedded viewport of 912x512 or click-to-launch fullscreen. The canvas also adapts to other viewport sizes.
The desktop best score is stored in the platform user data directory under the-bird-that-flaps/score.save. The web build stores it in browser local storage.
Structure
src/main.odin: window/audio lifetime and the frame loopsrc/shared/:Drawable/Screenbase types with receiver-basedupdate/drawprocedure fieldssrc/app/: drawable root application; active screen, screen changes, and render pipelinesrc/title/: title screen; owns its state as a package variable,Initreturns a wiredTitle(aScreensubtype)src/gameplay/: gameplay screen; world, HUD, death flash, audio, score persistence, and popup lifecyclesrc/gameover/: drawable game-over popup with its own score snapshot, animation clock, retry button, and result eventssrc/transition/: drawable timed screen fade that reports the covered-frame screen switchsrc/button/: drawable button state;clickedis reset and recomputed every updatesrc/input/: shared per-frame input snapshotsrc/game/: simulation state, player physics, pipes/collisions, and timed power-ups; no raylib, UI, audio, or storage dependenciessrc/rendering/: render-target lifetime/resizing, compositing, world drawing, and sprite helperssrc/assets/: texture, audio, and shader loadingsrc/storage/: best-score persistencesrc/view/: virtual resolution, DPI scaling, mouse mapping, and viewport boundsshaders/effect.fs: slowdown and speedup post-processingassets/: runtime-ready art and audio extracted or copied from the Godot project
Screens and popups
Screens embed shared.Screen, which provides enter, exit, update, draw, and take_result procedures with no tagged-union dispatch. Each screen package keeps its state in a package variable. App initialization creates only the title screen. At the covered point of a transition, the app exits and releases the active screen, initializes the target screen, and enters it. Retry returns to a newly initialized title screen, matching the original flow.
The gameplay screen exclusively owns its drawable game-over popup and updates/draws it like any other child. The popup does not inspect the world or save scores. gameover.Open activates it and gameover.Take_Result reports a retry request and a one-time score-revealed event to gameplay. Gameplay persists new best scores and converts retry into a generic screen result, so neither shared screen state nor the app knows scores exist.
Gameplay updates return small, frame-local events. The screen layer handles their sound and UI effects, so simulation can run without a window or audio device.
Credits
The source project credits MegaCrash for Flappy Bird assets and the original Flappy Bird sound set. The copied audio license is in assets/audio/LICENSE.
Odin documentation: https://odin-lang.org/docs/
Odin raylib package documentation: https://pkg.odin-lang.org/vendor/raylib