使用 Pygame 构建迷宫游戏

PythonBeginner
立即练习

This tutorial is from open-source community. Access the source code

介绍

在这个项目中,我们将使用 Python 中的 Pygame 库创建一个迷宫游戏。游戏要求玩家在迷宫中导航,收集食物并避开墙壁。我们将把开发过程分成多个步骤,以便于理解和跟进。

👀 预览

迷宫游戏预览截图

🎯 任务

在这个项目中,你将学习:

  • 如何使用 Pygame 设置游戏环境
  • 如何使用单元格和墙壁创建迷宫
  • 如何为玩家添加食物以供收集
  • 如何实现玩家移动和碰撞检测
  • 如何处理游戏逻辑,包括计分和游戏结束条件
  • 如何跟踪玩家记录
  • 如何在屏幕上显示游戏统计信息,如时间、分数和记录

🏆 成果

完成这个项目后,你将能够:

  • 使用 Pygame 库进行游戏开发
  • 应用面向对象编程概念创建游戏元素
  • 展示用于生成迷宫的算法思维和问题解决技巧
  • 处理事件处理和玩家输入
  • 在游戏环境中实现碰撞检测和移动机制
  • 管理文件处理以存储和检索游戏记录
  • 在屏幕上显示游戏统计信息和信息
这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 中级 级别的实验,完成率为 67%。获得了学习者 50% 的好评率。

设置环境

首先,我们将为迷宫游戏创建项目文件。

cd ~/project
touch maze.py
sudo pip install pygame

在这一步中,我们将设置 Pygame 环境并定义常量。

import pygame
from random import choice, randrange

## 屏幕尺寸和图块大小的常量
RES = WIDTH, HEIGHT = 1202, 902
TILE = 100
cols, rows = WIDTH // TILE, HEIGHT // TILE

## 其余代码将放在这里...

在这一步中:

  • 我们导入必要的库(Pygame 和 random)。
  • 我们定义屏幕尺寸和图块大小的常量。
  • 初始化 Pygame 并设置游戏窗口。
  • 我们加载游戏的背景图像。
✨ 查看解决方案并练习

创建单元格类

在这一步中,我们将定义 Cell 类来表示迷宫中的单元格。

## 定义一个类来表示迷宫中的单元格
class Cell:
    def __init__(self, x, y):
        self.x, self.y = x, y
        ## 墙壁表示单元格的边界
        self.walls = {"top": True, "right": True, "bottom": True, "left": True}
        self.visited = False
        self.thickness = 4

    ## 绘制单元格的墙壁
    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
            )

    ## 获取表示单元格每面墙的矩形
    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

    ## 检查是否存在相邻单元格
    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)]

    ## 获取未被访问的相邻单元格
    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

## 其余代码将放在这里...

在这一步中:

  • 我们定义了 Cell 类及其用于绘制墙壁和检查邻居的属性和方法。
✨ 查看解决方案并练习

移除墙壁并生成迷宫

在这一步中,我们将创建用于移除墙壁和生成迷宫的函数。

## 移除两个相邻单元格之间墙壁的函数
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


## 生成迷宫的函数
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

## 其余代码将放在这里...

在这一步中:

  • 我们定义了 remove_walls 函数来移除相邻单元格之间的墙壁。
  • 我们创建了 generate_maze 函数,使用深度优先搜索算法生成迷宫。
✨ 查看解决方案并练习

向游戏中添加食物

在这一步中,我们将创建一个 Food 类,以便向游戏中添加食物物品。

## 表示游戏中食物的类
class Food:
    def __init__(self):
        ## 加载食物图像
        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()

    ## 随机设置食物的位置
    def set_pos(self):
        self.rect.topleft = randrange(cols) * TILE + 5, randrange(rows) * TILE + 5

    ## 在屏幕上绘制食物
    def draw(self):
        game_surface.blit(self.img, self.rect)

## 其余代码将放在这里...

在这一步中:

  • 我们定义了 Food 类,该类具有设置食物位置和绘制食物物品的方法。
✨ 查看解决方案并练习

玩家移动与碰撞检测

在这一步中,我们将设置玩家控制、移动和碰撞检测。

## 检查玩家是否与墙壁发生碰撞
def is_collide(x, y):
    tmp_rect = player_rect.move(x, y)
    if tmp_rect.collidelist(walls_collide_list) == -1:
        return False
    return True

## 其余代码将放在这里...

在这一步中:

  • 我们定义了 is_collide 函数来检查玩家是否与墙壁发生碰撞。
✨ 查看解决方案并练习

游戏玩法与计分

在这一步中,我们将实现游戏玩法逻辑,包括吃食物和计分。

## 检查玩家是否吃到了任何食物
def eat_food():
    for food in food_list:
        if player_rect.collidepoint(food.rect.center):
            food.set_pos()
            return True
    return False


## 检查游戏是否结束(时间用完)
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

## 其余代码将放在这里...

在这一步中:

  • 我们定义了 eat_food 函数来检查玩家是否吃到了任何食物。
  • 我们创建了 is_game_over 函数来检查当时间用完时游戏是否结束。
✨ 查看解决方案并练习

处理记录

在这一步中,我们将为游戏实现记录保存功能。

## 从文件中获取当前记录的函数
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"

## 在文件中设置并更新记录的函数
def set_record(record, score):
    rec = max(int(record), score)
    with open("record", "w") as f:
        f.write(str(rec))

## 其余代码将放在这里...

在这一步中:

  • 我们定义了从文件中检索当前记录并对其进行更新的函数。
✨ 查看解决方案并练习

游戏初始化

在这一步中,我们将执行游戏初始化任务。

## 初始化 Pygame 并设置游戏窗口
FPS = 60
pygame.init()
game_surface = pygame.Surface(RES)
surface = pygame.display.set_mode((WIDTH + 300, HEIGHT))
clock = pygame.time.Clock()

## 加载背景图像
bg_game = pygame.image.load("img/bg_1.jpg").convert()
bg = pygame.image.load("img/bg_main.jpg").convert()

## 生成迷宫
maze = generate_maze()

## 玩家设置
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_list = [Food() for i in range(3)]

## 创建一个表示墙壁的矩形列表,用于碰撞检测
walls_collide_list = sum([cell.get_rects() for cell in maze], [])

## 定时器、分数和记录
pygame.time.set_timer(pygame.USEREVENT, 1000)
time = 60
score = 0
record = get_record()

## 字体
font = pygame.font.SysFont("Impact", 150)
text_font = pygame.font.SysFont("Impact", 80)

## 其余代码将放在这里...

在这一步中:

  • 我们执行各种初始化任务,包括设置 Pygame、加载图像、生成迷宫以及初始化与玩家和食物相关的变量。
✨ 查看解决方案并练习

主游戏循环

在这一步中,我们将设置主游戏循环并显示游戏元素。

## 主游戏循环
while True:
    ## 绘制背景图像
    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

    ## 处理玩家控制和移动
    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)

    ## 绘制迷宫
    [cell.draw(game_surface) for cell in maze]

    ## 游戏玩法:检查玩家是否吃到食物以及游戏是否结束
    if eat_food():
        FPS += 10
        score += 1
    is_game_over()

    ## 绘制玩家
    game_surface.blit(player_img, player_rect)

    ## 绘制食物物品
    [food.draw() for food in food_list]

    ## 其余代码将放在这里...

在这一步中:

  • 我们设置了主游戏循环,该循环处理事件、玩家移动和游戏渲染。
✨ 查看解决方案并练习

显示游戏统计信息

在这一步中,我们将在屏幕上显示游戏统计信息。

    ## 绘制游戏统计信息
    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)

在这一步中:

  • 我们使用字体来显示与游戏相关的信息,如时间、分数和记录。
  • 我们使用 blit() 方法在屏幕上绘制文本。
  • 我们使用 flip() 方法更新显示。
✨ 查看解决方案并练习

运行游戏

既然我们已经完成了所有步骤,我们可以使用以下命令运行迷宫游戏:

cd ~/project
python maze.py
迷宫游戏执行截图
✨ 查看解决方案并练习

总结

在这个项目中,我们将使用 Pygame 构建迷宫游戏的过程分解为十个清晰且易于管理的步骤。你将学习如何设置游戏环境、创建迷宫单元格、生成迷宫、处理玩家移动和碰撞检测、实现游戏玩法和计分、管理记录等等。通过遵循这些步骤,你将能够用 Python 创建一个功能齐全的迷宫游戏。