Score, lives and game states
Pygame fundamentals · Lesson 5 of 7
A program becomes a game when it keeps score, can be lost and can start again.
One variable says what the game is doing
Most games are in one of a few states: waiting to start, playing, or over. Keep the current one in a variable such as state, and let each part of the loop check it: the stars move only while state == "playing", and the "Game over" panel is drawn only when it is over.
Without states, a game starts the moment the program does and ends by closing the window.
Starting again
Everything that must be fresh for a new game goes in one function, new_game(). It sets the score, the lives and the stars back to the start. global lets the function change those variables, which live outside it.
The best score stays outside new_game(), so it survives from game to game.
import random import pygame pygame.init() screen = pygame.display.set_mode((480, 320)) clock = pygame.time.Clock() font = pygame.font.Font(None, 30) big_font = pygame.font.Font(None, 64) basket = pygame.Rect(210, 290, 70, 14) def new_game(): global score, lives, stars score = 0 lives = 3 stars = [pygame.Rect(random.randint(0, 460), -20, 18, 18)] new_game() best = 0 state = "ready" # "ready", "playing" or "over" running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: if state != "playing": new_game() state = "playing" if state == "playing": keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: basket.x -= 7 if keys[pygame.K_RIGHT]: basket.x += 7 basket.clamp_ip(screen.get_rect()) for star in stars: star.y += 3 + score // 5 if star.colliderect(basket): score += 1 star.topleft = (random.randint(0, 460), -20) elif star.top > 320: lives -= 1 star.topleft = (random.randint(0, 460), -20) if score >= 5 * len(stars): # another star every 5 points stars.append(pygame.Rect(random.randint(0, 460), -60, 18, 18)) if lives == 0: state = "over" best = max(best, score) screen.fill((16, 18, 22)) for star in stars: pygame.draw.ellipse(screen, (255, 212, 59), star) pygame.draw.rect(screen, (90, 162, 224), basket, border_radius=5) screen.blit(font.render(f"Score {score} Lives {lives} Best {best}", True, (230, 232, 235)), (12, 10)) if state != "playing": message = "Catch the stars" if state == "ready" else "Game over" title = big_font.render(message, True, (255, 212, 59)) screen.blit(title, title.get_rect(center=(240, 140))) hint = font.render("Press Space to play", True, (230, 232, 235)) screen.blit(hint, hint.get_rect(center=(240, 190))) pygame.display.flip() clock.tick(60) pygame.quit()
Make it harder as it goes
Two lines make this game get harder: the stars fall faster as the score grows (3 + score // 5), and a new star joins every five points. Small rules like these keep a game interesting for longer than a fixed speed does.
Your turn
Change the programs above in the game panel, then press Run.
- Add a red "bomb" that falls like a star: catching it costs a life.
- Show "Level 2", "Level 3" and so on, going up every 10 points.
- Pause the game when P is pressed, with a "Paused" message.
Flappy Square has the same three states, a best score and a one-key restart. Open Flappy Square.