Rotate the playfield to portrait like a real pinball machine: - canvas 480x360 landscape -> 360x480 portrait; playfield 308px wide, ~390px tall, with HUD, title/game-over screens and HUD re-laid out for the narrow format (score/best top row, balls/stars/fever below) - flippers reduced to a regular ~quarter-width size (82px) pivoting at the lower corners, tips meeting over a wide centre drain - table elements re-placed for the taller field: 3 star lanes up top, candy bumpers mid-field, slingshots and floor-level outlane guard posts at the bottom; data-checked with the autoplay bot (~350 bumper hits / 30k frames, centre+side drains, no stuck balls) - probes & autoplay policy updated for the portrait geometry; all 13 pixel-probes pass
91 lines
3.3 KiB
Markdown
91 lines
3.3 KiB
Markdown
# 🍬 Cosmic Candy Pinball
|
||
|
||
A cute, 8-bit, pixel-art **pinball** game, written from scratch in Python with
|
||
[pygame-ce](https://pygame.org). 100% procedural — there are **no image or
|
||
audio asset files**: every sprite, texture, particle, chiptune and sound
|
||
effect is generated by code at runtime.
|
||
|
||
Not web-based: it opens its own native window and renders on a low-res
|
||
**portrait** `360×480` virtual canvas — like a proper pinball machine —
|
||
upscaled with crisp nearest-neighbour scaling for an authentic retro look.
|
||
|
||

|
||
|
||
## Running it
|
||
|
||
Fastest (uv, already used in this project):
|
||
|
||
```bash
|
||
uv venv .venv --python 3.12 # once
|
||
uv pip install --python .venv/bin/python -r requirements.txt # once
|
||
.venv/bin/python main.py
|
||
```
|
||
|
||
Or with plain venv + pip (Python 3.10+):
|
||
|
||
```bash
|
||
python3 -m venv .venv
|
||
.venv/bin/pip install -r requirements.txt
|
||
.venv/bin/python main.py
|
||
```
|
||
|
||
> Note: the dependency is **`pygame-ce`**, not `pygame` (the community edition
|
||
> supports newer Python versions and is what this was built against).
|
||
|
||
## Controls
|
||
|
||
| Key | Action |
|
||
| --- | --- |
|
||
| `←` `→` (or `Z` / `/`) | Flippers |
|
||
| hold `Space` / `↑` | Charge the **Candy Cannon** — release to fire |
|
||
| `M` | Mute |
|
||
| `P` | Pause |
|
||
| `R` | Restart game |
|
||
| `Esc` | Quit |
|
||
| `Enter` | Start / play again |
|
||
|
||
## How to play
|
||
|
||
- Light the **3 star lanes** up top → **CANDY FEVER!** (double points for
|
||
20s — keep hitting stars to extend it).
|
||
- Bounce off the three kawaii candy bumpers and the two slingshots. Every hit
|
||
inside 2.5s builds your **combo multiplier** (up to ×9).
|
||
- Watch the bottom-centre drain and the two outlanes… or lose the ball.
|
||
- Every **50,000 points** earns an extra ball.
|
||
- Squirrel says encouraging things. Blush at the cat in the corner.
|
||
|
||
## Project layout
|
||
|
||
| file | purpose |
|
||
| --- | --- |
|
||
| `main.py` | entry point / main loop |
|
||
| `game.py` | game states, scoring, juice, all rendering (pixel art) |
|
||
| `physics.py` | pure pinball physics (flippers, bumpers, slingshots, drains) — no rendering |
|
||
| `font.py` | hand-made 5×5 pixel bitmap font |
|
||
| `sfx.py` | procedural chiptune + music synthesis (pure code, no samples) |
|
||
| `highscore.json` | best score (written at game over) |
|
||
|
||
Dev / test tools (run headless, no window needed):
|
||
|
||
```bash
|
||
.venv/bin/python autotest.py 40000 # auto-playing bot, prints game-health stats
|
||
.venv/bin/python runtest.py 900 # simulated session incl. rendering
|
||
.venv/bin/python shots.py shots # dumps screenshots of every state
|
||
.venv/bin/python probe.py # pixel-probes the rendered layout
|
||
```
|
||
|
||
## Design notes
|
||
|
||
- **Physics**: fixed 60 Hz logic with up to 6 collision substeps so the ball
|
||
never tunnels; kinematic flippers whose angular velocity is transferred to
|
||
the ball (real "scoop" feel); inward-tilting floor funnels; centre drain +
|
||
guarded outlanes; a softlock rescue nudge if a ball ever parks too long.
|
||
- **Fun tuning was data-driven**: an `autotest.py` bot plays thousands of
|
||
frames and reports hit/drain rates per table element, so bumper, slingshot
|
||
and flipper geometry was tuned against measured behaviour, not guesswork.
|
||
- **Audio**: square/triangle/noise oscillators -> PCM at startup; distinct
|
||
pitched bump sounds that climb with your combo, plus two generative music
|
||
loops (menu + play).
|
||
|
||
Licence: MIT — go wild.
|