Learn
08.6 | PROJECT: A COMPLETE ARCADE GAME
01 | THE BRIEF: STAR RUNNER
Catch gold stars with a green player paddle and avoid pink rocks. A star gives ten points. The player starts with three lives and the round ends when lives reach zero or sixty seconds of simulation time pass.
Use Left/Right or A/D to move, Enter to start or restart, P to pause or resume, and Escape to quit. Window close must work in every state. The game has a title screen and final score rather than simply freezing after a loss.
The download contains all source and drawn assets. It needs Pygame, but no external artwork or audio files. It is a complete small learning game, not a commercial release or a networked multiplayer system.
Download Star Runner02 | SET UP AND TRY THE FINISHED LOOP
Extract the project, create a fresh .venv and install its requirements with the selected interpreter. Run main.py to play. The pure model tests do not import Pygame, so they can run independently of graphics installation.
Learn the rules by playing before changing them. Catch a star, hit a rock, pause, resume, end a round and restart. Watch how the HUD displays score, lives and remaining simulation time.
The requirements file records the course’s Pygame range. For a tested machine snapshot, use the dependency workflow from Topic 7 rather than assuming one range describes every environment perfectly.
TERMINAL | PROJECT FOLDER
python -m pip install -r requirements.txt python main.py python -m unittest discover -s . -v
03 | MAP THE GAME STATES
A state is a meaningful operating mode. title waits for Enter, playing advances the world, paused draws the current world without advancing it, and over shows the final result and permits restart.
Transitions are controlled actions: Enter starts from title or over, P toggles only playing and paused, and a rule can move playing to over. Escape or QUIT exits independently of those transitions.
Do not create a new nested game loop for every screen. One loop continues processing events and drawing; its current state determines which rules are active.
| Current state | Action / condition | Result |
|---|---|---|
| title | Enter | Reset round and play |
| playing | P | Pause |
| paused | P | Resume same round |
| playing | Zero lives or time limit | Show final score |
| over | Enter | Fresh round |
| Any state | Escape or window close | Exit cleanly |
04 | GIVE THE FILES ONE JOB EACH
game_model.py owns dimensions, Falling records, collision checks and the Game rules. main.py owns Pygame input, the display loop and drawing. test_game.py supplies thirteen model tests.
The model contains float positions and simple records. It deliberately does not inherit pygame.sprite.Sprite: this keeps tests independent of the graphics library. The previous lesson showed how Sprite Groups work, while this project demonstrates explicit model/render separation.
Drawing should be able to run twice without changing lives or score. Updating should be testable without opening a display. These boundaries make bugs easier to locate.
| Component | Responsibility |
|---|---|
| Game.step | Advance one bounded simulation step |
| Game.start | Reset every round-owned field |
| Falling | Store one star or rock’s position, kind and speed |
| overlaps | Test strict rectangle intersection |
| draw | Render existing state without applying rules |
| main | Events, held keys, clock, update and frame presentation |
05 | MOVE, SPAWN AND REMOVE
The player moves at 340 pixels per second, bounded inside the 640-pixel screen. Falling objects appear at random legal x positions. Their speed and spawn interval gradually change with elapsed simulation time.
A spawn accumulator adds dt each step. When it reaches the chosen interval, subtract that interval and spawn. Retaining the leftover time avoids resetting the accumulator and losing its remainder.
Random(seed) can make a new model’s generated sequence reproducible in tests. Restart resets round state but continues that object’s random sequence; it does not promise exactly the same next wave every restart. Off-screen objects are removed to keep the active collection bounded.
06 | MAKE SCORING AND DAMAGE FAIR
For each falling object, update its position and test its box against the player. A star awards ten points and is consumed. A rock costs one life only when invulnerability is zero, then starts 0.8 seconds of protection.
Every overlapping item is removed, including rocks encountered during protection. Thus one star cannot score repeatedly and several rocks in the same step do not remove every life. These are explicit game-design choices, not automatic consequences of a collision library.
overlaps uses strict comparisons so touching edges alone is not a hit. The model tests boundary contact separately from real intersection. Fast motion can still tunnel between sampled positions; the capped step reduces but does not mathematically eliminate that limitation.
from game_model import overlaps
player = (100, 400, 48, 24)
print("Touching:", overlaps(player, (148, 400, 24, 24)))
print("One-pixel overlap:", overlaps(player, (147, 400, 24, 24)))EXPECTED OUTPUT
Touching: False One-pixel overlap: True
07 | PAUSE AND RESTART WITHOUT LEFTOVER STATE
Pause prevents model advancement: positions, spawn accumulator, remaining protection and simulation elapsed time stay still. The loop continues processing input and redrawing, so P and Escape still work.
start resets player position, active items, score, lives, elapsed time, spawn time and protection. Resetting only the score would leave hazards or damage from the earlier round.
The game caps dt at 0.05 seconds and counts a sixty-second simulation round. On a severely slow machine, those sixty simulated seconds can take longer than sixty wall-clock seconds. State that tradeoff instead of presenting the displayed timer as an exact real-time deadline.
08 | EXPLORE THE STATE MACHINE
Choose an action and watch which transitions are allowed. “Collect” and “Hit” are simplified fixed encounters for the illustration. The actual download uses moving objects and a protection timer.
09 | TEST THE RULES AND THE PLAY EXPERIENCE
The thirteen model tests cover movement, bounds, one-time collection, protection, pause, zero-life and time-limit endings, spawning, off-screen removal, seeded repeatability, collision edges and invalid-update rejection.
A direct model check can inject a stationary star overlapping the player and advance zero seconds. This isolates the collision outcome without relying on keyboard skill or a lucky random wave. The sample belongs beside game_model.py.
Manual play tests also matter: input focus, both directions held, visible instructions, readable HUD, closing from every screen and a restart with clean state. Automated arithmetic checks cannot decide whether the challenge feels enjoyable.
from game_model import Game, Falling
game = Game(seed=7)
game.start()
game.items.append(Falling(game.player_x, game.player_y, "star", 0))
game.step(0, 0)
print("Score:", game.score)
print("Items left:", len(game.items))
game.toggle_pause()
before = game.elapsed
game.step(0.02, 1)
print("Pause froze time:", game.elapsed == before)EXPECTED OUTPUT
Score: 10 Items left: 0 Pause froze time: True
10 | FINISH, DOCUMENT AND CHOOSE ONE EXTENSION
Include install commands, controls, rule summary and test commands in the handover. The score is session-only; the project does not store a high score between launches. Use no external assets without checking their use conditions.
Useful extensions include a saved high score, sound feedback, a settings screen or a difficulty choice. A saved score needs careful file validation; difficulty needs clear values and tests; audio should not be essential to understanding the game.
Change one feature at a time and keep a tested Git commit before experimenting. Ask another student to play without verbal coaching, then improve one confusing instruction or unfair rule based on their evidence.
OFFICIAL REFERENCE
Use the worked examples first, then consult the official documentation to check widget, input or drawing options. Match it to the version selected in your project environment.
PUT YOUR SKILLS TO WORK
Use the Section 8 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.
Section 8 challenges Python course navigation