Lecture 11: Dive into Game Development

Lecture 11: Dive into Game Development - Creating Your First Game with Pygame!

1. Introduction

Welcome, aspiring game developers, to Lecture 11! We’ve learned a lot about Python’s fundamentals, text-based applications, and even GUIs with tkinter. Now, it’s time to take a very exciting step into the world of game development with Pygame!

What is Pygame?
Pygame is a free, open-source Python library specifically designed for making 2D multimedia applications, especially games. It provides a rich set of tools (modules) for handling graphics, sound, user input (keyboard, mouse), and much more. Think of it as a super-powered toolbox for building interactive visual experiences.

Why Use Pygame?

  • It’s Fun! Creating games is one of the most engaging ways to learn and apply programming concepts.
  • Visual Feedback: You get to see your code come to life visually, which is incredibly rewarding.
  • Applies Many Concepts: Game development uses variables, loops, conditionals, functions, classes (eventually), and event-driven programming – all things we’ve been learning!
  • Great for Beginners: While powerful, Pygame is also accessible for those new to game development.

Our Goal Today:
Our mission is to understand the basics of Pygame and to build our very first simple, interactive game. We’ll learn how to:

  • Set up a Pygame window.
  • Draw shapes and manage colors.
  • Understand the all-important “game loop.”
  • Handle user input to control game elements.
  • Implement basic game mechanics like movement and collision detection.

Crucially: Installing Pygame
Pygame is an external library, meaning it’s not part of Python’s standard installation. You’ll need to install it using pip, Python’s package installer.

Open your terminal or command prompt (the same place you usually run Python scripts from) and type:

pip install pygame

This command tells pip to download Pygame from the Python Package Index (PyPI) and install it into your Python environment.

Important Notes on Installation:

  • Admin Rights: Depending on your system setup (especially on Windows), you might need to run your terminal/command prompt as an administrator to install packages globally.
  • Virtual Environments: If you’re using virtual environments (which is a good practice for managing project dependencies), make sure your virtual environment is activated before you run pip install pygame. This will install Pygame only into that specific environment.
  • Internet Connection: You’ll need an active internet connection for pip to download Pygame.
  • Troubleshooting: If you encounter issues, the error message often provides clues. Common solutions include ensuring pip itself is up-to-date (pip install --upgrade pip) or checking your Python installation. The official Pygame website also has installation guides.

For most standard Python setups, pip install pygame should work smoothly.

Get ready to think visually and interactively! Game development is a fantastic way to combine logic and creativity.

2. Core Pygame Concepts

Let’s dive into the fundamental building blocks of any Pygame program.

a. Initialization and Setup

Before you can do any Pygame magic, you need to set the stage.

  • Import Pygame:
    Just like any other library, you start by importing it:

    import pygame
    
  • Initialize Pygame Modules: pygame.init()
    Pygame is made up of several modules (for sound, display, fonts, etc.). pygame.init() is a handy function that attempts to initialize all the necessary Pygame modules for you. It’s good practice to call this at the very beginning of your Pygame script.

    pygame.init() # Initializes all imported Pygame modules
    

    If it’s successful, it returns a tuple indicating how many modules initialized successfully versus how many failed. For most cases, just calling it is enough.

  • Screen Setup: Creating Your Game Window
    This is where your game will be displayed.

    # Define screen dimensions using variables (good practice!)
    screen_width = 800  # pixels
    screen_height = 600 # pixels
    
    # Create the screen (often called 'surface' in Pygame terminology)
    # set_mode takes a tuple (width, height)
    screen = pygame.display.set_mode((screen_width, screen_height))
    
    # Set the title of the window
    pygame.display.set_caption("My First Pygame - By Priya")
    
    • pygame.display.set_mode((width, height)) creates the actual game window. The screen variable now represents this display surface, and we’ll use it to draw everything.
    • pygame.display.set_caption(...) sets the title that appears in the window’s title bar.
  • Defining Colors (RGB Tuples)
    Colors in Pygame are typically represented as RGB (Red, Green, Blue) tuples. Each value ranges from 0 to 255.

    • (0, 0, 0) is Black (no red, no green, no blue).
    • (255, 255, 255) is White (maximum red, green, and blue).
    • (255, 0, 0) is Red.
    • (0, 255, 0) is Green.
    • (0, 0, 255) is Blue.

Pro Tip: Name Your Colors!

Mixing Red, Green, and Blue (RGB) light gives us all the colors on the screen! For example, (255, 0, 0) is full red, no green, no blue.
While you can use (255, 0, 0) directly in your drawing code, giving it a name like RED = (255, 0, 0) makes your code so much easier to read. Imagine telling a friend to “draw a red box” versus “draw a (255,0,0) box”! Which is clearer?

It's very common to define color constants at the top of your script:
```python
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
LIGHT_BLUE = (173, 216, 230) # Example custom color
```

**Standalone Example: Basic Window Setup**
```python
# pygame_s0_basic_window.py
import pygame

# 1. Initialize Pygame
pygame.init()

# 2. Define Screen Dimensions and Colors
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
BACKGROUND_COLOR = (200, 200, 200) # A light gray

# 3. Create the Screen
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Pygame Basic Window - By Aarav")

# 4. Game Loop (very basic for now)
running = True
while running:
    # 4a. Event Handling
    for event in pygame.event.get(): # Get all events
        if event.type == pygame.QUIT: # If the close button is clicked
            running = False

    # 4b. Game Logic (nothing yet)

    # 4c. Drawing
    screen.fill(BACKGROUND_COLOR) # Fill the screen with our color

    # 4d. Update the display
    pygame.display.flip() # Show the new frame

# 5. Quit Pygame
pygame.quit()
```
**To Do:** Save this as `pygame_s0_basic_window.py` and run it. You should see an 800x600 window with a light gray background and the title "Pygame Basic Window - By Aarav". You can close it by clicking the window's close button.

b. The Game Loop: The Heartbeat of Your Game

The game loop is the most fundamental concept in Pygame (and most game programming). It’s a while loop that keeps running as long as your game is active. Each iteration of the loop represents one “frame” of your game.

Did You Know? The Game Loop is the Game’s Brain!

The game loop is like the brain and heart of your game, all in one! It keeps running super fast, over and over again. In each tiny moment (each ‘frame’), it quickly checks:

  • “Has the player pressed any keys?” (Events)
  • “Where should everything move?” (Game Logic)
  • “What does the screen look like now?” (Drawing)
    Without the game loop, your game would just be a frozen picture!

Why is it necessary?

  • Keeps the game running: Without it, your script would just set up the window and then end.
  • Processes user input: Checks for key presses, mouse clicks, etc.
  • Updates game state: Changes positions of objects, checks for rules, updates scores.
  • Renders graphics: Redraws everything on the screen for each new frame.

A typical game loop structure looks like this:

running = True
while running:
    # 1. Event Handling (Process Inputs)
    # ... check for keyboard, mouse, window close ...

    # 2. Game Logic Updates
    # ... move objects, check rules, update scores ...

    # 3. Drawing / Rendering (on the 'screen' surface)
    # ... fill background, draw player, draw enemies, draw text ...

    # 4. Update the Full Display
    # ... pygame.display.flip() or pygame.display.update() ...

    # 5. (Optional but Recommended) Control Game Speed / FPS
    # ... clock.tick(FPS) ...

Let’s break down these parts:

  • 1. Event Handling (Processing Inputs):
    An “event” is anything that happens that your game might need to respond to – a key press, a mouse click, or even the user clicking the window’s close button.

    for event in pygame.event.get(): # Gets a list of all events that happened
        if event.type == pygame.QUIT: # The 'X' button on the window
            running = False # This will cause the 'while running:' loop to exit
    
        if event.type == pygame.KEYDOWN: # A key was pressed down
            if event.key == pygame.K_LEFT:
                print("Left arrow key pressed")
            if event.key == pygame.K_RIGHT:
                print("Right arrow key pressed")
            if event.key == pygame.K_SPACE:
                print("Spacebar pressed")
            # Other keys: pygame.K_UP, pygame.K_DOWN, pygame.K_a (for 'a' key), etc.
    
        # if event.type == pygame.KEYUP: # A key was released
        #     if event.key == pygame.K_LEFT:
        #         print("Left arrow key released")
    
    • pygame.event.get(): Fetches all events that have occurred since the last time it was called. It’s important to loop through these.
    • event.type: Tells you what kind of event it is (e.g., pygame.QUIT, pygame.KEYDOWN).
    • event.key: If it’s a keyboard event, this tells you which key (e.g., pygame.K_LEFT).
  • 2. Game Logic Updates:
    This is where you update your game’s state based on events or time. For example:

    • Move the player character if an arrow key was pressed.
    • Move enemies or bullets.
    • Check for collisions.
    • Update scores.
    • Change game states (e.g., from “playing” to “game over”).
      We’ll see this in action in our Shape Dodger game.
  • 3. Drawing / Rendering (Order Matters!):
    This is where you draw everything onto your screen surface. The order of drawing is important. Things you draw first will be underneath things you draw later.

    • Clear the Screen: Usually, the very first drawing step in each frame is to fill the entire screen with a background color. This erases everything from the previous frame, so you’re starting fresh.

      screen.fill(BACKGROUND_COLOR)
      
    • Drawing Shapes: Pygame provides functions for drawing simple shapes.

      • pygame.draw.rect(surface, color, (x, y, width, height)): Draws a rectangle.
        • surface: Where to draw (usually your screen).
        • color: An RGB tuple (e.g., RED).
        • (x, y, width, height): A tuple defining the rectangle’s top-left corner (x,y) and its dimensions.
      • pygame.draw.circle(surface, color, (center_x, center_y), radius): Draws a circle.
        • (center_x, center_y): Coordinates of the circle’s center.
        • radius: Radius of the circle in pixels.
      • pygame.draw.line(surface, color, (start_x, start_y), (end_x, end_y), thickness): Draws a line.

      Standalone Example: Drawing Shapes

      # pygame_s1_draw_shapes.py
      import pygame
      
      pygame.init()
      SCREEN_WIDTH, SCREEN_HEIGHT = 600, 400
      screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
      pygame.display.set_caption("Drawing Shapes - By Meena")
      
      BLACK = (0,0,0); WHITE = (255,255,255); RED = (255,0,0); GREEN = (0,255,0); BLUE = (0,0,255)
      
      running = True
      while running:
          for event in pygame.event.get():
              if event.type == pygame.QUIT:
                  running = False
      
          screen.fill(WHITE) # Clear screen with white
      
          # Draw a red rectangle
          pygame.draw.rect(screen, RED, (50, 50, 100, 60)) # x, y, width, height
          # Draw a green circle
          pygame.draw.circle(screen, GREEN, (250, 80), 40) # center_x, center_y, radius
          # Draw a blue line
          pygame.draw.line(screen, BLUE, (50, 200), (350, 250), 5) # start_pos, end_pos, thickness
      
          pygame.display.flip()
      pygame.quit()
      

      Run this to see the shapes!

    • Drawing Images (Blitting): For more complex graphics (like sprites for characters or backgrounds), you’ll load images from files and then “blit” them onto the screen (copy their pixels). This looks like screen.blit(image_surface, (x, y)). We’ll touch on this in homework.

  • 4. Updating the Display:
    After you’ve drawn everything for the current frame onto the screen surface (which is like an invisible canvas at this point), you need to tell Pygame to actually show it on the monitor.

    • pygame.display.flip(): Updates the entire screen to show everything you’ve drawn since the last flip(). This is often used.
    • pygame.display.update(): Can be more efficient if only small parts of your screen changed, as you can pass it a list of rectangles representing the changed areas. For simplicity, flip() is fine for most basic games.

c. Quitting Pygame

When your game loop ends (i.e., running becomes False), you should properly uninitialize Pygame modules:

pygame.quit()

This is typically the very last line of your script.

d. Controlling Game Speed (Frames Per Second - FPS)

Computers can run while loops very fast! If you don’t control the speed of your game loop, your game might run at hundreds or thousands of frames per second, making animations too fast and potentially using a lot of CPU.
Pygame provides pygame.time.Clock to help with this:

# Before the game loop:
clock = pygame.time.Clock()
FPS = 30 # Aim for 30 frames per second (or 60 for smoother animation)

# Inside the game loop, usually as the very last thing:
clock.tick(FPS)

clock.tick(FPS) will do its best to ensure your game loop doesn’t run more than FPS times per second. It does this by introducing a small delay if the current frame finished too quickly. This makes game speed more consistent across different computers.

e. Pygame Coordinates: Navigating Your Screen

Understanding Pygame’s coordinate system is crucial for positioning things.

  • (0,0) is the TOP-LEFT corner of the screen.

  • The X-axis increases as you move to the RIGHT.

  • The Y-axis increases as you move DOWN.

    (0,0) ------> X+
      |
      |
      |
      v Y+
    (e.g., if screen is 800x600, bottom-right is (800,600))
    

This is common in computer graphics but might be different from what you learned in math class (where Y usually increases upwards).

Fun Fact: Computer Screens are Like Maps!

Think of your game screen like a map. The very top-left corner is like the starting point, (0,0). When we say a character is at x=50, y=100, it means they are 50 steps to the right from the left edge, and 100 steps down from the top edge! This is how Pygame knows where to draw things.

f. pygame.Rect Objects: For Position, Size, and Collisions

A pygame.Rect is an object that represents a rectangular area. It’s incredibly useful for:

  • Storing the position (x, y of top-left corner) and dimensions (width, height) of your game objects (player, enemies, bullets, obstacles).

  • Moving objects.

  • Detecting if two objects (rectangles) are overlapping (collision detection!).

  • Creating a Rect:
    my_rect = pygame.Rect(x, y, width, height)
    Example: player_rect = pygame.Rect(50, 100, 30, 40) (A rect at x=50, y=100, with width=30, height=40)

  • Useful Attributes: Once you have a Rect object, you can access its properties:

    • Position: my_rect.x, my_rect.y, my_rect.left, my_rect.top
    • Opposite side positions: my_rect.right (x + width), my_rect.bottom (y + height)
    • Center points: my_rect.centerx, my_rect.centery
    • Dimensions: my_rect.width, my_rect.height
    • Size as a tuple: my_rect.size (returns (width, height))
    • Center as a tuple: my_rect.center (returns (centerx, centery))
  • Moving a Rect:

    • my_rect.x += 5 (moves 5 pixels to the right)
    • my_rect.y -= 3 (moves 3 pixels up)
    • my_rect.move_ip(dx, dy): Moves the rectangle “in-place” by dx (change in x) and dy (change in y). E.g., my_rect.move_ip(5, 0) is same as my_rect.x += 5.
  • Drawing a Rect Object:
    The pygame.draw.rect() function can directly take a pygame.Rect object as its rectangle argument:
    player_rect = pygame.Rect(100, 200, 50, 50)
    pygame.draw.rect(screen, BLUE, player_rect)

    Standalone Example: Using pygame.Rect

    # pygame_s2_rect_example.py
    import pygame
    
    pygame.init()
    screen = pygame.display.set_mode((400, 300))
    pygame.display.set_caption("Rect Example - By Meera")
    GREEN = (0, 255, 0)
    RED = (255, 0, 0)
    
    # Create a Rect object for our player
    player_rect = pygame.Rect(50, 50, 40, 60) # x, y, width, height
    
    # Create another Rect for an obstacle
    obstacle_rect = pygame.Rect(200, 100, 50, 50)
    
    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_RIGHT:
                    player_rect.x += 10 # Move player right
    
        screen.fill((200, 200, 200)) # Light gray background
    
        # Draw the player and obstacle using their Rect objects
        pygame.draw.rect(screen, GREEN, player_rect)
        pygame.draw.rect(screen, RED, obstacle_rect)
    
        # Simple collision detection
        if player_rect.colliderect(obstacle_rect):
            print("Collision detected!")
            # You could change colors or end game here
            pygame.draw.rect(screen, (255,255,0), player_rect) # Yellow on collision
            pygame.draw.rect(screen, (255,255,0), obstacle_rect)
    
    
        pygame.display.flip()
    pygame.quit()
    

    Run this. Use the right arrow key to move the green rectangle. When it touches the red one, “Collision detected!” will print. player_rect.colliderect(obstacle_rect) is the key for this.

These core concepts form the foundation of most Pygame projects. Next, we’ll combine them to build our first game!

4. Project: Simple “Shape Dodger” Game (Avoid Falling Blocks)

Let’s build a fun, simple game!

Game Idea:

  • You control a player rectangle at the bottom of the screen.
  • You can move the player left and right using the arrow keys.
  • “Enemy” rectangles will fall from the top of the screen.
  • Your goal is to dodge these falling enemies.
  • If an enemy hits your player, the game is over.

a. Planning & Setup (Conceptual)

Before coding, let’s plan the main components:

  • Screen:
    • SCREEN_WIDTH = 800
    • SCREEN_HEIGHT = 600
    • FPS = 60 (for smoother animation)
  • Colors:
    • BLACK = (0, 0, 0)
    • WHITE = (255, 255, 255)
    • PLAYER_COLOR = (0, 150, 255) (a nice blue)
    • ENEMY_COLOR = (255, 60, 30) (a reddish-orange)
    • BACKGROUND_COLOR = (230, 230, 250) (lavender blush)
  • Player:
    • player_width = 50
    • player_height = 50
    • player_x (will be center of screen initially)
    • player_y (fixed near bottom of screen)
    • We’ll use a player_rect = pygame.Rect(player_x, player_y, player_width, player_height).
    • player_speed = 10 (how many pixels it moves per key press/frame)
  • Enemy:
    • enemy_width = 50
    • enemy_height = 50
    • enemy_x (will be random at the top)
    • enemy_y (will start at 0, or just off-screen at the top)
    • We’ll use an enemy_rect = pygame.Rect(enemy_x, enemy_y, enemy_width, enemy_height).
    • enemy_speed = 5 (how many pixels it falls per frame)
  • Game State:
    • running = True (for the main game loop)
    • game_over = False (to switch to a game over screen)

We’ll also need import random for placing enemies randomly.

b. Let’s Build the Pygame Shape Dodger Game Step-by-Step!

(Correction: This section title should be “Let’s Build the Pygame Shape Dodger Step-by-Step!”)

Okay, let’s start coding our Shape Dodger game! We’ll build it up feature by feature.

A Note on Building the Game: The following steps (Step 1 through Step 7, referred to as shape_dodger_s1.py, shape_dodger_s2.py, etc.) are designed to show you how a game like Shape Dodger could be built piece by piece. These are conceptual illustrations to help you understand the different components. The complete, runnable code that puts all these ideas together is available in the examples/pygame_intro/shape_dodger_game.py file. We encourage you to look at that final file as you read through these steps to see how it all connects!

Step 1: Basic Pygame Setup & Window
Create the initial Pygame window, set up the clock, and the main game loop that handles the QUIT event.

# shape_dodger_s1.py
import pygame

# --- Initialization ---
pygame.init()

# --- Screen and Display ---
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Shape Dodger - By [Your Name Here, e.g., Rohan]")

# --- Colors ---
BACKGROUND_COLOR = (230, 230, 250) # Lavender blush

# --- Clock for controlling FPS ---
clock = pygame.time.Clock()

# --- Game Loop ---
running = True
while running:
    # Event Handling
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Game Logic (nothing yet)

    # Drawing
    screen.fill(BACKGROUND_COLOR) # Clear screen

    pygame.display.flip() # Update the full display

    clock.tick(FPS) # Control game speed

# --- Quitting Pygame ---
pygame.quit()

To Do: Save this as shape_dodger_s1.py. Replace [Your Name Here, e.g., Rohan] with your name. Run it. You should see an empty lavender-colored window. You can close it with the ‘X’ button.

Step 2: Player Setup and Drawing
Define the player’s properties and draw it on the screen.

# shape_dodger_s2.py
import pygame

pygame.init()
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600; FPS = 60
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Shape Dodger - Player Setup")
BACKGROUND_COLOR = (230, 230, 250); PLAYER_COLOR = (0, 150, 255) # Blue
clock = pygame.time.Clock()

# --- Player Setup ---
player_width = 50
player_height = 50
# Start player in the middle-bottom of the screen
player_x = (SCREEN_WIDTH - player_width) // 2
player_y = SCREEN_HEIGHT - player_height - 20 # 20 pixels from bottom
player_rect = pygame.Rect(player_x, player_y, player_width, player_height)
# player_speed = 0 # Will be used for movement later

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

    # Game Logic (nothing yet)

    # Drawing
    screen.fill(BACKGROUND_COLOR)
    pygame.draw.rect(screen, PLAYER_COLOR, player_rect) # Draw the player
    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()

To Do: Save and run. You should see your blue player rectangle at the bottom-center of the screen!

Step 3: Player Movement
Let’s make the player move left and right with arrow keys.

# shape_dodger_s3.py
import pygame

pygame.init()
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600; FPS = 60
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Shape Dodger - Player Movement")
BACKGROUND_COLOR = (230, 230, 250); PLAYER_COLOR = (0, 150, 255)
clock = pygame.time.Clock()

player_width = 50; player_height = 50
player_x = (SCREEN_WIDTH - player_width) // 2
player_y = SCREEN_HEIGHT - player_height - 20
player_rect = pygame.Rect(player_x, player_y, player_width, player_height)
player_speed = 7       # How many pixels player moves per frame when key is held
player_x_change = 0    # Current change in player's x position

running = True
while running:
    # Event Handling
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN: # Key pressed
            if event.key == pygame.K_LEFT:
                player_x_change = -player_speed
            if event.key == pygame.K_RIGHT:
                player_x_change = player_speed
        if event.type == pygame.KEYUP: # Key released
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                player_x_change = 0 # Stop moving

    # Game Logic - Update player position
    player_rect.x += player_x_change

    # Boundary Checking - Keep player on screen
    if player_rect.left < 0:
        player_rect.left = 0
    if player_rect.right > SCREEN_WIDTH:
        player_rect.right = SCREEN_WIDTH

    # Drawing
    screen.fill(BACKGROUND_COLOR)
    pygame.draw.rect(screen, PLAYER_COLOR, player_rect)
    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()

To Do: Run this. You should now be able to move the player rectangle left and right using your arrow keys! It should also stop at the screen edges.

Step 4: Enemy Setup & Falling
Time to introduce an enemy that falls from the top.

# shape_dodger_s4.py
import pygame
import random # For random enemy starting position

pygame.init()
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600; FPS = 60
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Shape Dodger - Enemy Falling")
BACKGROUND_COLOR = (230,230,250); PLAYER_COLOR = (0,150,255); ENEMY_COLOR = (255,60,30) # Reddish
clock = pygame.time.Clock()

# Player
player_width = 50; player_height = 50
player_rect = pygame.Rect((SCREEN_WIDTH - player_width) // 2, SCREEN_HEIGHT - player_height - 20, player_width, player_height)
player_speed = 7; player_x_change = 0

# Enemy
enemy_width = 50; enemy_height = 50
enemy_x = random.randint(0, SCREEN_WIDTH - enemy_width) # Random x position
enemy_y = -enemy_height # Start just above the screen
enemy_rect = pygame.Rect(enemy_x, enemy_y, enemy_width, enemy_height)
enemy_speed = 5

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_LEFT: player_x_change = -player_speed
            if event.key == pygame.K_RIGHT: player_x_change = player_speed
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT: player_x_change = 0

    # Game Logic
    player_rect.x += player_x_change
    if player_rect.left < 0: player_rect.left = 0
    if player_rect.right > SCREEN_WIDTH: player_rect.right = SCREEN_WIDTH

    enemy_rect.y += enemy_speed # Enemy falls

    # Drawing
    screen.fill(BACKGROUND_COLOR)
    pygame.draw.rect(screen, PLAYER_COLOR, player_rect)
    pygame.draw.rect(screen, ENEMY_COLOR, enemy_rect) # Draw enemy
    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()

To Do: Run it. You’ll see a red enemy block falling from a random position at the top. It will fall off the bottom of the screen for now.

Step 5: Resetting the Enemy
When the enemy falls off the bottom, let’s make it reappear at the top from a new random x-position.

# shape_dodger_s5.py
# ... (Keep all imports and setup from Step 4) ...
# (The code below shows only the MODIFIED game loop, keep the setup part same as shape_dodger_s4.py)

# --- Main Game Loop (Modified) ---
# running = True # This should be defined before the loop if not already
# while running:
#     # ... (Event handling for player movement is the same) ...
#     for event in pygame.event.get():
#         if event.type == pygame.QUIT: running = False
#         if event.type == pygame.KEYDOWN:
#             if event.key == pygame.K_LEFT: player_x_change = -player_speed
#             if event.key == pygame.K_RIGHT: player_x_change = player_speed
#         if event.type == pygame.KEYUP:
#             if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT: player_x_change = 0

#     # Game Logic
#     player_rect.x += player_x_change
#     if player_rect.left < 0: player_rect.left = 0
#     if player_rect.right > SCREEN_WIDTH: player_rect.right = SCREEN_WIDTH

#     enemy_rect.y += enemy_speed

#     # Reset enemy if it goes off screen
#     if enemy_rect.top > SCREEN_HEIGHT: # If top of enemy is below bottom of screen
#         enemy_rect.y = -enemy_height    # Reset y to be just above the screen
#         enemy_rect.x = random.randint(0, SCREEN_WIDTH - enemy_width) # New random x

#     # Drawing
#     screen.fill(BACKGROUND_COLOR)
#     pygame.draw.rect(screen, PLAYER_COLOR, player_rect)
#     pygame.draw.rect(screen, ENEMY_COLOR, enemy_rect)
#     pygame.display.flip()
#     clock.tick(FPS)

# pygame.quit()

To Do: Integrate the enemy reset logic into your shape_dodger_s4.py code (or a new shape_dodger_s5.py). Now, the enemy should continuously fall and reappear.

Step 6: Collision Detection
This is where the game gets its challenge! We need to detect if the player and enemy rectangles overlap.

# shape_dodger_s6.py
# ... (Keep all imports and setup from Step 4/5) ...
# (The code below shows only the MODIFIED game loop)

# --- Main Game Loop (Modified) ---
# game_over = False # Add this before the loop
# running = True
# while running:
#     # ... (Event handling for player movement) ...
#     for event in pygame.event.get():
#         if event.type == pygame.QUIT: running = False
#         # Player movement key handling (KEYDOWN, KEYUP)
#         # ... (as before) ...

#     if not game_over: # Only update game if not game over
#         # Game Logic
#         player_rect.x += player_x_change
#         # ... (player boundary checking) ...

#         enemy_rect.y += enemy_speed
#         if enemy_rect.top > SCREEN_HEIGHT:
#             enemy_rect.y = -enemy_height
#             enemy_rect.x = random.randint(0, SCREEN_WIDTH - enemy_width)

#         # Collision Detection
#         if player_rect.colliderect(enemy_rect):
#             print("COLLISION!") # For debugging
#             game_over = True

#     # Drawing
#     screen.fill(BACKGROUND_COLOR)
#     pygame.draw.rect(screen, PLAYER_COLOR, player_rect)
#     pygame.draw.rect(screen, ENEMY_COLOR, enemy_rect)

#     if game_over:
#         # (We'll add a Game Over message here in the next step)
#         pass

#     pygame.display.flip()
#     clock.tick(FPS)

# pygame.quit()

To Do: Add the game_over variable and the collision detection logic. When the rectangles collide, “COLLISION!” should print, and the game logic (movement, enemy reset) should pause.

Step 7: Game Over State & Message
Finally, let’s display a “Game Over” message.

# shape_dodger_s7.py
# ... (Keep all imports and setup) ...
# (The code below shows additions and modifications to the game loop)

# --- Game Over Font (define before loop or in setup) ---
# game_over_font = pygame.font.SysFont(None, 75) # Use a system font
# Or load a specific font file:
# try:
#     game_over_font = pygame.font.Font("arial.ttf", 75) # Try to load Arial
# except IOError:
#     game_over_font = pygame.font.Font(None, 75) # Fallback to default system font if Arial not found

# --- Main Game Loop (Modified) ---
# game_over = False
# running = True
# while running:
#     # ... (Event handling) ...

#     if not game_over:
#         # ... (Game Logic: player movement, enemy movement/reset, collision detection) ...
#         # if player_rect.colliderect(enemy_rect): game_over = True
#         pass # Placeholder for existing logic

#     # Drawing
#     screen.fill(BACKGROUND_COLOR)
#     pygame.draw.rect(screen, PLAYER_COLOR, player_rect)
#     pygame.draw.rect(screen, ENEMY_COLOR, enemy_rect)

#     if game_over:
#         # Create text surface
#         text_surface = game_over_font.render("GAME OVER", True, (180, 0, 0)) # Dark Red
#         # Get rect of the text surface to position it
#         text_rect = text_surface.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2))
#         screen.blit(text_surface, text_rect) # Draw the text

#     pygame.display.flip()
#     clock.tick(FPS)

# pygame.quit()

To Do: Add the font creation and the “Game Over” message rendering logic. Now, when a collision occurs, the game should display “GAME OVER” in the center of the screen.

This step-by-step process should give you a much clearer idea of how a game is built with Pygame, piece by piece!

c. Full Code Presentation (shape_dodger_game.py)

Here is the complete code for our “Shape Dodger” game. This is what you should have if you’ve followed all the incremental steps and integrated them into the CalculatorApp-like class structure (though for this game, a class isn’t strictly necessary for simplicity but would be good practice for larger games).

# examples/games/shape_dodger_game.py
import pygame
import random

# --- Initialization ---
pygame.init()

# --- Screen and Display ---
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Shape Dodger - By Priya")

# --- Colors ---
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
PLAYER_COLOR = (0, 150, 255)  # Blue
ENEMY_COLOR = (220, 50, 50)   # Reddish
BACKGROUND_COLOR = (230, 230, 250) # Lavender blush
GAME_OVER_TEXT_COLOR = (180, 0, 0) # Dark Red

# --- Clock for controlling FPS ---
clock = pygame.time.Clock()

# --- Player Setup ---
player_width = 50
player_height = 50
player_x = (SCREEN_WIDTH - player_width) // 2
player_y = SCREEN_HEIGHT - player_height - 20
player_rect = pygame.Rect(player_x, player_y, player_width, player_height)
player_speed = 7
player_x_change = 0

# --- Enemy Setup ---
enemy_width = 50
enemy_height = 50
enemy_x = random.randint(0, SCREEN_WIDTH - enemy_width)
enemy_y = -enemy_height # Start just above the screen
enemy_rect = pygame.Rect(enemy_x, enemy_y, enemy_width, enemy_height)
enemy_speed = 5

# --- Font for Game Over Message ---
try:
    game_font_path = None # Pygame will use its default system font
    # You could try to specify a font file e.g. "arial.ttf" if you have one
    # game_font_path = "arial.ttf"
    game_over_font = pygame.font.Font(game_font_path, 75)
    score_font = pygame.font.Font(game_font_path, 35)
except IOError: # Fallback if specified font not found
    print("Font file not found, using default system font.")
    game_over_font = pygame.font.SysFont(None, 75) # Default system font
    score_font = pygame.font.SysFont(None, 35)


# --- Game State ---
running = True
game_over = False
score = 0

# --- Game Loop ---
while running:
    # Event Handling
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        if not game_over: # Only process movement if game is not over
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    player_x_change = -player_speed
                if event.key == pygame.K_RIGHT:
                    player_x_change = player_speed
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                    player_x_change = 0
        else: # If game is over, allow restart on space press
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    # Reset game
                    game_over = False
                    score = 0
                    player_rect.x = (SCREEN_WIDTH - player_width) // 2
                    enemy_rect.y = -enemy_height
                    enemy_rect.x = random.randint(0, SCREEN_WIDTH - enemy_width)
                    player_x_change = 0


    if not game_over:
        # Game Logic Updates
        player_rect.x += player_x_change

        # Player Boundary Checking
        if player_rect.left < 0:
            player_rect.left = 0
        if player_rect.right > SCREEN_WIDTH:
            player_rect.right = SCREEN_WIDTH

        # Enemy Movement
        enemy_rect.y += enemy_speed

        # Reset Enemy and Increment Score if it goes off screen
        if enemy_rect.top > SCREEN_HEIGHT:
            enemy_rect.y = -enemy_height
            enemy_rect.x = random.randint(0, SCREEN_WIDTH - enemy_width)
            score += 1 # Player successfully dodged an enemy
            # Optional: Increase enemy speed slightly
            if score % 5 == 0 and enemy_speed < 15: # Every 5 points
                 enemy_speed += 1


        # Collision Detection
        if player_rect.colliderect(enemy_rect):
            game_over = True

    # Drawing / Rendering
    screen.fill(BACKGROUND_COLOR) # Clear screen first
    pygame.draw.rect(screen, PLAYER_COLOR, player_rect) # Draw player
    pygame.draw.rect(screen, ENEMY_COLOR, enemy_rect)   # Draw enemy

    # Display Score
    score_text_surface = score_font.render(f"Score: {score}", True, BLACK)
    screen.blit(score_text_surface, (10, 10)) # Display score at top-left

    if game_over:
        # Create "GAME OVER" text surface
        game_over_surface = game_over_font.render("GAME OVER", True, GAME_OVER_TEXT_COLOR)
        game_over_text_rect = game_over_surface.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - 50))
        screen.blit(game_over_surface, game_over_text_rect)

        # Create "Press SPACE to Restart" text surface
        restart_surface = score_font.render("Press SPACE to Restart", True, BLACK)
        restart_text_rect = restart_surface.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 30))
        screen.blit(restart_surface, restart_text_rect)

    pygame.display.flip() # Update the full display to show everything new

    clock.tick(FPS) # Control game speed

# --- Quitting Pygame ---
pygame.quit()

d. Detailed Code Walkthrough

Now that you’ve seen the game built step-by-step and the final code, let’s review the key parts of shape_dodger_game.py. This walkthrough will help you understand how the code works. For even more details, remember to read the comments directly in the examples/pygame_intro/shape_dodger_game.py file!

  1. Initialization and Setup:

    • pygame.init(): This is like turning on the power for Pygame. It starts up all the necessary Pygame modules (for display, sound, fonts, etc.) so we can use them.
    • Screen Dimensions & FPS: SCREEN_WIDTH, SCREEN_HEIGHT, and FPS (Frames Per Second) are defined. These constants make it easy to change the window size or game speed later.
    • Colors: Colors are defined as RGB (Red, Green, Blue) tuples. For example, BLACK = (0, 0, 0) means no red, no green, and no blue, while WHITE = (255, 255, 255) is maximum of all three.
    • screen = pygame.display.set_mode(...): This function creates the actual game window (often called a “surface” in Pygame). All our drawing will happen on this surface.
      • Pygame’s Coordinate System: It’s important to know that Pygame’s screen starts with (0,0) at the top-left corner. The X value increases as you go right, and the Y value increases as you go down.
    • pygame.display.set_caption(...): This sets the title that appears in the window’s title bar.
    • clock = pygame.time.Clock(): This creates a Clock object, which we use to control the game’s frame rate. Why is this important? It ensures the game runs at a consistent speed on different computers.
  2. Player and Enemy Setup:

    • Player:
      • player_rect = pygame.Rect(player_x, player_y, player_width, player_height): A pygame.Rect object is created to represent the player. This is super useful because it stores the player’s position (x, y) and size (width, height). We use Rect objects for drawing, moving our player, and importantly, for detecting collisions!
      • player_speed: This variable determines how many pixels the player moves each frame when a key is pressed.
      • player_x_change: This variable is key to smooth movement. When you press an arrow key, player_x_change is set to player_speed (or -player_speed). In each frame of the game loop, the player’s x position is updated by this player_x_change value. When the key is released, player_x_change is set back to 0, and the player stops.
    • Enemy:
      • enemy_rect = pygame.Rect(...): Similar to the player, the enemy is also a pygame.Rect.
      • enemy_x = random.randint(0, SCREEN_WIDTH - enemy_width): The enemy starts at a random horizontal (x) position, making the game less predictable.
      • enemy_y = -enemy_height: This makes the enemy start just above the top edge of the screen. Why? So it glides smoothly into view instead of just popping into existence.
      • enemy_speed: Controls how fast the enemy falls.
  3. Font Setup:

    • game_font_large = pygame.font.SysFont(None, 75) and game_font_small = pygame.font.SysFont(None, 35): These lines create font objects. We need these to render (draw) text on the screen, like the score and “GAME OVER” message. None tells Pygame to use its default system font, and the number is the font size. The try-except block is a good way to handle cases where a specific font might not be available, falling back to a default.
  4. Game State Variables:

    • running = True: This boolean variable controls the main game loop. As long as running is True, the loop continues. If it becomes False (e.g., when the player closes the window), the loop ends, and the game quits.
    • game_over = False: This is a very important flag! It tells us whether the game is currently being played (False) or if the player has lost (True). It’s used to change what happens in the game (e.g., stop player movement, show “GAME OVER” message).
    • score = 0: Initializes the player’s score to zero at the start of the game.
  5. The Game Loop (while running:): This is the engine of our game! It continuously does three main things:

    • Phase 1: Event Handling (for event in pygame.event.get():)
      • pygame.event.get(): This function fetches all user inputs (like key presses, mouse clicks) and window events (like clicking the close button) that have happened since the last check. It returns a list of these events.
      • if event.type == pygame.QUIT: If the player clicks the window’s ‘X’ (close) button, running is set to False to end the game.
      • Handling Input based on game_over state:
        • if not game_over: If the game is not over, we process player movement:
          • if event.type == pygame.KEYDOWN: A key has been pressed down.
            • event.key == pygame.K_LEFT or pygame.K_RIGHT: We check which key. If it’s an arrow key, we set player_x_change to make the player move.
          • if event.type == pygame.KEYUP: A key has been released.
            • We set player_x_change = 0 to stop the player when the arrow key is no longer pressed.
        • else (meaning game_over is True): If the game is over:
          • We check if any key is pressed. If it’s ‘Q’, we quit (by setting running = False). Any other key restarts the game by:
            • Setting game_over = False (so game logic and movement resume).
            • Resetting score = 0.
            • Moving the player back to the center: player_rect.x = (SCREEN_WIDTH - player_width) // 2.
            • Moving the enemy back above the screen: enemy_rect.y = -enemy_height and randomizing its enemy_rect.x.
            • Resetting player_x_change = 0 so the player isn’t moving unintentionally at the restart.
    • Phase 2: Game Logic Updates (if not game_over): This section only runs if the game is active (not over). Why do we check if not game_over? Because we don’t want the player to move or enemies to fall after the game has ended!
      • player_rect.x += player_x_change: Updates the player’s horizontal position based on player_x_change.
      • Boundary Checking: if player_rect.left < 0: and if player_rect.right > SCREEN_WIDTH: These checks are crucial! They prevent the player from moving off-screen. If the player hits an edge, their position is set to be right at the edge.
      • enemy_rect.y += enemy_speed: Makes the enemy fall downwards.
      • Enemy Reset: if enemy_rect.top > SCREEN_HEIGHT:. How do we know the enemy is off-screen? When its top edge (enemy_rect.top) is below the bottom of the screen (SCREEN_HEIGHT).
        • If it is, we reset its y position to -enemy_height (back above the screen) and give it a new random x position. This makes enemies reappear continuously, keeping the game going.
        • score += 1: The player successfully dodged an enemy, so their score increases!
      • Collision Detection: if player_rect.colliderect(enemy_rect):. This is the core game challenge! colliderect() is a Pygame function that checks if two Rect objects are overlapping. If the player’s rectangle and the enemy’s rectangle touch, it returns True.
        • If a collision happens, we set game_over = True. This will change how the event handling and drawing phases work in the next loop iteration!
    • Phase 3: Drawing / Rendering: This is where we draw everything for the current frame.
      • screen.fill(BACKGROUND_COLOR): This is usually the first drawing step. It clears the entire screen with the background color. Why? To erase everything from the previous frame. Without this, you’d see trails of moving objects, like a visual echo.
      • pygame.draw.rect(screen, PLAYER_COLOR, player_rect): Draws the player rectangle at its current position.
      • pygame.draw.rect(screen, ENEMY_COLOR, enemy_rect): Draws the enemy rectangle at its current position.
      • Score Display:
        • score_text_surface = score_font.render(f"Score: {score}", True, TEXT_COLOR): To show text, we first “render” it onto a new, separate surface. True enables anti-aliasing, which makes the edges of the text look smoother.
        • screen.blit(score_text_surface, (10, 10)): Then, we “blit” (which is like copying or pasting) this text surface onto our main screen surface at the given coordinates (10,10 is near the top-left).
      • Game Over Message (if game_over):
        • If game_over is True, we render the “GAME OVER” text and the “Press any key to play again…” instructions.
        • game_over_surface.get_rect(center=(...)) is a handy Pygame method. It returns a Rect object for the text surface, and we can easily set its center attribute to position the text in the middle of the screen.
        • Then, these text surfaces are blitted to the screen.
    • pygame.display.flip(): This is essential! After drawing everything for the current frame onto the (currently invisible) screen surface, flip() makes it all visible on the monitor. Think of it as taking the drawing you just made and showing it to the player. All drawing commands since the last flip() are shown at once.
    • clock.tick(FPS): This function controls the game’s speed. It makes Pygame pause just long enough to ensure the game doesn’t run faster than FPS frames per second. This leads to consistent game speed on different computers and prevents the game from using too much CPU power. Why is this important? Without it, the game might run too fast on some computers and too slow on others!
  6. Quitting Pygame:

    • pygame.quit(): When the while running: loop finally ends (because running was set to False), this line uninitializes all the Pygame modules. It’s like turning off the power for Pygame and is important for cleanly closing your game, releasing any resources it was using.

This detailed walkthrough, along with the comments in the code itself (in examples/pygame_intro/shape_dodger_game.py), should give you a solid understanding of how our Shape Dodger game is built and operates!

5.b. Key Game Terms We Learned! (Glossary)

Here are some of the important new words and ideas we learned about making games with Pygame:

  • Game Loop: The main engine of your game that runs over and over. It handles player input, updates game objects, and redraws the screen very quickly to make things look like they are moving.
  • Event: Anything that happens in the game that your program might need to know about, like a key press, a mouse click, or someone trying to close the game window.
  • RGB Colors: A way to make colors by mixing different amounts of Red, Green, and Blue light. For example, (255, 0, 0) is bright red!
  • Coordinates (in Pygame): Like a secret code to tell Pygame where to put things on the screen. It’s usually (x, y), where ‘x’ is how far from the left edge, and ‘y’ is how far from the top edge. Remember, (0,0) is the very top-left corner!
  • pygame.Rect (Rectangle): A Pygame tool for easily storing and managing a rectangular area. It’s super useful for knowing where your player or enemies are, their size, and for checking if they bump into each other!
  • Collision Detection: Figuring out if two things in your game (like the player and an enemy) are touching or overlapping. player_rect.colliderect(enemy_rect) is one way to do this!
  • FPS (Frames Per Second): How many times your game updates and redraws the screen every second. A higher FPS usually makes the game look smoother. We use clock.tick(FPS) to control this.
  • Surface (in Pygame): Think of it like a digital piece of paper you can draw on. The main game window is a surface, and images you load also become surfaces.
  • Blitting: This is like copying and pasting an image (or another surface) onto your main game screen surface. It’s how you get your player’s picture or text to show up!

5. Recap of Concepts Learned

Wow, we’ve covered a lot and built a game! Let’s recap the key Pygame concepts:

  • Pygame Setup: import pygame, pygame.init(), creating the display screen with pygame.display.set_mode(), and setting the caption.
  • The Game Loop: The essential while running: structure that keeps the game alive.
  • Event Handling: Using pygame.event.get() to process events like pygame.QUIT (closing window) and pygame.KEYDOWN/pygame.KEYUP for keyboard input (e.g., event.key == pygame.K_LEFT).
  • Game Logic: Updating variables that control positions, scores, and game states.
  • Drawing:
    • Clearing the screen each frame: screen.fill(COLOR).
    • Drawing basic shapes: pygame.draw.rect(), pygame.draw.circle().
    • The order of drawing matters!
  • Updating Display: Using pygame.display.flip() to show the newly drawn frame.
  • pygame.Rect Objects: Using Rects to manage position and size of game objects (player_rect, enemy_rect) and for easy movement (player_rect.x += ...).
  • Collision Detection: Using player_rect.colliderect(enemy_rect) to check if two objects overlap.
  • Controlling Game Speed: Using pygame.time.Clock() and clock.tick(FPS).
  • Basic Font Rendering: Creating font objects with pygame.font.Font() or SysFont(), rendering text to a surface with font.render(), and then bliting that surface onto the screen.
  • Randomness: Using import random and random.randint() for unpredictable enemy placement.

6. Homework & Further Challenges

Ready to enhance your game or build something new?

0. Easy Tweaks (Warm-up!):

Before diving into bigger changes, try these small experiments with the shape_dodger_game.py code. It’s a great way to get comfortable!

  • Can you change the PLAYER_COLOR in the code to your favorite color? (Hint: Look for PLAYER_COLOR = (0, 150, 255) and try different RGB values!)
  • What happens if you change the player_speed variable to be much faster (e.g., 15) or slower (e.g., 2)?
  • Can you make the enemy_width or enemy_height twice as big? How does that change how the game feels?
  • Try changing the ENEMY_COLOR too!
  1. Score System Enhancements for Shape Dodger:
    • The current game increments the score when an enemy goes off-screen. Can you also make the “Game Over” screen display the final score?
  2. Multiple Enemies:
    • Modify the game to have multiple enemies falling at the same time. You’ll likely need to store them in a list.
    • Each enemy in the list will need its own pygame.Rect and potentially its own speed or starting time.
    • You’ll need to loop through your list of enemies to:
      • Update their positions.
      • Draw them.
      • Check for collision with the player for each enemy.
      • Reset them individually when they go off-screen.
        Hint for Multiple Enemies: You’ll probably want to use a Python list to keep track of all your enemy rectangles. In your game loop, you’ll need to use a for loop to go through this list to update each enemy’s position, draw it, and check if it has collided with the player or gone off-screen.
  3. Increasing Difficulty:
    • Make the enemy_speed gradually increase as the score gets higher, making the game harder over time. (The example code has a basic version of this).
  4. Player Movement - Up/Down:
    • Allow the player to also move up and down, but restrict their movement to the bottom third or half of the screen so they can’t just fly to the top.
  5. (Advanced) Using Images (Sprites):
    • Find or create small PNG images for your player and enemy (e.g., a simple spaceship and an asteroid).
    • Learn to load them using my_image = pygame.image.load("image_filename.png").convert_alpha(). .convert_alpha() is important for images with transparency.
    • Instead of pygame.draw.rect(), use screen.blit(my_player_image, player_rect) to draw your images at the position of their corresponding Rect objects. The Rect will still be useful for managing position and collisions.
  6. (Advanced) Sound Effects:
    • Find short sound effects (e.g., .wav or .ogg files) for when the player moves, an enemy is dodged, or a collision occurs.
    • Learn about pygame.mixer.init() and pygame.mixer.Sound("sound_effect.wav").
    • Play sounds using sound_object.play().

Pygame opens up a whole new world of creative programming. Experiment, have fun, and see what amazing games you can come up with!