Be the first user to complete this post

  • 0
Add to List
Medium

557. How to Build a Snake Game in Python Using Pygame

In this tutorial, we’ll walk through how to build a classic Snake Game in Python using the pygame library. We’ll start by explaining each component of the game and how they work together to create an engaging, interactive experience.

Overview of the Game

The Snake Game is a well-known game where the player controls a snake that moves around a grid to eat food (in this case, apples). The snake grows longer each time it eats an apple, and the player must avoid colliding with the snake’s own body or the walls.

Key Features:

  • Snake Movement: The snake moves automatically in one direction, controlled by the arrow keys.
  • Apple: Randomly placed on the screen, and the snake must "eat" the apple by moving over it.
  • Collision Detection: The game checks if the snake collides with its body or edges.
  • Score and High Score: A score that increments when the snake eats an apple, and a high score stored in a file.
  • Speed Control: The player can control the speed of the snake by pressing numeric keys (1 to 5).

Building a classic Snake game in Python is a fun and educational way to learn about game development and object-oriented programming. In this tutorial, we’ll walk you through creating a fully functional Snake game using Pygame, a popular game development library in Python. The game will feature basic elements such as the snake, food (apples), score tracking, and collision detection.

By the end, you’ll understand how to build and run the game, and you’ll have a working Snake game to play and enhance.

Click here to jump to YouTube tutorial video

Table of Contents:

  1. Introduction to Pygame
  2. Setting Up the Game
  3. Understanding the Snake Class
  4. Creating the Apple Class
  5. Managing Game State and Score
  6. Handling Collisions and Game Over
  7. Running the Game Loop
  8. Youtube Tutorial Video
  9. Complete Code
  10. Conclusion

Setting Up Pygame

First, we need to install the pygame library, which is a popular module for game development in Python.

import pygame
import random
import os
import json

SCREEN_SIZE = 800
BLOCK_WIDTH = 40

Here, SCREEN_SIZE is the size of the game window, and BLOCK_WIDTH represents the size of each segment of the snake and the apple.

Snake Class

The Snake class handles the snake’s movement, drawing, and growth mechanics. This is one of the core components of our game.

class Snake:
    def __init__(self, parent_screen, length=5):
        self.length = length
        self.parent_screen = parent_screen
        self.block = pygame.image.load("resources/block.jpg")  # Snake body block image
        self.x = [BLOCK_WIDTH] * self.length  # Initial X-coordinates of the snake body
        self.y = [BLOCK_WIDTH] * self.length  # Initial Y-coordinates of the snake body
        self.direction = "right"  # Snake's initial direction

Explanation of the properties:

  • parent_screen: The screen where the snake is drawn.
  • length: The initial length of the snake.
  • block: The image representing the snake’s body.
  • x, y: Lists that store the positions of the snake’s blocks.
  • direction: Tracks the snake's current movement direction.

    def draw(self):
        self.parent_screen.fill((0, 0, 0))  # Clear the screen (black background)
        for i in range(self.length):
            self.parent_screen.blit(self.block, (self.x[i], self.y[i]))  # Draw the snake


  • This method is called to render the snake on the screen.
  •  It clears the previous frame and redraws each segment of the snake using the coordinates stored in self.x and self.y.

Snake Movement

def move_left(self):
    if self.direction != 'right':
        self.direction = 'left'

def move_right(self):
    if self.direction != 'left':
        self.direction = 'right'

def move_up(self):
    if self.direction != 'down':
        self.direction = 'up'

def move_down(self):
    if self.direction != 'up':
        self.direction = 'down'

These functions ensure the snake can’t reverse direction immediately, which would cause instant death by collision.

Updating Snake Position

The snake moves by shifting the positions of its body parts. The head moves in the current direction, while each body part follows the one before it.

    def move(self):
        for i in range(self.length - 1, 0, -1):  # Shift the body parts forward
            self.x[i] = self.x[i - 1]
            self.y[i] = self.y[i - 1]

        if self.direction == 'right':
            self.x[0] += BLOCK_WIDTH
        elif self.direction == 'left':
            self.x[0] -= BLOCK_WIDTH
        elif self.direction == 'up':
            self.y[0] -= BLOCK_WIDTH
        elif self.direction == 'down':
            self.y[0] += BLOCK_WIDTH

        # Wrap around the screen (appear on the other side)
        if self.x[0] >= SCREEN_SIZE:
            self.x[0] = 0
        elif self.x[0] < 0:
            self.x[0] = SCREEN_SIZE
        if self.y[0] >= SCREEN_SIZE:
            self.y[0] = 0
        elif self.y[0] < 0:
            self.y[0] = SCREEN_SIZE

        self.draw()

  • Purpose: This method updates the snake’s position based on its current direction.
  • How it works:
    • The snake’s body shifts forward, with each block taking the position of the block ahead of it.
    • The head (self.x[0] and self.y[0]) moves in the direction specified by self.direction.
    • The method also ensures that the snake wraps around the screen when it reaches the edges.

Apple Class

The Apple class manages the apple (food) that the snake eats. It randomly positions the apple on the game grid, ensuring it doesn't overlap the snake’s body.

Placing the Apple

class Apple:
    class Apple:
    def __init__(self, parent_screen):
        self.parent_screen = parent_screen
        self.apple_img = pygame.image.load("resources/apple.jpg")  # Apple image
        self.x = BLOCK_WIDTH * 4
        self.y = BLOCK_WIDTH * 5

    def draw(self):
        self.parent_screen.blit(self.apple_img, (self.x, self.y))  # Draw apple

The apple image is loaded, and it’s initially placed at a random position on the grid.

Moving the Apple

Repositions the apple randomly on the grid while ensuring it doesn’t overlap with the snake’s body.

def move(self, snake):
    while True:
        x = random.randint(0, 19) * BLOCK_WIDTH
        y = random.randint(0, 19) * BLOCK_WIDTH
        clean = True
        for i in range(0, snake.length):
            if x == snake.x[i] and y == snake.y[i]:
                clean = False
                break
        if clean:
            self.x = x
            self.y = y
            return

Game Class

The Game class handles the main game loop, including score tracking, controlling speed, and saving high scores.

Initialization

class Game:
    def __init__(self):
        pygame.init()
        pygame.display.set_caption("Snake Game - PyGame")
        self.SCREEN_UPDATE = pygame.USEREVENT
        self.timer = 150
        pygame.time.set_timer(self.SCREEN_UPDATE, self.timer)
        self.surface = pygame.display.set_mode((SCREEN_SIZE, SCREEN_SIZE))
        self.snake = Snake(self.surface)
        self.apple = Apple(parent_screen=self.surface)
        self.score = 0
        self.record = 00
        self.retrieve_data()

This initializes the game, sets up the window, and retrieves the high score from a file.

Save and Retrieve Data

Saves the current high score to a JSON file and Loads the high score from a file so it can be displayed in-game.


    def save_data(self):
        data_folder_path = "./resources"
        file_name = "data.json"
        if not os.path.exists(data_folder_path):
            os.makedirs(data_folder_path)

        complete_path = os.path.join(data_folder_path, file_name)
        data = {'record': self.record}
        with open(complete_path, 'w') as file:
            json.dump(data, file, indent=4)

    def retrieve_data(self):
        data_folder_path = os.path.join("./resources", "data.json")
        if os.path.exists(data_folder_path):
            with open(data_folder_path, 'r') as file:
                data = json.load(file)

            if data is not None:
                self.record = data['record']

Speed Control

The game supports 5 different speed levels, which can be controlled by pressing keys 1-5. Each number corresponds to a different speed, changing the timer interval.

def run(self):
    running = True
    pause = False
    while running:
        for event in pygame.event.get():
            if event.type == KEYDOWN:
                if event.key == K_1:
                    self.timer = 10
                if event.key == K_2:
                    self.timer = 50
                if event.key == K_3:
                    self.timer = 100
                if event.key == K_4:
                    self.timer = 150
                if event.key == K_5:
                    self.timer = 200
                ...
            elif event.type == self.SCREEN_UPDATE:
                if not pause:
                    self.play()

Scoring System

The game tracks the player’s score and updates the high score (record) whenever a new high score is reached:

def play(self):
    self.snake.move()
    self.apple.draw()
    self.display_score()

    if self.snake.x[0] == self.apple.x and self.snake.y[0] == self.apple.y:
        self.score += 1
        self.snake.increase()
        self.apple.move(self.snake)
        if self.record < self.score:
            self.record = self.score
            self.save_data()

Display Score

  • Displays the current score and high score on the game screen.
  • The method renders the score and record as text using Pygame’s SysFont and draws it at the top-left corner of the screen.
 def display_score(self):
        font = pygame.font.SysFont('arial', 30)
        score_text = f"Score: {self.score} Record: {self.record}"
        score_render = font.render(score_text, True, (255, 255, 255))
        self.surface.blit(score_render, (10, 10))

Handling Collisions and Game Over

Collision detection is crucial in Snake games. If the snake collides with its own body, the game ends.


for i in range(1, self.snake.length):
    if self.snake.x[0] == self.snake.x[i] and self.snake.y[0] == self.snake.y[i]:
        raise Exception("Collision Occurred")

If a collision is detected, the game throws an exception, signaling the game over.

Game Over

The game ends when the snake collides with its own body. The show_game_over method displays the final score and prompts the user to restart:

def show_game_over(self):
    font = pygame.font.SysFont('arial', 30)
    line = font.render(f"Game over! score is {self.score}", True, (255, 255, 255))
    self.surface.blit(line, (200, 300))
    line1 = font.render(f"Press Enter to Restart", True, (255, 255, 255))
    self.surface.blit(line1, (200, 350))
    pygame.display.update()
    pygame.mixer.music.pause()

Running the Game Loop

Finally, the run method contains the game loop that keeps the game running until the player quits or the snake collides with itself.


def run(self):
    running = True
    pause = False
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    pause = False

        if not pause:
            try:
                self.play()
            except Exception:
                self.show_game_over()
                pause = True

        pygame.display.update()
  • Manages the overall game loop and listens for user input (such as pausing and restarting the game).
  • It continuously checks for events like quitting the game or pressing keys. If the game is paused, the loop halts until the player resumes by pressing the Enter key. The game updates the screen in each iteration of the loop using pygame.display.update().

Complete Code

 - GitHub

Conclusion

You now have a working Snake game built using Python and Pygame! This project covered the essentials of game development, including object-oriented design, rendering graphics, handling user input, managing game state, and collision detection.

Feel free to expand this game by adding more features like sound effects, levels, or different types of food. Have fun coding!