31. From Python to pygame

So far every program you have written runs in the terminal, prints some output, and exits. From here on, programs open a window and keep running until the user closes them. The tool for this is pygame, a Python library for making 2D games and interactive graphical programs.

What pygame is

pygame is a free, open-source library that wraps the SDL graphics and input system. It gives Python programs the ability to:

  • Open a resizable window.
  • Draw shapes, images, and text.
  • Read keyboard and mouse input.
  • Play sounds.
  • Control frame rate.

You use the same Python you already know — if, for, functions, classes — and pygame supplies the window and the tools to fill it.

Installing pygame

Open a terminal (Command Prompt or PowerShell on Windows) and run:

pip install pygame

If that fails with a "permission denied" error, try:

pip install pygame --user

Once installed, test it:

import pygame
print(pygame.version.ver)

If a version number prints (e.g., 2.5.2), the install worked.

The minimal pygame program

Here is the smallest pygame program that opens a window, keeps it open, and closes cleanly when you click the X button.

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("My Game")
clock = pygame.time.Clock()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill((0, 0, 0))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Run it with:

python exercises/31/01-minimal-window.py

A black 800x600 window should appear. Close it with the X button.

What each line does

pygame.init()

Starts all pygame subsystems — display, input, sound. Call this once before anything else.

screen = pygame.display.set_mode((800, 600))

Opens a window 800 pixels wide and 600 pixels tall. The return value is a Surface object — think of it as a blank canvas you can draw on. The argument is a tuple (width, height), so the double parentheses ((800, 600)) are: the outer ones are the function call, the inner ones create the tuple.

pygame.display.set_caption("My Game")

Sets the title bar text. Change this to whatever your game is called.

clock = pygame.time.Clock()

Creates a clock object used to control frame rate. You will call clock.tick(60) at the end of every frame.

running = True and while running:

The game loop. This loop runs once per frame, thousands of times per second if unconstrained. Setting running = False exits the loop and ends the program.

for event in pygame.event.get():

Every input event that happened since the last frame — key presses, mouse clicks, window events — is collected here. You loop through them and react to whichever ones matter.

if event.type == pygame.QUIT:

pygame.QUIT fires when the user clicks the X button. Setting running = False tells the loop to stop after this frame.

screen.fill((0, 0, 0))

Paints the entire canvas black. The argument is an RGB colour tuple: (red, green, blue), each 0-255. This clears whatever was drawn last frame, so the new frame starts with a clean slate. Change the tuple to change the background colour.

pygame.display.flip()

pygame draws to an off-screen buffer first. flip() copies that buffer to the actual window so the player sees it. Without flip(), the window stays blank. Call this once at the end of every frame, after all drawing.

clock.tick(60)

Limits the loop to 60 frames per second. Without this, the loop runs at full CPU speed — thousands of frames per second on a modern machine — which wastes power and makes movement speed unpredictable. 60 is the target; on a slow machine, the loop will run slower, but it will never run faster than 60 fps.

pygame.quit()

Shuts down pygame after the loop exits. On some platforms, skipping this leaves the window open or causes errors.

The game loop pattern

Every pygame program follows the same four-step cycle each frame:

1. Process events   (read keyboard, mouse, window events)
2. Update state     (move things, check collisions, update score)
3. Draw             (fill background, draw all objects)
4. Flip             (show the frame; throttle frame rate)

Repeat until running = False.

This is the fundamental difference between a game and a terminal script:

  • A terminal script runs top to bottom once, then exits.
  • A game loop runs the same four steps over and over — 60 times per second — updating the world a tiny bit each time.

All the interesting game logic goes in steps 2 and 3.

Changing the window size

To get a different window size, change the tuple passed to set_mode:

screen = pygame.display.set_mode((1024, 768))   # larger
screen = pygame.display.set_mode((400, 300))    # smaller

The width and height are in pixels. Common sizes: 640x480 (classic), 800x600, 1280x720 (HD).

Homework

Problem 1 — Different window size

Open exercises/31/homework/01-window-size.py. Change the window to 1024x768. Add a caption that reads "Big Window". Run it and confirm the window is noticeably larger.

Problem 2 — Background colour

Open exercises/31/homework/02-background-colour.py. Change screen.fill(...) to a colour that is not black. Try at least two different colours by running the program twice with different values.

Problem 3 — Two fills

Open exercises/31/homework/03-two-fills.py. Inside the game loop, call screen.fill twice before pygame.display.flip() — first with red (255, 0, 0), then with blue (0, 0, 255). Run it and observe what colour the background is. Explain why in a comment.

Challenge — Shrinking window

Open exercises/31/homework/04-shrinking-window.py. Start with a 500x500 window. Each time the user presses the spacebar, reduce the window size by 50 pixels in both dimensions, down to a minimum of 100x100. Use pygame.KEYDOWN and event.key == pygame.K_SPACE to detect the key press, and pygame.display.set_mode(...) to resize.

Stuck or finished? Open the homework solutions page.