Game Loop
The game loop is the heartbeat of a real-time game. While the application is running, it repeatedly handles events, updates game state, renders the next frame, and controls the speed of the cycle.
Learning Objective
- 12.5.1.3 Code the game loop of the application
How Games Work
Clear Code — how games create movement from repeated frames. Starts at 01:06.
Conceptual Anchor
The Film Projector Analogy
A film creates the illusion of motion by showing many still frames in rapid succession. A game does the same thing, except each frame is produced by code: read input → update state → draw the current state → show the frame → repeat.
One Frame of the Game Loop
1
True
Events
| Phase | Purpose | Typical PyGame code |
|---|---|---|
| 1. Handle Events | Read queued events such as QUIT or key presses | pygame.event.get() |
| 2. Update State | Change positions, scores, timers and other game data | Game logic |
| 3. Render | Clear the old frame, draw the new state and show it | fill() + draw + flip() |
| 4. Control FPS | Limit how quickly frames repeat | clock.tick(FPS) |
Anatomy of the Game Loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# update game state
screen.fill(BLACK)
# draw current game state
pygame.display.flip()
clock.tick(FPS)
pygame.quit()while running:
Repeat frames
The program keeps producing frames while the control variable remains True.
pygame.event.get()
Handle events
Retrieve queued PyGame events so the application can react.
screen.fill(...)
pygame.display.flip()
Render
Prepare the current frame and then make the complete display Surface visible.
clock.tick(FPS)
Control speed
Limit the loop so it does not run unrestricted as fast as the CPU allows.
Why while running?
How the loop ends
When a pygame.QUIT event is found, the program sets
running = False. The current iteration finishes,
the while condition fails on the next check, and
execution continues after the loop to pygame.quit().
Event Queue
Mouse movement, keyboard actions and window controls generate events.
Events wait in a queue until your program retrieves and handles them.
Gets the queued events so a for event in ... loop can process them.
Why process events regularly?
The event queue has a limit and should be serviced every frame. If your application stops processing events for too long, the operating system may consider the program unresponsive.
QUIT Event Tracer
| Event | Role in this unit |
|---|---|
pygame.QUIT |
Core today: user requests that the window close |
pygame.KEYDOWN |
Preview: a key has been pressed |
pygame.KEYUP |
Preview: a key has been released |
pygame.MOUSEBUTTONDOWN |
Coming later: Mouse Control |
pygame.MOUSEMOTION |
Coming later: Mouse Control |
Render Pipeline
screen.fill()# draw...
display.flip()
Why pygame.display.flip()?
After you prepare the current frame on the display Surface,
pygame.display.flip() updates the full display so the
user can see that frame.
Render Order Lab
fill → draw → flip
draw → fill → flip
fill → draw
# no flip()
FPS Control with Clock
| Command | Purpose |
|---|---|
clock = pygame.time.Clock() |
Create one Clock object before the game loop |
clock.tick(60) |
Limit the loop to approximately a maximum of 60 frames per second |
clock.get_fps() |
Estimate the current frame rate measured by the Clock |
Extension: delta time preview
dt = clock.tick(60) / 1000
tick() returns elapsed milliseconds. Dividing by 1000
gives seconds. Later, during Movement & Animation, delta time can be
used for frame-rate-independent motion.
Game Loop Templates
for event in pygame.event.get():
# change game state
fill → draw → flip
clock.tick(FPS)
Build the Game Loop
Click the phases in the correct order for one frame.
What Belongs Where?
Extension: display FPS on the game screen
This requires pygame.font, font.render()
and blit(), so it is intentionally an extension rather
than core Game Loop content.
font = pygame.font.SysFont("Arial", 24)
fps_text = font.render(
f"FPS: {clock.get_fps():.0f}",
True,
(0, 255, 0)
)
screen.blit(fps_text, (10, 10))Game Loop Error Lab
Pitfalls & Common Errors
Not processing events
A game window must keep servicing its event queue. Otherwise it can become unresponsive and the operating system may report it as frozen.
No clock.tick() = unrestricted loop speed
Without an FPS limit, the loop can run as quickly as the machine allows, wasting resources and making frame-dependent behaviour inconsistent.
Drawing before clearing
If you draw an object and then call screen.fill(),
the background covers that object before the frame is shown.
Missing display.flip()
You can prepare a new frame on the Surface but still fail to show it. The display update belongs after drawing the current frame.
Creating Clock() inside every frame
Create the Clock once before the loop, then call
clock.tick() each iteration.
pygame.quit() inside the loop
Normally pygame.quit() belongs after the loop finishes.
Calling it every frame uninitialises modules while the program is still running.
Graded Tasks
Name the three core game-loop phases and state the role of
clock.tick().
Explain the purpose of while running: and how a
pygame.QUIT event causes the loop to finish.
Explain what the event queue is and why
pygame.event.get() should be called regularly.
Write the event-handling code that sets
running = False when the user closes the window.
Change a background variable when the user presses
Space, then use that variable when rendering the next frame.
Add pygame.time.Clock() and limit a game loop to
approximately 60 FPS.
Find and explain all problems in a loop that never processes events, clears the screen after drawing, and creates a new Clock every frame.
Complete a clean game-loop template with clearly separated Events, Update, Render and FPS control sections.
Exam Practice
Q1: Complete the missing code. [5]
running = True
while ______:
for event in pygame.event._____():
if event.type == pygame._____:
running = False
pygame.display._____()
clock._____(60)Reveal marking points
Q2: Explain why pygame.event.get() must be called regularly. [2]
Reveal marking points
Q3: State the correct rendering order. [3]
Reveal marking points
Q4: A game uses clock.tick(50). Calculate the ideal
time available per frame in milliseconds. [2]
Reveal marking points
Self-Check Quiz
pygame.event.get() return?
screen.fill() normally called before drawing the current frame?
pygame.display.flip() do?
clock.tick(60) do?
pygame.quit() normally be placed?