PYGAME MASTERY

A Comprehensive Reference Guide

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.

Table of Contents

1. INITIALIZATION & SETUP

import pygame
import sys

pygame.init()
📍 Placement: Top of the file
Explanation: Before you can use any Pygame features, you must import the library. 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")
📍 Placement: Setup phase (Before the game loop)
Explanation: Here, we define our window size using a tuple (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
📍 Placement: Setup phase (Before the game loop)
Explanation: Computers run code as fast as they possibly can. Without a clock, a fast computer might run your game at 3000 frames per second, making your character zip across the screen instantly! 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.

2. THE GAME LOOP

running = True
while running:
📍 Placement: The Main Game Loop (Below setup)
Explanation: A game is just a flipbook. It draws a picture, checks for input, and draws the next picture, over and over again. This 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
📍 Placement: Inside the Game Loop (Event handling section)
Explanation: Every time you press a key, click the mouse, or click the red 'X' on the window, Pygame creates an "Event" and puts it in a queue. 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))
📍 Placement: Inside the Game Loop (Drawing section, before drawing anything else)
Explanation: At the start of every frame, your screen still has the drawing from the previous frame on it! If you don't erase it, your moving characters will leave a smeared trail behind them. 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
📍 Placement: Inside the Game Loop (At the very end of the loop)
Explanation: 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()
📍 Placement: End of the file (Outside and below the while loop)
Explanation: Once the 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!

3. COLORS & RECTANGLES

RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
📍 Placement: Setup phase (Before the game loop)
Explanation: Computers understand colors by mixing Red, Green, and Blue light. These are called RGB tuples. Each number goes from 0 (none of that color) to 255 (maximum brightness of that color). Mixing them creates any color you want!
my_rect = pygame.Rect(100, 150, 50, 50)
📍 Placement: Setup phase (Before the game loop)
Explanation: A 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)
📍 Placement: Inside the Game Loop (Logic/Update section)
Explanation: Rects are amazing because they do math for you. If you change 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!

4. DRAWING SHAPES

pygame.draw.rect(screen, RED, my_rect, width=0)
📍 Placement: Inside the Game Loop (Drawing section, after screen.fill)
Explanation: This tells Pygame to draw a rectangle shape. It needs to know where to draw it (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)
📍 Placement: Inside the Game Loop (Drawing section, after screen.fill)
Explanation: Drawing a circle requires the surface (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.

5. HANDLING INPUT (KEYBOARD & MOUSE)

keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
    player_x += 300 * dt
📍 Placement: Inside the Game Loop (Logic/Update section)
Explanation: 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()
📍 Placement: Inside the Game Loop (Logic/Update section)
Explanation: 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.

6. IMAGES (SURFACES)

player_img = pygame.image.load("spaceship.png").convert_alpha()
📍 Placement: Setup phase (Before the game loop)
Explanation: 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)
📍 Placement: get_rect in Setup / blit inside Game Loop (Drawing)
Explanation: 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."

7. TEXT & FONTS

font = pygame.font.Font(None, 36)
📍 Placement: Setup phase (Before the game loop)
Explanation: To draw text, you first need a Font object. 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))
📍 Placement: Inside the Game Loop (Drawing section)
Explanation: Pygame can't draw raw text. It has to take the letters and "stamp" them onto a new invisible image (a Surface). font.render() creates that image. Then, we blit that new text image onto the screen!

8. SOUND & MUSIC

jump_sfx = pygame.mixer.Sound("jump.wav")
# -- later on --
jump_sfx.play()
📍 Placement: Load in Setup / play() inside Game Loop (when event happens)
Explanation: The 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)
📍 Placement: Setup phase (Before the game loop)
Explanation: The 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.

9. CLASSES & SPRITES

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))
📍 Placement: Top of the file (After imports, before Setup)
Explanation: A "Sprite" is an object that combines a picture (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)
📍 Placement: Setup Group before loop / update & draw inside Game Loop
Explanation: A 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!

10. COLLISION DETECTION

if my_rect.colliderect(obstacle_rect):
    print("Ouch!")
📍 Placement: Inside the Game Loop (Logic/Update section)
Explanation: 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)
📍 Placement: Inside the Game Loop (Logic/Update section)
Explanation: 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!

11. TIMERS AND VECTORS

current_time = pygame.time.get_ticks()
if current_time - last_action_time > 500:
    do_something()
    last_action_time = current_time
📍 Placement: Inside the Game Loop (Logic/Update section)
Explanation: 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))
📍 Placement: Create Vectors in Setup / Modify inside Game Loop
Explanation: A 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!

12. IMAGE TRANSFORMATIONS

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)
📍 Placement: Setup phase (Before the game loop)
Explanation: 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!

13. ADVANCED MOUSE CONTROL

pygame.mouse.set_visible(False)
pygame.event.set_grab(True)

# -- inside loop --
mouse_dx, mouse_dy = pygame.mouse.get_rel()
📍 Placement: Setup visible/grab before loop / get_rel inside Game Loop
Explanation: 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.

14. CUSTOM EVENTS (AUTO-TIMERS)

SPAWN_EVENT = pygame.USEREVENT + 1
pygame.time.set_timer(SPAWN_EVENT, 2000)

# -- Inside Event Loop:
if event.type == SPAWN_EVENT:
    spawn_new_item()
📍 Placement: set_timer in Setup / if event.type inside the Event Loop
Explanation: You can tell Pygame to inject a custom event into the event loop automatically! set_timer() triggers that specific event every 2000 milliseconds (2 seconds) in the background. When the event loop catches it, you run your code.

15. PIXEL-PERFECT COLLISIONS (MASKS)

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))
📍 Placement: Generate Masks in Setup / overlap() inside Game Loop
Explanation: A "Mask" ignores transparent pixels and only looks at the actual colored pixels of your image. Generate them once in setup. Inside the game loop, overlap() checks if any colored pixels are touching based on the distance (offset) between them.

16. ANIMATIONS & SPRITE SHEETS

# 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)
📍 Placement: Setup Variables before loop / Logic & Drawing inside Game Loop
Explanation: The easiest way to animate in Pygame is by storing multiple images (frames) in a list. We use a float value (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!

17. GAMEPAD & CONTROLLER INPUT

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
📍 Placement: Setup joysticks before loop / Input checking inside loops
Explanation: Pygame has fantastic built-in support for Xbox, PlayStation, and generic controllers. First, initialize the joystick module and create a list of all plugged-in controllers. You can then check for button presses using the JOYBUTTONDOWN event, or constantly check analog stick values using get_axis().

18. TRANSPARENCY & SPECIAL EFFECTS

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)
📍 Placement: Setup phase (Before the game loop)
Explanation: If you aren't using `.convert_alpha()` on PNGs with built-in transparency, you can use 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!

19. EXAMPLE GAME 1: The Bouncing Ball

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()

20. EXAMPLE GAME 2: Catch the Apples!

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()

21. EXAMPLE GAME 3: Click the Target (Mouse Input)

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()

22. EXAMPLE GAME 4: Asteroid Shooter (Sprites & Projectiles)

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()

23. THE MEGA QUICK-REFERENCE CHEAT SHEET

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)