Introduction
In this project, we will create a maze game using the Pygame library in Python. The game involves navigating a player through a maze to collect food items while avoiding walls. We will split the development process into multiple steps to make it easier to understand and follow along.
👀 Preview

🎯 Tasks
In this project, you will learn:
- How to set up the game environment using Pygame
- How to create the maze using cells and walls
- How to add food items for the player to collect
- How to implement player movement and collision detection
- How to handle game logic, including scoring and game over conditions
- How to keep track of the player's record
- How to display game statistics such as time, score, and record on the screen
🏆 Achievements
After completing this project, you will be able to:
- Use the Pygame library for game development
- Apply object-oriented programming concepts to create game elements
- Demonstrate algorithmic thinking and problem-solving skills for maze generation
- Handle event processing and player input
- Implement collision detection and movement mechanics in a game environment
- Manage file handling for storing and retrieving game records
- Display game statistics and information on the screen
Setting up the Environment
First, we'll create the project files for the Maze game.
cd ~/project
touch maze.py
sudo pip install pygame
In this step, we'll set up the Pygame environment and define constants.
import pygame
from random import choice, randrange
## Constants for screen dimensions and tile size
RES = WIDTH, HEIGHT = 1202, 902
TILE = 100
cols, rows = WIDTH // TILE, HEIGHT // TILE
## The rest of your code will go here...
In this step:
- We import the necessary libraries (Pygame and random).
- We define constants for screen dimensions and tile size.
- Pygame is initialized, and the game window is set up.
- We load background images for the game.
Creating the Cell Class
In this step, we'll define the Cell class to represent maze cells.
## Define a class to represent cells in the maze
class Cell:
def __init__(self, x, y):
self.x, self.y = x, y
## Walls represent the boundaries of the cell
self.walls = {"top": True, "right": True, "bottom": True, "left": True}
self.visited = False
self.thickness = 4
## Draw the cell's walls
def draw(self, sc):
x, y = self.x * TILE, self.y * TILE
if self.walls["top"]:
pygame.draw.line(
sc, pygame.Color("darkorange"), (x, y), (x + TILE, y), self.thickness
)
if self.walls["right"]:
pygame.draw.line(
sc,
pygame.Color("darkorange"),
(x + TILE, y),
(x + TILE, y + TILE),
self.thickness,
)
if self.walls["bottom"]:
pygame.draw.line(
sc,
pygame.Color("darkorange"),
(x + TILE, y + TILE),
(x, y + TILE),
self.thickness,
)
if self.walls["left"]:
pygame.draw.line(
sc, pygame.Color("darkorange"), (x, y + TILE), (x, y), self.thickness
)
## Get the rectangles representing each wall of the cell
def get_rects(self):
rects = []
x, y = self.x * TILE, self.y * TILE
if self.walls["top"]:
rects.append(pygame.Rect((x, y), (TILE, self.thickness)))
if self.walls["right"]:
rects.append(pygame.Rect((x + TILE, y), (self.thickness, TILE)))
if self.walls["bottom"]:
rects.append(pygame.Rect((x, y + TILE), (TILE, self.thickness)))
if self.walls["left"]:
rects.append(pygame.Rect((x, y), (self.thickness, TILE)))
return rects
## Check if a neighboring cell exists
def check_cell(self, x, y):
find_index = lambda x, y: x + y * cols
if x < 0 or x > cols - 1 or y < 0 or y > rows - 1:
return False
return self.grid_cells[find_index(x, y)]
## Get neighboring cells that have not been visited
def check_neighbors(self, grid_cells):
self.grid_cells = grid_cells
neighbors = []
top = self.check_cell(self.x, self.y - 1)
right = self.check_cell(self.x + 1, self.y)
bottom = self.check_cell(self.x, self.y + 1)
left = self.check_cell(self.x - 1, self.y)
if top and not top.visited:
neighbors.append(top)
if right and not right.visited:
neighbors.append(right)
if bottom and not bottom.visited:
neighbors.append(bottom)
if left and not left.visited:
neighbors.append(left)
return choice(neighbors) if neighbors else False
## The rest of your code will go here...
In this step:
- We define the
Cellclass with its properties and methods for drawing walls and checking neighbors.
Removing Walls and Generating the Maze
In this step, we'll create functions to remove walls and generate the maze.
## Function to remove walls between two adjacent cells
def remove_walls(current, next):
dx = current.x - next.x
if dx == 1:
current.walls["left"] = False
next.walls["right"] = False
elif dx == -1:
current.walls["right"] = False
next.walls["left"] = False
dy = current.y - next.y
if dy == 1:
current.walls["top"] = False
next.walls["bottom"] = False
elif dy == -1:
current.walls["bottom"] = False
next.walls["top"] = False
## Function to generate the maze
def generate_maze():
grid_cells = [Cell(col, row) for row in range(rows) for col in range(cols)]
current_cell = grid_cells[0]
array = []
break_count = 1
while break_count != len(grid_cells):
current_cell.visited = True
next_cell = current_cell.check_neighbors(grid_cells)
if next_cell:
next_cell.visited = True
break_count += 1
array.append(current_cell)
remove_walls(current_cell, next_cell)
current_cell = next_cell
elif array:
current_cell = array.pop()
return grid_cells
## The rest of your code will go here...
In this step:
- We define the
remove_wallsfunction to remove walls between adjacent cells. - We create the
generate_mazefunction to generate the maze using a depth-first search algorithm.
Adding Food to the Game
In this step, we'll create a Food class to add food items to the game.
## Class to represent food in the game
class Food:
def __init__(self):
## Load the food image
self.img = pygame.image.load("img/food.png").convert_alpha()
self.img = pygame.transform.scale(self.img, (TILE - 10, TILE - 10))
self.rect = self.img.get_rect()
self.set_pos()
## Set the position of the food randomly
def set_pos(self):
self.rect.topleft = randrange(cols) * TILE + 5, randrange(rows) * TILE + 5
## Draw the food on the screen
def draw(self):
game_surface.blit(self.img, self.rect)
## The rest of your code will go here...
In this step:
- We define the
Foodclass with methods for setting the position and drawing food items.
Player Movement and Collision Detection
In this step, we'll set up player controls, movement, and collision detection.
## Check if the player collides with walls
def is_collide(x, y):
tmp_rect = player_rect.move(x, y)
if tmp_rect.collidelist(walls_collide_list) == -1:
return False
return True
## The rest of your code will go here...
In this step:
- We define the
is_collidefunction to check if the player collides with walls.
Gameplay and Scoring
In this step, we'll implement gameplay logic, including eating food and scoring.
## Check if the player has eaten any food
def eat_food():
for food in food_list:
if player_rect.collidepoint(food.rect.center):
food.set_pos()
return True
return False
## Check if the game is over (time runs out)
def is_game_over():
global time, score, record, FPS
if time < 0:
pygame.time.wait(700)
player_rect.center = TILE // 2, TILE // 2
[food.set_pos() for food in food_list]
set_record(record, score)
record = get_record()
time, score, FPS = 60, 0, 60
## The rest of your code will go here...
In this step:
- We define the
eat_foodfunction to check if the player has eaten any food. - We create the
is_game_overfunction to check if the game is over when time runs out.
Handling Records
In this step, we'll implement record keeping for the game.
## Function to get the current record from a file
def get_record():
try:
with open("record") as f:
return f.readline()
except FileNotFoundError:
with open("record", "w") as f:
f.write("0")
return "0"
## Function to set and update the record in a file
def set_record(record, score):
rec = max(int(record), score)
with open("record", "w") as f:
f.write(str(rec))
## The rest of your code will go here...
In this step:
- We define functions to retrieve the current record from a file and update it.
Game Initialization
In this step, we'll perform game initialization tasks.
## Initialize Pygame and set up the game window
FPS = 60
pygame.init()
game_surface = pygame.Surface(RES)
surface = pygame.display.set_mode((WIDTH + 300, HEIGHT))
clock = pygame.time.Clock()
## Load background images
bg_game = pygame.image.load("img/bg_1.jpg").convert()
bg = pygame.image.load("img/bg_main.jpg").convert()
## Generate the maze
maze = generate_maze()
## Player settings
player_speed = 5
player_img = pygame.image.load("img/0.png").convert_alpha()
player_img = pygame.transform.scale(
player_img, (TILE - 2 * maze[0].thickness, TILE - 2 * maze[0].thickness)
)
player_rect = player_img.get_rect()
player_rect.center = TILE // 2, TILE // 2
directions = {
"a": (-player_speed, 0),
"d": (player_speed, 0),
"w": (0, -player_speed),
"s": (0, player_speed),
}
keys = {"a": pygame.K_LEFT, "d": pygame.K_RIGHT, "w": pygame.K_UP, "s": pygame.K_DOWN}
direction = (0, 0)
## Food settings
food_list = [Food() for i in range(3)]
## Create a list of rectangles representing walls for collision detection
walls_collide_list = sum([cell.get_rects() for cell in maze], [])
## Timer, score, and record
pygame.time.set_timer(pygame.USEREVENT, 1000)
time = 60
score = 0
record = get_record()
## Fonts
font = pygame.font.SysFont("Impact", 150)
text_font = pygame.font.SysFont("Impact", 80)
## The rest of your code will go here...
In this step:
- We perform various initialization tasks, including setting up Pygame, loading images, generating the maze, and initializing player and food-related variables.
Main Game Loop
In this step, we'll set up the main game loop and display game elements.
## Main game loop
while True:
## Blit background images
surface.blit(bg, (WIDTH, 0))
surface.blit(game_surface, (0, 0))
game_surface.blit(bg_game, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
if event.type == pygame.USEREVENT:
time -= 1
## Handle player controls and movement
pressed_key = pygame.key.get_pressed()
for key, key_value in keys.items():
if pressed_key[key_value] and not is_collide(*directions[key]):
direction = directions[key]
break
if not is_collide(*direction):
player_rect.move_ip(direction)
## Draw the maze
[cell.draw(game_surface) for cell in maze]
## Gameplay: Check if the player has eaten food and if the game is over
if eat_food():
FPS += 10
score += 1
is_game_over()
## Draw the player
game_surface.blit(player_img, player_rect)
## Draw food items
[food.draw() for food in food_list]
## The rest of your code will go here...
In this step:
- We set up the main game loop that handles events, player movement, and game rendering.
Displaying Game Statistics
In this step, we'll display game statistics on the screen.
## Draw game statistics
surface.blit(
text_font.render("TIME", True, pygame.Color("cyan"), True), (WIDTH + 70, 30)
)
surface.blit(font.render(f"{time}", True, pygame.Color("cyan")), (WIDTH + 70, 130))
surface.blit(
text_font.render("score:", True, pygame.Color("forestgreen"), True),
(WIDTH + 50, 350),
)
surface.blit(
font.render(f"{score}", True, pygame.Color("forestgreen")), (WIDTH + 70, 430)
)
surface.blit(
text_font.render("record:", True, pygame.Color("magenta"), True),
(WIDTH + 30, 620),
)
surface.blit(
font.render(f"{record}", True, pygame.Color("magenta")), (WIDTH + 70, 700)
)
pygame.display.flip()
clock.tick(FPS)
In this step:
- We use fonts to display game-related information such as time, score, and the record.
- We use the
blit()method to draw the text on the screen. - We use the
flip()method to update the display.
Run the Game
Now that we've completed all the steps, we can run the Maze game using the following command:
cd ~/project
python maze.py

Summary
In this project, we've split the process of building a maze game using Pygame into ten clear and manageable steps. You'll learn how to set up the game environment, create maze cells, generate the maze, handle player movement and collision detection, implement gameplay and scoring, manage records, and more. By following these steps, you'll be able to create a fully functional maze game in Python.



