DEV Community

Cover image for How to Build Your First Python Game: A Step-by-Step Guide to Creating a Simple Shooter with PyGame
Lou Creemers
Lou Creemers

Posted on

How to Build Your First Python Game: A Step-by-Step Guide to Creating a Simple Shooter with PyGame

Hi lovely readers,

Have you ever wanted to create your own video game? Maybe you’ve thought about building a simple shooter game where you can move around, dodge incoming enemies, and blast away at targets. Well, today’s your lucky day! We’re going to dive into the wonderful world of PyGame, a fantastic Python library that makes game development accessible and fun, even if you’ve only dabbled in Python with basic console applications.

If you already know the basics of Python—things like variables, loops, conditions, and functions—you’re in the perfect spot to start building your own game. Don’t worry if you’ve never used PyGame before; by the end of this post, you’ll have a basic but functional game to show off. So let’s get started!

Why PyGame?

Before we jump into the code, let’s take a moment to talk about why PyGame is such a great tool for building games, especially if you’re a beginner. PyGame is a 2D desktop game library that is:

  • Easy to Learn: PyGame is straightforward and beginner-friendly. It abstracts many of the complex parts of game development, letting you focus on building your game.
  • Cross-Platform: Games made with PyGame can run on Windows, Mac, and Linux without any changes to your code.
  • Active: There’s a large and helpful community of developers using PyGame. You can find tons of tutorials, examples, and forums where you can ask questions and share your projects.

Setting Up Your Environment

Before we start coding, you’ll need to have Python installed on your computer. If you don’t have it yet, head over to python.org and download the latest version. It’s important to have Python set up correctly because it’s the foundation that PyGame runs on.

Next, you need to install PyGame. This is a library that provides the tools you need to create games, like managing windows, drawing shapes, and handling user input. Installing PyGame is easy—just open your terminal (or command prompt if you’re on Windows) and type:

pip install pygame
Enter fullscreen mode Exit fullscreen mode

Once that’s done, you’re ready to start creating your game!

Step 1: Setting Up the Game Window

The first thing we need to do is create a window where the game will run. This window is where all the action will happen, so think of it as the stage for your game. Let’s write the code to set this up.

import pygame
import sys

# Initialize PyGame
pygame.init()

# Set up the game window
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Simple Shooter Game")

# Set the frame rate
clock = pygame.time.Clock()

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    # Fill the screen with a color (black in this case)
    screen.fill((0, 0, 0))

    # Update the display
    pygame.display.flip()

    # Cap the frame rate at 60 frames per second
    clock.tick(60)
Enter fullscreen mode Exit fullscreen mode

Let’s break this down:

  1. Importing Libraries: We start by importing pygame and sys. The pygame library is what we’ll use to create the game, while sys helps us cleanly exit the program when needed.

  2. Initializing PyGame: The line pygame.init() is crucial—it sets up all the modules that PyGame needs to run. You should always call this at the beginning of your PyGame projects.

  3. Creating the Game Window: We use pygame.display.set_mode() to create a window with a width of 800 pixels and a height of 600 pixels. This is where everything in our game will be displayed. The pygame.display.set_caption() function lets us set the title of the window to something meaningful, like "Simple Shooter Game".

  4. Setting Up the Frame Rate: The clock = pygame.time.Clock() line creates a clock object that helps us control how fast the game runs. By setting the frame rate to 60 frames per second, we ensure that the game runs smoothly.

  5. Main Game Loop: The while True loop is the heart of our game. It keeps running, allowing us to update the game and check for events like closing the window. Inside this loop:
    * Event Handling: We use pygame.event.get() to check if the player wants to quit the game. If they do, we call pygame.quit() to clean up and sys.exit() to exit the program.
    * Drawing the Background: The screen.fill((0, 0, 0)) line fills the screen with black, essentially clearing it for the next frame.
    * Updating the Display: Finally, pygame.display.flip() updates the window to show whatever we’ve drawn.

When you run this code, you should see a plain black window. Congratulations! You’ve just set up the foundation of your game.

Step 2: Adding the Player

Now that we have a game window, let’s add something more interesting—a player character. For simplicity, we’ll represent the player as a rectangle that you can move left and right. The enemies will also be represented as rectangles, keeping things simple and focused on the game logic rather than complex graphics

# Player settings
player_width = 50
player_height = 60
player_x = screen_width // 2 - player_width // 2
player_y = screen_height - player_height - 10
player_speed = 5

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    # Handle player movement
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_x > 0:
        player_x -= player_speed
    if keys[pygame.K_RIGHT] and player_x < screen_width - player_width:
        player_x += player_speed

    # Fill the screen with black
    screen.fill((0, 0, 0))

    # Draw the player
    pygame.draw.rect(screen, (0, 128, 255), (player_x, player_y, player_width, player_height))

    # Update the display
    pygame.display.flip()

    # Cap the frame rate at 60 FPS
    clock.tick(60)
Enter fullscreen mode Exit fullscreen mode

Here’s what’s going on:

  1. Player Settings: We define the player’s size (player_width and player_height), starting position (player_x and player_y), and speed (player_speed). The starting position is calculated so that the player appears centered horizontally near the bottom of the window.

  2. Handling Player Movement: Inside the main game loop, we check which keys are pressed using pygame.key.get_pressed(). This function returns a list of all keys on the keyboard, with a True value for the keys that are currently pressed. If the left arrow key is pressed, and the player isn’t at the edge of the screen, we move the player to the left by subtracting player_speed from player_x. Similarly, we move the player to the right if the right arrow key is pressed.

  3. Drawing the Player: The pygame.draw.rect() function draws a rectangle (our player) on the screen. The parameters are the screen to draw on, the color of the rectangle (a shade of blue in this case), and the rectangle’s position and size.

When you run this code, you’ll see a blue rectangle that you can move left and right using the arrow keys. This rectangle is our player, and it will be the hero of our game.

Step 3: Shooting Bullets

What’s a shooter game without some shooting? Let’s add the ability to fire bullets. We’ll create a bullet each time the player presses the space bar.

# Bullet settings
bullet_width = 5
bullet_height = 10
bullet_speed = 7
bullets = []

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                # Create a bullet at the current player position
                bullet_x = player_x + player_width // 2 - bullet_width // 2
                bullet_y = player_y
                bullets.append(pygame.Rect(bullet_x, bullet_y, bullet_width, bullet_height))

    # Handle player movement
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_x > 0:
        player_x -= player_speed
    if keys[pygame.K_RIGHT] and player_x < screen_width - player_width:
        player_x += player_speed

    # Update bullet positions
    for bullet in bullets:
        bullet.y -= bullet_speed

    # Remove bullets that are off the screen
    bullets = [bullet for bullet in bullets if bullet.y > 0]

    # Fill the screen with black
    screen.fill((0, 0, 0))

    # Draw the player
    pygame.draw.rect(screen, (0, 128, 255), (player_x, player_y, player_width, player_height))

    # Draw the bullets
    for bullet in bullets:
        pygame.draw.rect(screen, (255, 255, 255), bullet)

    # Update the display
    pygame.display.flip()

    # Cap the frame rate at 60 FPS
    clock.tick(60)
Enter fullscreen mode Exit fullscreen mode

Let’s break this down:

  1. Bullet Settings: We define the bullet’s size (bullet_width and bullet_height), speed (bullet_speed), and a list (bullets) to keep track of all active bullets.

  2. Firing Bullets: Inside the main loop, we check for a KEYDOWN event, which occurs when any key is pressed. If the space bar (pygame.K_SPACE) is pressed, we create a new bullet at the player’s current position. The bullet’s x-position is calculated to be centered horizontally with the player, and the bullet is then added to the bullets list.

  3. Updating Bullet Positions: Each bullet in the bullets list is moved upwards by subtracting bullet_speed from its y-position. Bullets that move off the top of the screen are removed from the list to save memory.

  4. Drawing Bullets: We loop through the bullets list and use pygame.draw.rect() to draw each bullet on the screen.

Now, when you run the game, pressing the space bar will shoot white bullets from the player’s position. The bullets move upward, just like you’d expect in a shooter game.

Step 4: Adding Enemies

Let’s make the game more challenging by adding enemies that the player needs to shoot. We’ll start by creating some enemies that move down the screen toward the player. Again, we’ll keep things simple by representing the enemies as red rectangles.

import random

# Enemy settings
enemy_width = 50
enemy_height = 60
enemy_speed = 2
enemies = []

# Spawn an enemy every 2 seconds
enemy_timer = 0
enemy_spawn_time = 2000

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                bullet_x = player_x + player_width // 2 - bullet_width // 2
                bullet_y = player_y
                bullets.append(pygame.Rect(bullet_x, bullet_y, bullet_width, bullet_height))

    # Handle player movement
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_x > 0:
        player_x -= player_speed
    if keys[pygame.K_RIGHT] and player_x < screen_width - player_width:
        player_x += player_speed

    # Update bullet positions
    for bullet in bullets:
        bullet.y -= bullet_speed
    bullets = [bullet for bullet in bullets if bullet.y > 0]

    # Update enemy positions and spawn new ones
    current_time = pygame.time.get_ticks()
    if current_time - enemy_timer > enemy_spawn_time:
        enemy_x = random.randint(0, screen_width - enemy_width)
        enemy_y = -enemy_height
        enemies.append(pygame.Rect(enemy_x, enemy_y, enemy_width, enemy_height))
        enemy_timer = current_time

    for enemy in enemies:
        enemy.y += enemy_speed

    # Remove enemies that are off the screen
    enemies = [enemy for enemy in enemies if enemy.y < screen_height]

    # Fill the screen with black
    screen.fill((0, 0, 0))

    # Draw the player
    pygame.draw.rect(screen, (0, 128, 255), (player_x, player_y, player_width, player_height))

    # Draw the bullets
    for bullet in bullets:
        pygame.draw.rect(screen, (255, 255, 255), bullet)

    # Draw the enemies
    for enemy in enemies:
        pygame.draw.rect(screen, (255, 0, 0), enemy)

    # Update the display
    pygame.display.flip()

    # Cap the frame rate at 60 FPS
    clock.tick(60)
Enter fullscreen mode Exit fullscreen mode

Here’s how we’ve added enemies:

  1. Enemy Settings: We define the size (enemy_width and enemy_height), speed (enemy_speed), and a list (enemies) to track all active enemies.

  2. Spawning Enemies: We use a timer to spawn a new enemy every 2 seconds. The current time is tracked with pygame.time.get_ticks(). If enough time has passed since the last enemy was spawned, we create a new enemy at a random horizontal position above the screen (so it moves downward). This enemy is then added to the enemies list.

  3. Updating Enemy Positions: Each enemy in the enemies list moves downward by adding enemy_speed to its y-position. If an enemy moves off the bottom of the screen, it’s removed from the list.

  4. Drawing Enemies: We loop through the enemies list and use pygame.draw.rect() to draw each enemy on the screen.

When you run this code, you’ll see red rectangles (our enemies) falling from the top of the screen. The game is starting to take shape!

Step 5: Detecting Collisions

Now, let’s add some logic so that when a bullet hits an enemy, both the bullet and the enemy disappear. This involves detecting collisions between bullets and enemies.

# Collision detection function
def check_collision(rect1, rect2):
    return rect1.colliderect(rect2)

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                bullet_x = player_x + player_width // 2 - bullet_width // 2
                bullet_y = player_y
                bullets.append(pygame.Rect(bullet_x, bullet_y, bullet_width, bullet_height))

    # Handle player movement
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_x > 0:
        player_x -= player_speed
    if keys[pygame.K_RIGHT] and player_x < screen_width - player_width:
        player_x += player_speed

    # Update bullet positions
    for bullet in bullets:
        bullet.y -= bullet_speed
    bullets = [bullet for bullet in bullets if bullet.y > 0]

    # Update enemy positions and spawn new ones
    current_time = pygame.time.get_ticks()
    if current_time - enemy_timer > enemy_spawn_time:
        enemy_x = random.randint(0, screen_width - enemy_width)
        enemy_y = -enemy_height
        enemies.append(pygame.Rect(enemy_x, enemy_y, enemy_width, enemy_height))
        enemy_timer = current_time

    for enemy in enemies:
        enemy.y += enemy_speed

    # Check for collisions
    for bullet in bullets[:]:
        for enemy in enemies[:]:
            if check_collision(bullet, enemy):
                bullets.remove(bullet)
                enemies.remove(enemy)
                break

    # Remove enemies that are off the screen
    enemies = [enemy for enemy in enemies if enemy.y < screen_height]

    # Fill the screen with black
    screen.fill((0, 0, 0))

    # Draw the player
    pygame.draw.rect(screen, (0, 128, 255), (player_x, player_y, player_width, player_height))

    # Draw the bullets
    for bullet in bullets:
        pygame.draw.rect(screen, (255, 255, 255), bullet)

    # Draw the enemies
    for enemy in enemies:
        pygame.draw.rect(screen, (255, 0, 0), enemy)

    # Update the display
    pygame.display.flip()

    # Cap the frame rate at 60 FPS
    clock.tick(60)
Enter fullscreen mode Exit fullscreen mode

Here’s what we did:

  1. Collision Detection: We define a function check_collision that takes the positions and sizes of two rectangles and checks if they overlap using colliderect(). This is how we detect if a bullet has hit an enemy.

  2. Removing Colliding Objects: Inside the main loop, after updating the positions of the bullets and enemies, we check if any bullet has collided with any enemy. If they have, both the bullet and the enemy are removed from their respective lists.

Now, when you run the game, bullets that hit enemies will make the enemies disappear. You’ve created a basic but functioning shooter game!

Important Note: In this simple game, there is no penalty for colliding with an enemy. The player can move through enemies without taking damage or losing the game. This keeps things straightforward but might be something you want to change in a more advanced version.

Putting it all together

In case you need it, here's everything we wrote:

import pygame
import sys
import random

# Initialize PyGame
pygame.init()

# Set up the game window
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Simple Shooter Game")

# Set the frame rate
clock = pygame.time.Clock()

# Player settings
player_width = 50
player_height = 60
player_x = screen_width // 2 - player_width // 2
player_y = screen_height - player_height - 10
player_speed = 5

# Bullet settings
bullet_width = 5
bullet_height = 10
bullet_speed = 7
bullets = []

# Enemy settings
enemy_width = 50
enemy_height = 60
enemy_speed = 2
enemies = []

# Spawn an enemy every 2 seconds
enemy_timer = 0
enemy_spawn_time = 2000

# Collision detection function
def check_collision(rect1, rect2):
    return pygame.Rect(rect1).colliderect(pygame.Rect(rect2))

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                # Create a bullet at the current player position
                bullet_x = player_x + player_width // 2 - bullet_width // 2
                bullet_y = player_y
                bullets.append([bullet_x, bullet_y])

    # Handle player movement
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT] and player_x > 0:
        player_x -= player_speed
    if keys[pygame.K_RIGHT] and player_x < screen_width - player_width:
        player_x += player_speed

    # Update bullet positions
    for bullet in bullets:
        bullet[1] -= bullet_speed
    bullets = [bullet for bullet in bullets if bullet[1] > 0]

    # Update enemy positions and spawn new ones
    current_time = pygame.time.get_ticks()
    if current_time - enemy_timer > enemy_spawn_time:
        enemy_x = random.randint(0, screen_width - enemy_width)
        enemy_y = -enemy_height
        enemies.append([enemy_x, enemy_y])
        enemy_timer = current_time

    for enemy in enemies:
        enemy[1] += enemy_speed

    # Check for collisions
    for bullet in bullets[:]:
        for enemy in enemies[:]:
            if check_collision((bullet[0], bullet[1], bullet_width, bullet_height),
                               (enemy[0], enemy[1], enemy_width, enemy_height)):
                bullets.remove(bullet)
                enemies.remove(enemy)
                break

    # Remove enemies that are off the screen
    enemies = [enemy for enemy in enemies if enemy[1] < screen_height]

    # Fill the screen with black
    screen.fill((0, 0, 0))

    # Draw the player
    pygame.draw.rect(screen, (0, 128, 255), (player_x, player_y, player_width, player_height))

    # Draw the bullets
    for bullet in bullets:
        pygame.draw.rect(screen, (255, 255, 255), (bullet[0], bullet[1], bullet_width, bullet_height))

    # Draw the enemies
    for enemy in enemies:
        pygame.draw.rect(screen, (255, 0, 0), (enemy[0], enemy[1], enemy_width, enemy_height))

    # Update the display
    pygame.display.flip()

    # Cap the frame rate at 60 FPS
    clock.tick(60)
Enter fullscreen mode Exit fullscreen mode

What's Next?

Congratulations, you’ve just built your first simple shooter game with PyGame! But this is just the beginning—there’s so much more you can do:

  • Add a Scoring System: Track how many enemies the player destroys and display the score on the screen.
  • Create Different Enemy Types: Make enemies that move differently, shoot back, or take multiple hits to destroy.
  • Enhance Graphics: Replace the rectangles with images for the player, bullets, and enemies.
  • Add Sound Effects: Make the game more immersive by adding sounds for shooting, hitting enemies, and other actions.
  • Introduce Levels: Add different levels or waves of enemies to increase the difficulty as the player progresses.
  • Add Player Health and Damage: Allow the player to take damage when colliding with an enemy and lose the game if their health reaches zero.

PyGame is incredibly flexible, so let your imagination run wild and keep experimenting. The more you play with the code, the more you’ll learn and the better your game will become.

It's a wrap!

And that’s it! You’ve gone from an empty window to a functioning shooter game in just a few steps. Whether you’re planning to expand this project or move on to something new, you’ve taken a big step in your game development journey. Don’t hesitate to share your progress or ask questions—I'm here to help!

Do you have any questions or comments? Be sure to leave them here or contact me at @lovelacecoding on most social media platforms. Thanks for coding along!

Top comments (2)

Collapse
 
sreno77 profile image
Scott Reno

Can you post a screenshot of the game?

Collapse
 
martinbaun profile image
Martin Baun

Good job! Very detailed, cant believe this doesn't have more likes!!