Building an A Level Platform Game Project — Part 7: Menus, Lives, Scoring, High Scores and Game Structure
In Part 1, we planned the platform game and set realistic success criteria.
In Part 2, we created the game window and added player movement.
In Part 3, we added gravity and jumping.
In Part 4, we added platforms and collision detection.
In Part 5, we turned platforms into proper levels with routes, collectables, hazards and finish points.
In Part 6, we added enemies, moving hazards and more advanced interactions to make the level feel alive.
Now we need to step back and look at the wider game.
So far, we have been building the mechanics of a platform game. The player can move, jump, collect items, avoid hazards and complete a level. That is excellent progress.
But a complete game needs more than a playable level.
It needs structure.
It needs a start screen.
It needs instructions.
It needs a score.
It needs lives.
It needs win and lose screens.
It may need high scores.
It needs a way to restart.
It needs clear feedback for the player.
This is the stage where a prototype starts to feel like a finished project.
For A Level Computer Science, this is also a valuable stage because it introduces state management, file handling, user interface design, validation, testing and evaluation.
Why Game Structure Matters
A game is not just what happens during play.
The player needs to move through different stages:
Start the game.
Read or understand the instructions.
Play the level.
Collect points.
Lose lives when making mistakes.
Complete the level or lose the game.
See the result.
Restart or exit.
Without this structure, the game may technically work, but it can feel unfinished.
A student might have a good level, but if the program immediately launches into the game with no explanation and no proper ending, the user experience is weak.
Good structure helps the player understand what is happening.
It also gives the student more opportunities to demonstrate good programming.
The Aim for Part 7
The aim of this stage is:
Add menus, lives, scoring, high scores and game states so the platform game feels like a complete playable game rather than a single test level.
By the end of this stage, the game could include:
a start menu
an instruction screen
a playing state
a game over screen
a level complete screen
a lives system
a score system
a high score system
restart and quit options
testing evidence for each game state
Students do not need to add every possible feature at once. The key is to add structure carefully and test each part.
From One Loop to Game States
Most simple games run inside one main loop.
In the early stages, that loop handled everything:
checking input
moving the player
applying gravity
checking collisions
drawing the screen
As the game grows, this can become messy.
One way to organise the program is to use game states.
A game state records what part of the game is currently active.
For example:
game_state = "menu"
The possible states might be:
"menu"
"instructions"
"playing"
"level_complete"
"game_over"
"high_scores"
Then the main loop can decide what to do depending on the current state.
For example:
if game_state == "menu":
draw_menu()
elif game_state == "instructions":
draw_instructions()
elif game_state == "playing":
update_game()
draw_game()
elif game_state == "level_complete":
draw_level_complete()
elif game_state == "game_over":
draw_game_over()
This is a very important design improvement.
Instead of one large, confusing block of code, the program is divided into sections.
That makes it easier to understand, test and extend.
Why Game States Are Useful for A Level Projects
Game states give students something strong to explain in their documentation.
They can say:
I used a game state variable to control which part of the game was active. This made the program easier to organise because the menu, instructions, gameplay and game over screen could be handled separately.
This shows design thinking.
It also helps with testing.
The student can test:
whether the menu appears first
whether pressing a key starts the game
whether the instructions screen displays correctly
whether the game over screen appears when lives reach zero
whether the level complete screen appears when the finish point is reached
whether the player can restart the game
Each state has clear expected behaviour.
Creating a Start Menu
A simple start menu does not need to be complicated.
It might show:
game title
short instruction
start option
quit option
For example:
ESCAPE THE PLATFORMS
Press SPACE to start
Press I for instructions
Press Q to quit
In Pygame-style code, the menu could be drawn using text:
def draw_text(text, x, y, size=36):
font = pygame.font.SysFont(None, size)
image = font.render(text, True, (0, 0, 0))
screen.blit(image, (x, y))
Then:
def draw_menu():
screen.fill((255, 255, 255))
draw_text("ESCAPE THE PLATFORMS", 230, 180, 48)
draw_text("Press SPACE to start", 280, 260, 32)
draw_text("Press I for instructions", 270, 310, 32)
draw_text("Press Q to quit", 320, 360, 32)
The input handling might include:
if game_state == "menu":
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
game_state = "playing"
elif event.key == pygame.K_i:
game_state = "instructions"
elif event.key == pygame.K_q:
running = False
This gives the game a proper beginning.
It also improves usability because the player is not suddenly thrown into the level without explanation.
Adding an Instruction Screen
An instruction screen is especially useful if someone else will test the game.
Students often forget that they know their own game better than a new user does.
A new user needs to know:
which keys to press
what the objective is
what collectables do
what hazards do
how to win
how to restart
Example instruction screen:
HOW TO PLAY
Move left: Left arrow
Move right: Right arrow
Jump: Space
Collect stars to increase your score.
Avoid enemies and red hazards.
Reach the flag to complete the level.
Press B to return to the menu.
This is a small feature, but it can improve user testing significantly.
It also gives the student evidence of user interface design.
Adding Lives
Lives give the player a limited number of attempts.
A simple lives system might start with:
lives = 3
When the player touches a hazard or enemy:
lives -= 1
reset_player()
If lives reach zero:
if lives <= 0:
game_state = "game_over"
This creates a clear lose condition.
The player should also be able to see how many lives remain:
draw_text("Lives: " + str(lives), 10, 40, 30)
This makes the game feel fairer because the player understands the consequence of mistakes.
Avoiding the Multiple-Life-Loss Bug
One common problem is that the player loses several lives from one collision.
This happens because the game loop runs many times per second. If the player remains touching an enemy for several frames, the collision may be detected repeatedly.
There are several possible solutions.
One simple solution is to reset the player position immediately.
Another solution is to add a short invulnerability timer:
invulnerable_timer = 0
Each frame:
if invulnerable_timer > 0:
invulnerable_timer -= 1
When the player touches danger:
if invulnerable_timer == 0:
lives -= 1
reset_player()
invulnerable_timer = 60
At 60 frames per second, this gives about one second before another life can be lost.
This is a useful advanced feature because it introduces timed state management.
It also gives students a good debugging example for their project write-up.
Adding a Score System
A score system gives the player feedback and reward.
The simplest score might increase when the player collects an item:
score += 10
It might also increase when:
collecting a coin
completing a level
defeating an enemy
finishing quickly
collecting all items
For a first version, collectables are enough.
Example:
for item in collectables[:]:
if player_rect.colliderect(item):
collectables.remove(item)
score += 10
Then the score can be displayed:
draw_text("Score: " + str(score), 10, 10, 30)
This supports several success criteria:
The score is displayed during play.
The score increases when a collectable is collected.
A collectable disappears after being collected.
The same collectable cannot be scored more than once.
These are all testable.
Making Scoring More Interesting
Once the basic score works, students can think about better scoring rules.
For example:
coin collected: +10
enemy defeated: +25
level completed: +100
all collectables collected: bonus +50
losing a life: no score penalty
optional hard-to-reach item: +30
This creates design decisions.
Should the game reward risk?
Should difficult collectables be worth more?
Should speed matter?
Should players be encouraged to explore?
A thoughtful student can justify these choices in their project documentation.
For example:
I gave optional collectables a higher score because they were placed near hazards and required more skill to collect. This encouraged players to take risks without making the level impossible to complete.
That is a good design explanation.
Adding a Level Complete Screen
When the player reaches the finish point, the game should respond clearly.
Instead of just stopping or closing, it should show a level complete screen.
Example:
LEVEL COMPLETE!
Score: 120
Lives remaining: 2
Press SPACE for next level
Press M for menu
The state change might be:
if player_rect.colliderect(finish_rect):
game_state = "level_complete"
This creates a more polished game experience.
It also prepares the project for multiple levels.
Adding a Game Over Screen
If the player loses all lives, the game should show a game over screen.
Example:
GAME OVER
Final Score: 80
Press R to restart
Press M for menu
Input handling might include:
if game_state == "game_over":
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
restart_game()
game_state = "playing"
elif event.key == pygame.K_m:
game_state = "menu"
This gives the player control.
It also makes the game feel complete rather than unfinished.
Restarting the Game Properly
Restarting is not just moving the player back to the start.
Several things may need to be reset:
player position
vertical velocity
lives
score
collected items
enemy positions
moving hazard positions
current level
game state
A restart function can help:
def restart_game():
global score, lives, player_y_velocity, game_state
score = 0
lives = 3
player_y_velocity = 0
player_rect.x, player_rect.y = current_level["player_start"]
reset_level_items()
game_state = "playing"
This is another useful programming idea.
A function can collect repeated reset behaviour in one place.
Students should be careful here. If collectables have already been removed from a list, they need to be restored when the game restarts. That may mean keeping an original copy of the level data.
Adding High Scores
High scores are a good extension because they introduce file handling.
A simple high score system might store the best score in a text file.
When the game ends, the program compares the current score with the saved high score.
Example:
def load_high_score():
try:
with open("highscore.txt", "r") as file:
return int(file.read())
except:
return 0
Saving a high score:
def save_high_score(score):
with open("highscore.txt", "w") as file:
file.write(str(score))
Then:
if score > high_score:
high_score = score
save_high_score(high_score)
This gives the project a valuable file handling feature.
However, it must be handled carefully.
The student should test:
what happens if the file exists
what happens if the file does not exist
whether the high score loads correctly
whether the high score updates when beaten
whether a lower score does not replace the high score
This is excellent A Level evidence.
Validating High Score Data
A more careful version checks whether the file contains valid data.
For example, if the file is empty or contains text instead of a number, the program should not crash.
The loading function could be improved:
def load_high_score():
try:
with open("highscore.txt", "r") as file:
data = file.read()
if data.isdigit():
return int(data)
else:
return 0
except FileNotFoundError:
return 0
This is a strong extension because it shows defensive programming.
It also gives the student something useful to discuss in testing.
A Simple Game Structure
By this stage, the game structure might look like this:
Start program
Load high score
Set game state to menu
Main loop:
Check events
If state is menu:
handle menu input
draw menu
If state is instructions:
handle instruction input
draw instructions
If state is playing:
update player
update enemies
check collisions
update score and lives
draw level
If state is level_complete:
handle next level or menu input
draw level complete screen
If state is game_over:
check high score
handle restart or menu input
draw game over screen
Quit program
This kind of structure can be shown as a flowchart in the project documentation.
That would be excellent evidence of design.
Testing the Wider Game Structure
Testing now needs to cover more than movement and collisions.
Students should test the whole user journey.
Example test table:
| Test Number | Test | Expected Result | Actual Result | Pass/Fail |
|---|---|---|---|---|
| 1 | Start program | Menu screen appears | Menu appears | Pass |
| 2 | Press I on menu | Instructions appear | Instructions appear | Pass |
| 3 | Press B on instructions | Returns to menu | Returns to menu | Pass |
| 4 | Press SPACE on menu | Game starts | Game starts | Pass |
| 5 | Collect item | Score increases by 10 | Score increases | Pass |
| 6 | Touch hazard | Lives decrease by 1 | Lives decrease | Pass |
| 7 | Lose all lives | Game over screen appears | Game over appears | Pass |
| 8 | Press R on game over | Game restarts | Game restarts | Pass |
| 9 | Reach finish point | Level complete screen appears | Level complete appears | Pass |
| 10 | Beat high score | High score updates | High score updates | Pass |
| 11 | Score below high score | High score unchanged | High score unchanged | Pass |
| 12 | Delete high score file and run game | Program creates or assumes score of 0 | No crash | Pass |
This is much more complete than simply testing the player movement.
It shows that the program works as a whole game.
User Testing: Does the Game Make Sense?
At this stage, user testing should focus on clarity.
Ask a user to play without explaining everything verbally.
Watch what they do.
Useful questions include:
Did the menu make sense?
Were the instructions clear?
Did you understand how to start?
Did you understand the score?
Did you notice how many lives you had?
Did the game over screen tell you what to do next?
Did you want to try again?
Was the high score motivating?
This is where students may discover that something obvious to them is not obvious to a new player.
For example:
A user did not realise that pressing R restarted the game, so I added the instruction “Press R to restart” to the game over screen.
That is useful evidence of user-centred improvement.
Personal Reflection: This Is Where the Project Becomes a Product
I often find this stage changes the way students view their projects.
Before this point, they are mainly thinking as programmers.
Can I make the player move?
Can I make the jump work?
Can I detect the collision?
Can I add an enemy?
Those are important questions.
But menus, scoring, lives and high scores push the student to think more like a designer.
What does the user see first?
How do they know what to do?
What happens when they lose?
Why would they play again?
How does the game communicate success and failure?
This is where the project starts to become a product.
It is no longer just a collection of mechanics. It becomes a complete experience.
That is valuable for A Level Computer Science because it links technical implementation with user needs.
Common Bugs at This Stage
This stage can introduce new bugs.
Bug 1: The Game Restarts but the Score Does Not Reset
This happens when the restart function resets the player but not the score.
The solution is to make sure all necessary variables are reset.
Bug 2: Collected Items Do Not Return After Restart
If collectables are removed from the list during play, they need to be recreated when the level restarts.
Students may need to store original level data and create a fresh copy.
Bug 3: The Game Continues Behind the Menu
Sometimes the player, enemies or hazards continue updating even when the menu or game over screen is displayed.
The solution is to update gameplay only when:
game_state == "playing"
Bug 4: High Score File Causes a Crash
If the file is missing or contains invalid data, the program may crash.
The solution is to use error handling and validation.
Bug 5: Pressing One Key Triggers Too Many Actions
If key input is checked continuously instead of through key-down events, a single key press may skip screens or restart too quickly.
Students should think carefully about event handling.
Each of these bugs can become useful evidence if recorded properly.
Linking Back to Success Criteria
This stage supports success criteria such as:
The game displays a start menu.
The game provides instructions.
The score is visible during play.
The lives remaining are visible during play.
The game displays a game over screen when lives reach zero.
The game displays a level complete screen when the finish point is reached.
The player can restart the game.
The program stores and displays a high score.
The program handles missing high score files without crashing.
User testing is used to improve the interface.
These criteria are specific, measurable and useful for evaluation.
Practical Task for Students
Part 7 Student Task
Add wider game structure to your platform game.
Your program should include:
A start menu.
An instruction screen.
A playing state.
A level complete screen.
A game over screen.
A visible score.
A visible lives display.
A restart option.
A test table covering menu, score, lives and game states.
User feedback on whether the game is easy to understand.
Extension Task
Add one or more of the following:
high score saved to a file
high score validation
multiple player names
level selection menu
pause screen
settings menu
sound on/off option
different difficulty modes
animated menu screen
improved visual design
Students should only attempt these extensions once the basic game structure works reliably.
Development Log Example
A good development log entry might look like this:
Development Stage
Adding menus, lives, scoring and game states.
Aim
To make the game feel more complete by adding a start menu, instructions, game over screen, scoring system, lives system and restart option.
What Was Added
game state variable
start menu
instruction screen
score display
lives display
game over screen
level complete screen
restart function
high score file handling as an extension
Problems Found
The game continued updating while the menu was displayed.
The score did not reset properly after restarting.
Collectables did not reappear after restarting the level.
The high score file caused an error when it was missing.
A user did not know which key restarted the game.
Changes Made
Updated gameplay only when the game state was set to “playing”.
Added score and lives reset to the restart function.
Recreated collectables when restarting the level.
Added error handling for the high score file.
Added restart instructions to the game over screen.
Evidence Collected
screenshots of the menu and instruction screen
screenshot of score and lives display
screenshot of game over screen
screenshot of level complete screen
test table
user feedback
code showing game states
code showing high score file handling
This is strong evidence because it shows planning, implementation, testing, debugging and improvement.
Final Thoughts: A Complete Game Needs More Than a Good Level
At the beginning of this series, the project was only an idea.
Then it became a moving player.
Then a jumping player.
Then a player who could land on platforms.
Then a level with collectables and hazards.
Then a more dynamic game with enemies and moving hazards.
Now it is becoming a complete game.
Menus, lives, scoring, high scores and game states may not sound as exciting as enemies or jumping, but they are essential.
They turn a playable level into a structured user experience.
They give the player instructions, feedback, consequences and reasons to try again.
For A Level Computer Science, this stage is particularly valuable because it shows that programming is not just about making features work in isolation. It is about organising a whole system.
A strong project is not judged only by the most impressive feature. It is judged by whether the program works reliably, whether the user understands it, whether the code is organised, whether testing is thorough and whether the student can explain the decisions they made.
A good platform game is not just a character jumping across platforms.
It is a complete system.
And by this stage, students are starting to build exactly that.

No comments:
Post a Comment