Creating a small game is a practical way to learn how a program reacts continuously to input. With Pygame for beginners, you can open a graphical window, draw shapes, process keyboard events, control frame rate, detect collisions, display a score, and build a complete playable project with ordinary Python code.
This guide starts with installation and the basic game loop, then builds a simple dodge game. The player moves horizontally while blocks fall from the top of the screen. Every block that reaches the bottom increases the score; touching one ends the round.
Pygame is especially useful after you understand basic variables, loops, functions, and conditions. A smaller project using only the standard library is available in the companion tutorial to build a Snake game with Turtle.
What Is Pygame?
Pygame is a collection of Python modules for creating games and multimedia applications. It provides access to windows, drawing, images, fonts, sound, input events, timing, and collision-friendly rectangle objects.
The official Pygame documentation contains the API reference, while the Pygame introduction explains the basic structure of a program.
Pygame is not a visual game engine. You write the loop and game rules yourself, which makes it a strong learning tool. For larger projects, classes and Python dataclasses can help organize players, enemies, and configuration.
Install Pygame
Create and activate a virtual environment, then install the package:
python -m pip install pygameVerify the installation:
python -m pygame.examples.aliensIf a Pygame example opens, the package and graphical environment are working. In an editor, ensure the selected Python interpreter is the one where you installed Pygame.
Understand the Game Loop
A real-time game repeatedly performs four steps:
- Read events and input.
- Update positions and game state.
- Draw the current frame.
- Wait long enough to maintain a controlled frame rate.
The loop continues until the player closes the window or a game condition stops it. A clock prevents the game from running at a different speed on every computer.
Create Your First Pygame Window
import pygame
pygame.init()
WIDTH = 800
HEIGHT = 600
BACKGROUND = (20, 24, 35)
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("My First Pygame")
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(BACKGROUND)
pygame.display.flip()
clock.tick(60)
pygame.quit()pygame.init() initializes imported Pygame modules. set_mode() creates the display surface. pygame.display.flip() presents the completed frame, and clock.tick(60) limits the loop to approximately 60 frames per second.
Coordinates, Colors, and Rectangles
Pygame uses a coordinate system whose origin is the top-left corner. The x-coordinate increases to the right and the y-coordinate increases downward.
Colors are usually RGB tuples:
WHITE = (255, 255, 255)
RED = (239, 68, 68)
BLUE = (59, 130, 246)A pygame.Rect stores position and size and includes convenient properties such as left, right, centerx, and bottom.
player = pygame.Rect(375, 520, 50, 50)
pygame.draw.rect(screen, BLUE, player)Rectangles also simplify collision detection. The method player.colliderect(enemy) returns whether two rectangles overlap.
Process Continuous Keyboard Input
Window events are read from pygame.event.get(). For movement that continues while a key is held, use pygame.key.get_pressed().
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.x -= player_speed
if keys[pygame.K_RIGHT]:
player.x += player_speedKeep the player inside the window with clamp_ip():
player.clamp_ip(screen.get_rect())This is clearer than writing four separate boundary comparisons. Game code often benefits from small functions with clear return values rather than one very long loop.
Make Movement Independent of Frame Rate
Moving a fixed number of pixels per frame can still behave differently if the frame rate drops. Use elapsed time, commonly called delta time:
delta_time = clock.tick(60) / 1000
player.x += int(player_speed * delta_time)If player_speed represents pixels per second, multiplying by seconds elapsed keeps movement more consistent. For this beginner project, enemy spawning also uses elapsed time rather than a random chance on every frame.
Create Falling Enemies
Store enemy rectangles in a list. A timer controls when a new one appears:
import random
enemies = []
spawn_timer = 0
spawn_interval = 700
spawn_timer += delta_time * 1000
if spawn_timer >= spawn_interval:
enemy_width = 45
x = random.randint(0, WIDTH - enemy_width)
enemies.append(pygame.Rect(x, -45, 45, 45))
spawn_timer = 0For security-sensitive random values, Python has the secrets module, but ordinary game randomness should use random because cryptographic unpredictability is unnecessary here.
Move and Remove Enemies Safely
Do not remove items from a list while iterating over that same list unless you carefully control the iteration. A list comprehension can keep only active enemies:
for enemy in enemies:
enemy.y += int(enemy_speed * delta_time)
active_enemies = []
for enemy in enemies:
if enemy.top <= HEIGHT:
active_enemies.append(enemy)
else:
score += 1
enemies = active_enemiesThis approach also provides a natural place to increase the score when an obstacle leaves the screen.
Detect Collisions
if any(player.colliderect(enemy) for enemy in enemies):
game_over = Trueany() stops after finding the first collision. For image-based games, rectangular collisions are often good enough. Pygame also supports masks for pixel-level collision tests when accurate sprite shapes are required.
Render Text and Score
font = pygame.font.Font(None, 36)
score_surface = font.render(
f"Score: {score}",
True,
WHITE,
)
screen.blit(score_surface, (20, 20))Rendering creates a surface containing the text. blit() copies that surface to the main display at the chosen position.
Complete Beginner Dodge Game
import random
import pygame
pygame.init()
WIDTH = 800
HEIGHT = 600
FPS = 60
BACKGROUND = (17, 24, 39)
PLAYER_COLOR = (59, 130, 246)
ENEMY_COLOR = (239, 68, 68)
TEXT_COLOR = (255, 255, 255)
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Python Dodge Game")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 36)
large_font = pygame.font.Font(None, 64)
player = pygame.Rect(
WIDTH // 2 - 25,
HEIGHT - 80,
50,
50,
)
player_speed = 360
enemy_speed = 240
spawn_interval = 700
spawn_timer = 0
enemies = []
score = 0
running = True
game_over = False
while running:
delta_time = clock.tick(FPS) / 1000
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if (
event.type == pygame.KEYDOWN
and event.key == pygame.K_r
and game_over
):
enemies.clear()
player.centerx = WIDTH // 2
score = 0
spawn_timer = 0
game_over = False
if not game_over:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
player.x -= int(player_speed * delta_time)
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
player.x += int(player_speed * delta_time)
player.clamp_ip(screen.get_rect())
spawn_timer += delta_time * 1000
if spawn_timer >= spawn_interval:
size = random.randint(35, 65)
x = random.randint(0, WIDTH - size)
enemies.append(pygame.Rect(x, -size, size, size))
spawn_timer = 0
for enemy in enemies:
enemy.y += int(enemy_speed * delta_time)
active_enemies = []
for enemy in enemies:
if enemy.top <= HEIGHT:
active_enemies.append(enemy)
else:
score += 1
enemies = active_enemies
if any(player.colliderect(enemy) for enemy in enemies):
game_over = True
screen.fill(BACKGROUND)
pygame.draw.rect(screen, PLAYER_COLOR, player)
for enemy in enemies:
pygame.draw.rect(screen, ENEMY_COLOR, enemy)
score_surface = font.render(
f"Score: {score}",
True,
TEXT_COLOR,
)
screen.blit(score_surface, (20, 20))
if game_over:
message = large_font.render(
"Game Over",
True,
TEXT_COLOR,
)
restart = font.render(
"Press R to restart",
True,
TEXT_COLOR,
)
screen.blit(
message,
message.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 25)),
)
screen.blit(
restart,
restart.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 30)),
)
pygame.display.flip()
pygame.quit()Save the file as dodge_game.py, then run python dodge_game.py. Move with the left and right arrows or A and D. After a collision, press R to restart.
How the Complete Program Is Organized
- Initialization: starts Pygame, creates the window, fonts, and clock.
- State: stores the player, enemies, score, timers, and game-over flag.
- Events: handles closing the window and restarting.
- Update: moves objects, spawns enemies, removes old ones, and detects collisions.
- Render: clears the screen and draws the latest state.
- Timing: limits FPS and calculates delta time.
As the project grows, extract those responsibilities into functions or classes. Add Python logging for startup errors, missing assets, and unexpected failures instead of filling the loop with print calls.
Add Images and Sound
Replace a colored rectangle with an image:
player_image = pygame.image.load("player.png").convert_alpha()
player_image = pygame.transform.scale(
player_image,
player.size,
)
screen.blit(player_image, player)Load assets once before the game loop. Repeated disk access and conversion inside every frame will reduce performance.
For sound:
pygame.mixer.music.load("music.ogg")
pygame.mixer.music.play(-1)
hit_sound = pygame.mixer.Sound("hit.wav")Use files you have permission to distribute. Keep asset paths relative to a known project directory; the Python pathlib guide shows a reliable way to build those paths.
Common Beginner Mistakes
- Forgetting to process the event queue, causing the window to appear frozen.
- Not limiting FPS, making movement hardware-dependent.
- Loading images or fonts inside the game loop.
- Updating the display before drawing the full frame.
- Removing list items during a forward iteration.
- Mixing event handling, updating, and drawing into one unstructured block.
- Using fixed per-frame movement without considering delta time.
- Calling
pygame.quit()before the loop has ended.
Ways to Extend the Project
- Increase enemy speed as the score rises.
- Add lives instead of ending after one collision.
- Save the high score to JSON.
- Add sprites and animation frames.
- Add a start screen and pause state.
- Create separate Player and Enemy classes.
- Add sound effects and background music.
- Write unit tests for non-graphical helper functions.
For another playable project, continue with the Snake game in Python. It demonstrates grid movement and a growing body rather than continuous pixel movement.
Frequently Asked Questions
Is Pygame included with Python?
No. Install it with python -m pip install pygame.
Can Pygame create professional games?
It can create complete 2D games and prototypes. Larger teams may prefer engines with built-in editors, asset pipelines, and deployment tools.
Why does my window stop responding?
The program must regularly process pygame.event.get(). Long blocking tasks inside the loop prevent events and drawing from being handled.
Does Pygame support controllers?
Yes. Its joystick and controller APIs can read supported gamepads.
Can I export a Pygame game as an executable?
Packaging tools such as PyInstaller can bundle the program, but you must include images, sounds, fonts, and other asset files correctly.
Conclusion
Pygame teaches the core architecture behind real-time applications: input, state updates, rendering, timing, and collision detection. The dodge game provides a complete foundation without hiding the logic behind a visual editor.
Start by changing colors, speeds, sizes, and spawn intervals. Then add one feature at a time and reorganize the code when repetition appears. That process develops both game-design intuition and stronger Python programming habits.






