CS Logo

Python Coding Camp

Day 4 — Pygame (Day 1)

Game loop · Coordinates · Drawing · Keyboard events · Movement

⬇ Today's slides (PDF)

Schedule

TimeActivity
0:00 – 0:45Lecture: game loop, coordinate system, events, drawing
0:45 – 1:30Guided: add a player sprite that moves with arrow keys
1:30 – 1:40Break
1:40 – 3:00Open project: start building your game

Install

pip install pygame

The Starter Template

Copy this into a new file called game.py. It opens a window, handles the quit button, and runs at 60 frames per second. You build the rest.

Notice the main loop stays tiny — it only calls three functions: handle_events(), update(), and draw(). Each one has a single job, so when something breaks you know exactly where to look: nothing moving? check update(). Nothing showing up? check draw().

import pygame
import sys
import random

pygame.init()

# --- Window setup ---
WIDTH = 800
HEIGHT = 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("My Game")
clock = pygame.time.Clock()

# --- Colors (Red, Green, Blue) ---
WHITE  = (255, 255, 255)
BLACK  = (0,   0,   0)
RED    = (220, 50,  50)
GREEN  = (50,  200, 50)
BLUE   = (50,  100, 220)
YELLOW = (240, 200, 0)

# --- Font ---
font = pygame.font.SysFont(None, 36)

# --- Your variables here ---
player_x = WIDTH // 2
player_y = HEIGHT - 80
player_speed = 5

score = 0


# ============================================================
# FUNCTIONS — the main loop below only calls these three.
# Splitting the work up keeps each job in one place, so a bug
# is easy to track down.
# ============================================================

def handle_events():
    """Deal with one-time events like the quit button.

    Returns False when the player closes the window, and True
    otherwise. The main loop uses this to know when to stop.
    """
    for event in pygame.event.get():
        if event.type == pygame.QUIT:   # the window's X was clicked
            return False
    return True


def update():
    """Move things and change the score — the game's 'thinking'.

    Runs once every frame. Reads which keys are held down and
    moves the player. Add your collision checks and scoring here.
    """
    # List every outside variable you CHANGE here.
    # (Add 'score' to this line once you start scoring points.)
    global player_x

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_x > 0:
        player_x -= player_speed
    if keys[pygame.K_RIGHT] and player_x < WIDTH - 50:
        player_x += player_speed


def draw():
    """Draw everything for this frame, then show it on screen.

    Always fill the background first (this erases the last frame),
    then draw your objects, then flip the display to show it.
    """
    screen.fill(BLACK)                       # erase the previous frame

    # Draw player (a rectangle for now — swap in a sprite later)
    pygame.draw.rect(screen, GREEN, (player_x, player_y, 50, 50))

    # Draw score
    score_text = font.render(f"Score: {score}", True, WHITE)
    screen.blit(score_text, (10, 10))

    pygame.display.flip()                    # show the frame we just drew


# ============================================================
# MAIN GAME LOOP — small on purpose: it just calls functions.
# Runs about 60 times per second.
# ============================================================
running = True
while running:
    running = handle_events()   # 1. input & quit button
    update()                    # 2. move things, update the score
    draw()                      # 3. put everything on the screen
    clock.tick(60)              # wait so the game runs at 60 FPS

pygame.quit()
sys.exit()
Tip: Run this first to make sure the window opens. You should see a green square you can move left and right. Then start adding your game's pieces.

Using Sprites (Images)

The sprites folder on your computer has free images from Kenney.nl. Ask your instructor where it's located.

# Load an image — the file must be in the same folder as game.py (or give the full path)
player_img = pygame.image.load("sprites/player.png")

# scale() resizes the image to exactly (width, height) in pixels
player_img = pygame.transform.scale(player_img, (50, 50))

# blit() "stamps" the image onto the screen at position (x, y)
# Do this inside draw(), after screen.fill()
screen.blit(player_img, (player_x, player_y))

Default Project — Catch the Falling Objects

If you have your own game idea, go for it! If you want a starting point, build this:

The one rule for the functions: if a function changes a variable that lives outside it (like score or obj_y), add that variable to a global line at the top of the function. Just reading a variable needs no global. Forgetting this is the #1 cause of "my score won't change." You're filling in the same update() and draw() from the template — not writing new ones.

Adding a Falling Object

# --- With your other variables, up near the top (before the functions) ---
obj_x = random.randint(0, WIDTH - 30)  # random x so it doesn't always fall in the same spot
obj_y = 0                               # start at the top (y=0 is the top of the window in pygame)
obj_speed = 4                           # how many pixels it moves down per frame

# --- Inside update(): the object moves, and we check for a miss ---
def update():
    global player_x, obj_y        # every outside variable we CHANGE goes on this line

    # ...your player movement from the template stays here...

    obj_y += obj_speed            # move the object down each frame
    if obj_y > HEIGHT:            # fell past the bottom of the window?
        print("You missed it!")   # replace with a lives / game-over system later

# --- Inside draw(): stamp the object on the screen (after screen.fill) ---
def draw():
    # ...fill the screen and draw the player first...
    pygame.draw.rect(screen, RED, (obj_x, obj_y, 30, 30))  # a 30x30 red square

Collision Detection

# This all goes inside update(). pygame.Rect makes a rectangle from
# (left x, top y, width, height) so we can test whether two overlap.
def update():
    global score, obj_x, obj_y     # we change all three below, so list them here

    player_rect = pygame.Rect(player_x, player_y, 50, 50)
    obj_rect    = pygame.Rect(obj_x, obj_y, 30, 30)

    # colliderect() is True when the two rectangles overlap — a catch!
    if player_rect.colliderect(obj_rect):
        score += 1                             # award a point
        obj_x = random.randint(0, WIDTH - 30)  # send the object back up...
        obj_y = 0                              # ...at a new random x

Choose Your Game — Pick Any

"Catch the Falling Objects" is just the default. Build any of these instead — they all reuse the starter template's game loop, drawing, and movement. Variety makes the Day 5 gallery walk way more fun. The Boss games need collision, a grid, or an enemy.

Starter — one moving thing + one goal

Starter

Dodger

The opposite of Catch: obstacles fall and you avoid them. Survive as long as you can — score goes up over time.

Starter

Whack-a-Mole / Aim Trainer

Targets pop up at random spots; click them for points before they vanish. Uses the mouse instead of the keyboard (pygame.MOUSEBUTTONDOWN).

Standard — collision or bouncing

Standard

Pong

A ball bounces between two paddles. Teaches ball velocity and wall/paddle collision. Play vs. a friend, or vs. a simple computer paddle.

Standard

Flappy-Style Tap Game

Gravity pulls the player down; tap a key to flap upward through gaps in pipes. One button, surprisingly addictive.

Boss Challenge — grids and AI

Boss

Snake

Move on a grid, grow a tail when you eat food, and lose if you hit yourself. Store the tail as a list of positions. Huge "wow" for the line count.

Boss

Maze / Top-Down Collector

Walls you can't walk through and coins to grab. Draw the level from a grid, and block movement when the player would hit a wall.

Stretch Goals

  1. Add multiple falling objects at the same time — store them in a list
  2. Make objects fall faster as your score increases
  3. Add a second type of object to avoid — hitting it loses a life
  4. Add sound effects: pygame.mixer.Sound("sound.wav").play()
  5. Add a lives counter — show it on screen, end game when lives reach 0
  6. Boss challenge: Add a high score that persists across rounds