33. Events and input — Homework solutions

Problem 1 — Key reporter

Problem. Print which key is currently held to the terminal each frame; print "no key" when nothing is held.

How to think about it. Check pygame.key.get_pressed() for each key you care about. Use a flag variable to know whether any key matched. Checking every possible key is impractical, so check the ones that matter — arrow keys, spacebar, escape.

Worked solution.

import pygame

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

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

    keys = pygame.key.get_pressed()
    held = []
    if keys[pygame.K_LEFT]:
        held.append("left")
    if keys[pygame.K_RIGHT]:
        held.append("right")
    if keys[pygame.K_UP]:
        held.append("up")
    if keys[pygame.K_DOWN]:
        held.append("down")
    if keys[pygame.K_SPACE]:
        held.append("space")
    if keys[pygame.K_ESCAPE]:
        running = False

    if held:
        print(", ".join(held))
    else:
        print("no key")

    screen.fill((30, 30, 30))
    pygame.display.flip()
    clock.tick(10)   # slow tick so terminal output is readable

pygame.quit()

clock.tick(10) slows to 10 fps so the terminal does not flood with 60 prints per second.

Common mistakes.

  • Using pygame.KEYDOWN event instead of get_pressed. KEYDOWN fires only once per press; get_pressed returns the current held state every frame.

Problem 2 — Click counter

Problem. Count left-clicks and print the count each time it goes up.

How to think about it. A counter variable starts at 0. On MOUSEBUTTONDOWN with event.button == 1, increment and print.

Worked solution.

import pygame

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

clicks = 0

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                clicks += 1
                print(f"clicks: {clicks}")

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

pygame.quit()

Common mistakes.

  • Checking event.button == 1 inside MOUSEBUTTONDOWN is important because button 3 (right-click) would also trigger MOUSEBUTTONDOWN.

Problem 3 — Arrow mover

Problem. A circle that moves with arrow keys; Escape quits; print "moved" each frame the circle is moving.

How to think about it. Use get_pressed for movement. Track whether any movement key is held to decide whether to print.

Worked solution.

import pygame

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

cx, cy = 400, 300
speed = 4

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

    keys = pygame.key.get_pressed()
    moving = False
    if keys[pygame.K_LEFT]:
        cx -= speed
        moving = True
    if keys[pygame.K_RIGHT]:
        cx += speed
        moving = True
    if keys[pygame.K_UP]:
        cy -= speed
        moving = True
    if keys[pygame.K_DOWN]:
        cy += speed
        moving = True

    if moving:
        print("moved")

    screen.fill((30, 30, 30))
    pygame.draw.circle(screen, (255, 100, 100), (cx, cy), 30)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Common mistakes.

  • Putting the Escape check inside get_pressed. Escape as an exit key is a one-shot action — put it in KEYDOWN, not get_pressed. Both work technically, but KEYDOWN is the correct pattern for exit.

Challenge — Mouse follower

Problem. Draw a circle that follows the mouse. Change its colour while the left button is held.

How to think about it. Track a boolean button_held. Set it to True on MOUSEBUTTONDOWN and back to False on MOUSEBUTTONUP. Use it to choose the draw colour.

Worked solution.

import pygame

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

button_held = False

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                button_held = True
        if event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                button_held = False

    mx, my = pygame.mouse.get_pos()
    color = (255, 255, 0) if button_held else (200, 200, 200)

    screen.fill((30, 30, 30))
    pygame.draw.circle(screen, color, (mx, my), 15)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

pygame.mouse.get_pos() returns the current cursor position, updated every frame. The circle stays centred on the cursor.

Common mistakes.

  • Using pygame.mouse.get_pressed()[0] directly instead of tracking MOUSEBUTTONDOWN/MOUSEBUTTONUP. Both approaches work. The event approach is shown here because it follows the pattern used in the chapter.

Done?

Chapter 34 uses positions and velocities to make objects move inside the game loop.