Welcome to the ultimate step-by-step Pygame cheat sheet. This guide is designed to help you understand every part of the Pygame engine, from the first line of code to fully working games.
import pygame
import sys
pygame.init()
sys is a standard Python library used later to safely close the program. pygame.init() is the engine's starter motor. It turns on all the internal parts of Pygame (like the audio mixer, the font renderer, and the display systems) so they are ready to use. If you forget this, your game will crash immediately!
WIDTH = 800
HEIGHT = 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("My Awesome Game")
(800, 600). pygame.display.set_mode() creates the actual game window that pops up on your computer. It returns a Surface (a blank canvas) which we save in the variable screen. Everything in your game will eventually be drawn onto this screen surface. set_caption() simply changes the text at the very top of the window border.
clock = pygame.time.Clock()
FPS = 60
pygame.time.Clock() creates a timer that helps us limit the game to a specific speed (like 60 Frames Per Second), so it plays exactly the same on every computer.
running = True
while running:
while loop is the heart of your game. As long as running is True, the game stays open. If we set it to False, the loop ends and the game closes. All future "inside the game loop" code goes indented under this.
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.event.get() empties that queue and gives us a list of those events. We loop through them. If Pygame sees a QUIT event (the user clicked the window's 'X' button), we set running = False to break the game loop.
screen.fill((0, 0, 0))
screen.fill() acts like an eraser, painting the entire canvas a solid color. (0, 0, 0) is the RGB code for black.
pygame.display.flip()
dt = clock.tick(FPS) / 1000.0
pygame.display.flip() takes everything you've drawn in the background during this frame and pushes it to the actual computer monitor all at once. clock.tick(FPS) tells Pygame to pause for a tiny fraction of a second to ensure the loop only runs 60 times a second. It returns the number of milliseconds that passed since the last frame. We divide by 1000.0 to get dt (Delta Time) in seconds, which is incredibly useful for smooth movement.
pygame.quit()
sys.exit()
while loop finishes (because the player quit), we need to clean up. pygame.quit() shuts down the Pygame engine safely, and sys.exit() tells Python to close the whole program completely. Make sure this is completely un-indented!
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
my_rect = pygame.Rect(100, 150, 50, 50)
Rect (Rectangle) is Pygame's most powerful tool. It stores four numbers: X position, Y position, Width, and Height. In this example, the rectangle starts 100 pixels from the left, 150 pixels from the top, and is 50x50 pixels large. Pygame uses Rects for almost everything: drawing, positioning, and checking if two objects crashed into each other.
my_rect.center = (400, 300)
print(my_rect.right)
my_rect.center, Pygame automatically recalculates my_rect.x and my_rect.y to move the box. You can instantly access the top, bottom, left, right, centerx, or centery of any box just by asking for it!
pygame.draw.rect(screen, RED, my_rect, width=0)
screen), what color (RED), and what dimensions (my_rect). The width=0 part means the shape will be filled in completely. If you changed it to width=5, it would draw a hollow box with a 5-pixel thick border.
pygame.draw.circle(screen, BLUE, (400, 300), 25)
screen), the color (BLUE), a tuple for the exact center X and Y coordinates (400, 300), and the radius (25 pixels). The radius is the distance from the center to the edge, so this circle will be 50 pixels wide in total.
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
player_x += 300 * dt
pygame.key.get_pressed() looks at your keyboard and returns a giant list showing the current state (pressed or not pressed) of every single key. We check if the Right Arrow key (K_RIGHT) is currently held down. If it is, we move the player. Multiplying our speed (300) by dt ensures the player moves exactly 300 pixels per second, regardless of how fast the computer runs.
mouse_x, mouse_y = pygame.mouse.get_pos()
left_click, middle_click, right_click = pygame.mouse.get_pressed()
get_pos() returns a tuple (X, Y) of exactly where the user's mouse pointer is right now. get_pressed() returns three True/False values telling you if the left, middle (scroll wheel), or right mouse buttons are currently being held down.
player_img = pygame.image.load("spaceship.png").convert_alpha()
pygame.image.load() reads a picture file from your computer. .convert_alpha() translates that picture into a format that Pygame's graphics card can draw lightning-fast, while preserving transparent backgrounds. Doing this inside the game loop causes massive lag, so load it before the loop starts!
img_rect = player_img.get_rect(center=(400, 300))
# -- inside loop --
screen.blit(player_img, img_rect)
get_rect() looks at your image and generates a Pygame Rect that is the exact width and height of the picture! blit stands for "Block Transfer". It literally means "draw this image surface onto the screen surface."
font = pygame.font.Font(None, 36)
None tells Pygame to use the computer's default font. 36 is the font size. You only ever need to do this ONCE, before your game loop starts. Loading a font repeatedly is very slow.
text_surf = font.render("Score: 100", True, WHITE)
screen.blit(text_surf, (10, 10))
font.render() creates that image. Then, we blit that new text image onto the screen!
jump_sfx = pygame.mixer.Sound("jump.wav")
# -- later on --
jump_sfx.play()
Sound object is used for short, quick audio clips. Load it before the game loop so the entire sound is in the computer's memory. Call .play() during the game loop inside an `if` statement when an action occurs.
pygame.mixer.music.load("background.mp3")
pygame.mixer.music.play(-1)
music module is used for long background songs. Load it and start playing it right before your while loop begins. Passing -1 to play() tells Pygame to loop the song infinitely forever in the background.
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50, 50))
self.image.fill(GREEN)
self.rect = self.image.get_rect(center=(100, 100))
self.image) and a position (self.rect). When you make your own Player class, you MUST inherit from pygame.sprite.Sprite and call super().__init__().
all_sprites = pygame.sprite.Group()
my_player = Player()
all_sprites.add(my_player)
# -- inside loop --
all_sprites.update(dt)
all_sprites.draw(screen)
Group is a container that holds multiple sprites. Create it and add your sprites before the loop starts. In your main loop, calling update() automatically updates every sprite, and draw() automatically blits them all to the screen for you!
if my_rect.colliderect(obstacle_rect):
print("Ouch!")
colliderect does the math to check if the boundaries of your player's rectangle have touched or overlapped the boundaries of the obstacle's rectangle.
hits = pygame.sprite.spritecollide(my_player, obstacle_group, True)
spritecollide checks one single sprite against an entire Group. The True means "Do Kill" - Pygame will automatically delete any obstacle from memory that touched the player!
current_time = pygame.time.get_ticks()
if current_time - last_action_time > 500:
do_something()
last_action_time = current_time
get_ticks() returns how many milliseconds have passed since the game started. Subtracting the last_action_time (a variable you set to 0 in Setup) from current_time lets you build weapon cooldowns or delay actions.
position = pygame.math.Vector2(100, 100)
velocity = pygame.math.Vector2(5, 0)
# -- inside loop --
position += velocity
my_rect.center = (round(position.x), round(position.y))
Vector2 is a mathematical tool holding an X and Y value. It is much better for movement than standard variables because you can add them together directly for incredibly precise movement!
scaled_img = pygame.transform.scale(player_img, (100, 100))
rotated_img = pygame.transform.rotate(player_img, 45)
flipped_img = pygame.transform.flip(player_img, True, False)
scale() forcefully resizes an image. rotate() spins your image counter-clockwise. flip() mirrors an image. DO NOT do this inside the game loop every frame if you can avoid it, as resizing/rotating graphics takes a lot of processing power. Do it once in setup!
pygame.mouse.set_visible(False)
pygame.event.set_grab(True)
# -- inside loop --
mouse_dx, mouse_dy = pygame.mouse.get_rel()
set_visible(False) hides the cursor. set_grab(True) locks the mouse inside the window so the player doesn't accidentally click outside. get_rel() tells you exactly how many pixels the mouse has moved since the last frame.
SPAWN_EVENT = pygame.USEREVENT + 1
pygame.time.set_timer(SPAWN_EVENT, 2000)
# -- Inside Event Loop:
if event.type == SPAWN_EVENT:
spawn_new_item()
set_timer() triggers that specific event every 2000 milliseconds (2 seconds) in the background. When the event loop catches it, you run your code.
mask1 = pygame.mask.from_surface(player_img)
mask2 = pygame.mask.from_surface(obstacle_img)
# -- inside loop --
offset_x = obstacle_rect.x - player_rect.x
offset_y = obstacle_rect.y - player_rect.y
exact_hit = mask1.overlap(mask2, (offset_x, offset_y))
overlap() checks if any colored pixels are touching based on the distance (offset) between them.
# Assuming we have a list of images loaded into a list called 'frames'
current_frame = 0
animation_speed = 0.15 # Higher = faster animation
# -- Inside your Game Loop (Update section):
current_frame += animation_speed
if current_frame >= len(frames):
current_frame = 0 # Loop back to the first frame
# -- Inside your Game Loop (Drawing section):
screen.blit(frames[int(current_frame)], player_rect)
current_frame) that constantly increases. Because lists require whole numbers for indexes, we use int() to round down. When the value hits the length of the list, we reset it to 0 to loop the animation!
pygame.joystick.init()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
# -- Inside the Event Loop:
if event.type == pygame.JOYBUTTONDOWN:
if event.button == 0: # Usually the 'A' or 'X' button
player_jump()
# -- Inside the Game Loop (Logic Section):
if len(joysticks) > 0:
x_axis = joysticks[0].get_axis(0) # Left thumbstick horizontal (-1 to 1)
if abs(x_axis) > 0.1: # Deadzone to prevent drift
player.rect.x += x_axis * speed * dt
JOYBUTTONDOWN event, or constantly check analog stick values using get_axis().
ghost_surface = pygame.image.load("ghost.png").convert()
ghost_surface.set_colorkey((0, 0, 0)) # Makes pure black pixels transparent
ghost_surface.set_alpha(128) # Sets overall opacity (0 = invisible, 255 = solid)
set_colorkey() to pick one specific color (like a bright green or black background) and make Pygame ignore it entirely. set_alpha() applies transparency to the entire image, which is perfect for fading things in and out, or making ghosts/glass!
Here is a complete, runnable script! It demonstrates Initialization, Vectors, Rectangles, Drawing, and the Game Loop all in one place.
import pygame
import sys
pygame.init()
# Setup Screen
WIDTH, HEIGHT = 640, 480
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Bouncing Ball Example")
clock = pygame.time.Clock()
# Ball Variables
ball_pos = pygame.math.Vector2(WIDTH/2, HEIGHT/2)
ball_vel = pygame.math.Vector2(250, 200) # Speed in pixels per second
ball_radius = 20
running = True
while running:
# 1. Event Handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Calculate Delta Time
dt = clock.tick(60) / 1000.0
# 2. Game Logic (Movement & Bouncing)
ball_pos += ball_vel * dt
# Bounce off Left/Right walls
if ball_pos.x - ball_radius <= 0 or ball_pos.x + ball_radius >= WIDTH:
ball_vel.x *= -1 # Reverse X direction
# Bounce off Top/Bottom walls
if ball_pos.y - ball_radius <= 0 or ball_pos.y + ball_radius >= HEIGHT:
ball_vel.y *= -1 # Reverse Y direction
# 3. Drawing
screen.fill((30, 30, 30)) # Dark gray background
pygame.draw.circle(screen, (0, 255, 100), (int(ball_pos.x), int(ball_pos.y)), ball_radius)
pygame.display.flip()
pygame.quit()
sys.exit()
This game puts together Keyboard Input, Rectangles for collision, Fonts for scoring, and randomly falling objects. Use the Left/Right arrows to catch!
import pygame
import random
import sys
pygame.init()
# Setup Display and Font
WIDTH, HEIGHT = 500, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Catch the Apples!")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 40)
# Game Objects
player = pygame.Rect(WIDTH//2 - 40, HEIGHT - 60, 80, 20)
apple = pygame.Rect(random.randint(0, WIDTH-30), 0, 30, 30)
score = 0
apple_speed = 250
running = True
while running:
dt = clock.tick(60) / 1000.0
# 1. Check Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 2. Player Movement
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player.left > 0:
player.x -= 350 * dt
if keys[pygame.K_RIGHT] and player.right < WIDTH:
player.x += 350 * dt
# 3. Apple Logic (Falling and Resetting)
apple.y += apple_speed * dt
# If apple hits the bottom, reset it
if apple.top > HEIGHT:
apple.y = -30
apple.x = random.randint(0, WIDTH-30)
# 4. Collision Detection (Catching the apple)
if player.colliderect(apple):
score += 1
apple_speed += 10 # Make it harder!
# Reset apple to top
apple.y = -30
apple.x = random.randint(0, WIDTH-30)
# 5. Drawing
screen.fill((135, 206, 235)) # Sky blue background
# Draw Player (Basket)
pygame.draw.rect(screen, (139, 69, 19), player)
# Draw Apple
pygame.draw.circle(screen, (255, 0, 0), apple.center, 15)
# Draw Score Text
score_text = font.render(f"Score: {score}", True, (255, 255, 255))
screen.blit(score_text, (10, 10))
pygame.display.flip()
pygame.quit()
sys.exit()
This game teaches you how to use mouse clicks and custom timers. The target jumps around every second. Click it to score!
import pygame
import random
import sys
pygame.init()
WIDTH, HEIGHT = 600, 500
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Click the Target!")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 45)
# Game Objects
target_rect = pygame.Rect(WIDTH//2, HEIGHT//2, 50, 50)
score = 0
# Custom Event for moving the target automatically
MOVE_TARGET_EVENT = pygame.USEREVENT + 1
pygame.time.set_timer(MOVE_TARGET_EVENT, 1000) # Move every 1000ms (1 sec)
def move_target():
target_rect.x = random.randint(0, WIDTH - target_rect.width)
target_rect.y = random.randint(50, HEIGHT - target_rect.height)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Check Custom Timer Event
if event.type == MOVE_TARGET_EVENT:
move_target()
# Check Mouse Clicks
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Left Click
mouse_pos = event.pos # (X, Y) tuple
if target_rect.collidepoint(mouse_pos):
score += 1
move_target() # Move it instantly when clicked
# Reset the timer so it doesn't move right after clicking
pygame.time.set_timer(MOVE_TARGET_EVENT, 1000)
# Drawing
screen.fill((40, 40, 60))
pygame.draw.rect(screen, (255, 215, 0), target_rect, border_radius=25) # Round target
# Text
score_surf = font.render(f"Score: {score}", True, (255, 255, 255))
screen.blit(score_surf, (20, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
This game puts Sprite Groups and Projectiles to the test! Press Spacebar to shoot lasers at falling blocks.
import pygame
import random
import sys
pygame.init()
WIDTH, HEIGHT = 500, 700
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Asteroid Shooter")
clock = pygame.time.Clock()
# --- SPRITE CLASSES ---
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((40, 40))
self.image.fill((0, 255, 0)) # Green player
self.rect = self.image.get_rect(center=(WIDTH//2, HEIGHT - 50))
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and self.rect.left > 0:
self.rect.x -= 5
if keys[pygame.K_RIGHT] and self.rect.right < WIDTH:
self.rect.x += 5
class Laser(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((5, 15))
self.image.fill((255, 0, 0)) # Red laser
self.rect = self.image.get_rect(center=(x, y))
def update(self):
self.rect.y -= 10
if self.rect.bottom < 0:
self.kill() # Remove from memory if it goes off screen
class Asteroid(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((30, 30))
self.image.fill((150, 150, 150)) # Gray asteroid
self.rect = self.image.get_rect(center=(random.randint(20, WIDTH-20), -20))
def update(self):
self.rect.y += 3
if self.rect.top > HEIGHT:
self.kill()
# --- SETUP GROUPS ---
all_sprites = pygame.sprite.Group()
lasers = pygame.sprite.Group()
asteroids = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
# Custom Timer to spawn asteroids
SPAWN_ASTEROID = pygame.USEREVENT + 1
pygame.time.set_timer(SPAWN_ASTEROID, 800)
score = 0
running = True
while running:
# 1. Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == SPAWN_ASTEROID:
asteroid = Asteroid()
all_sprites.add(asteroid)
asteroids.add(asteroid)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
# Shoot a laser from the player's top center!
laser = Laser(player.rect.centerx, player.rect.top)
all_sprites.add(laser)
lasers.add(laser)
# 2. Update all sprites
all_sprites.update()
# 3. Collision Logic (Lasers hitting Asteroids)
# groupcollide(group1, group2, dokill1, dokill2)
hits = pygame.sprite.groupcollide(asteroids, lasers, True, True)
for hit in hits:
score += 1 # We get a point for every asteroid destroyed
# 4. Draw
screen.fill((10, 10, 30)) # Dark space background
all_sprites.draw(screen)
font = pygame.font.Font(None, 36)
score_text = font.render(f"Score: {score}", True, (255, 255, 255))
screen.blit(score_text, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
No explanations here! Just raw, copy-pasteable code for when you forget a function's name or parameters.
# --- INITIALIZATION & DISPLAY ---
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
screen = pygame.display.set_mode((800, 600), pygame.FULLSCREEN | pygame.RESIZABLE)
pygame.display.set_caption("Title")
pygame.display.flip() # Updates the entire screen
pygame.display.update(rect) # Updates just a specific area
# --- TIMERS & CLOCK ---
clock = pygame.time.Clock()
dt = clock.tick(60) / 1000.0 # Returns seconds since last frame
time_now = pygame.time.get_ticks() # Milliseconds since init
pygame.time.set_timer(pygame.USEREVENT + 1, 1000) # Auto-trigger event
# --- EVENTS ---
for event in pygame.event.get():
if event.type == pygame.QUIT: ...
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE: ...
if event.type == pygame.KEYUP: ...
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: ... # 1=Left, 2=Middle, 3=Right
# --- KEYBOARD & MOUSE STATES ---
keys = pygame.key.get_pressed()
if keys[pygame.K_w]: ...
mouse_pos = pygame.mouse.get_pos() # Returns (x, y)
mouse_buttons = pygame.mouse.get_pressed() # Returns (Left, Middle, Right)
# --- GAMEPAD / CONTROLLERS ---
pygame.joystick.init()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
# Inside Event Loop: event.type == pygame.JOYBUTTONDOWN (event.button)
# --- RECTANGLES ---
rect = pygame.Rect(x, y, width, height)
rect.center = (x, y)
# Properties: .x, .y, .top, .bottom, .left, .right, .centerx, .centery, .width, .height
is_hit = rect.colliderect(other_rect)
is_hit = rect.collidepoint((mouse_x, mouse_y))
# --- DRAWING ---
screen.fill((255, 255, 255)) # RGB color tuple
pygame.draw.rect(surface, color, rect, width=0, border_radius=0)
pygame.draw.circle(surface, color, center_pos, radius, width=0)
pygame.draw.line(surface, color, start_pos, end_pos, width=1)
pygame.draw.polygon(surface, color, [(x1,y1), (x2,y2), ...], width=0)
# --- IMAGES & SURFACES ---
img = pygame.image.load("file.png").convert_alpha()
surface = pygame.Surface((width, height))
surface.set_colorkey((0,0,0)) # Makes black transparent
surface.set_alpha(128) # 0-255 transparency
screen.blit(source_surface, destination_rect_or_tuple)
# --- TRANSFORMATIONS ---
scaled = pygame.transform.scale(img, (new_w, new_h))
rotated = pygame.transform.rotate(img, degrees_angle)
flipped = pygame.transform.flip(img, flip_x_bool, flip_y_bool)
# --- FONTS ---
font = pygame.font.Font("font.ttf", size) # Or None for default
text_surface = font.render("Hello", True, (255,255,255)) # True = antialias
# --- AUDIO ---
sound = pygame.mixer.Sound("sfx.wav")
sound.set_volume(0.5) # 0.0 to 1.0
sound.play()
pygame.mixer.music.load("song.mp3")
pygame.mixer.music.play(-1) # -1 = Loop forever
pygame.mixer.music.stop()
# --- SPRITES ---
class MySprite(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((50, 50))
self.rect = self.image.get_rect()
group = pygame.sprite.Group()
group.add(my_sprite)
group.update()
group.draw(screen)
# Sprite Collisions
hit_list = pygame.sprite.spritecollide(sprite, group, dokill)
hit_dict = pygame.sprite.groupcollide(group1, group2, dokill1, dokill2)