You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame随机迷宫中Sprite与非Sprite墙体的碰撞检测实现问题

迷宫游戏墙体碰撞检测解决方案

问题描述

我正在开发一款带有自动寻路追击敌人的随机生成迷宫游戏,目前遇到玩家可穿透迷宫墙体移动的问题:由于迷宫墙体并非Sprite对象,无法直接调用sprite collide或Rect collide接口实现碰撞检测,请问有其他方案可以阻止玩家穿模移动吗?我目前能想到的唯一方案是记录每面墙的坐标范围,禁止玩家移动到对应的x、y坐标区间。
预期实现效果可参考下图:
实际效果与预期效果示例

现有迷宫实现代码

import pygame
import random

class Cell(object):
    def __init__(self, x, y, cell_size, screen, black, white, red, blue):
        # 矩阵内坐标
        self.x = x
        self.y = y
        # 存储四面墙的可见状态:上、下、右、左
        self.walls = [True, True, True, True]
        # 生成阶段是否被访问过
        self.generated = False
        # 寻路阶段是否在路径上
        self.on_path = False
        # 寻路阶段是否被访问过
        self.visited = False
        self.cell_size = cell_size
        self.screen = screen
        self.black = black
        self.white = white
        self.red = red
        self.blue = blue

    def draw_cell(self):
        # 屏幕上的实际像素坐标
        x = self.x * self.cell_size
        y = self.y * self.cell_size
        # 存在的墙就绘制出来
        if self.walls[0]:
            pygame.draw.line(self.screen, self.black, (x, y), (x + self.cell_size, y), 5)
        if self.walls[1]:
            pygame.draw.line(self.screen, self.black,
                             (x, y + self.cell_size), (x + self.cell_size, y + self.cell_size), 5)
        if self.walls[2]:
            pygame.draw.line(self.screen, self.black,
                             (x + self.cell_size, y), (x + self.cell_size, y + self.cell_size), 5)
        if self.walls[3]:
            pygame.draw.line(self.screen, self.black, (x, y), (x, y + self.cell_size), 5)
        # 生成过的单元格填充白色底色
        if self.generated:
            pygame.draw.rect(self.screen, self.white, (x, y, self.cell_size, self.cell_size))


class Maze():
    def __init__(self, screen, cell_size, rows, cols, white, black, red, blue):
        self.screen = screen
        self.cell_size = cell_size
        self.rows = rows
        self.cols = cols
        self.state = None
        self.maze = []
        self.stack = []
        self.current_x = 0
        self.current_y = 0
        self.row = []
        self.neighbours = []
        self.black = black
        self.white = white
        self.red = red
        self.blue = blue
        self.cell = None
        self.maze_generated = False
        # 新增属性:存储所有墙体的Rect碰撞盒,用于平滑移动碰撞检测
        self.wall_rects = []

    def on_start(self):
        # 初始化迷宫状态
        self.maze = []
        self.stack = []
        self.current_x, self.current_y = 0, 0

        self.maze.clear()
        self.stack.clear()
        for x in range(self.cols):
            self.row = []
            for y in range(self.rows):
                self.cell = Cell(x, y, self.cell_size, self.screen,
                                 self.black, self.white, self.red, self.blue)
                self.row.append(self.cell)
            self.maze.append(self.row)

    def in_bounds(self, x, y):
        # 判断坐标是否在迷宫范围内
        return 0 <= x < self.cols and 0 <= y < self.rows

    def find_next_cell(self, x, y):
        self.neighbours = []
        # 遍历四个方向的相邻单元格
        dx, dy = [1, -1, 0, 0], [0, 0, 1, -1]
        for d in range(4):
            if self.in_bounds(x + dx[d], y + dy[d]):
                if not self.maze[x + dx[d]][y + dy[d]].generated:
                    self.neighbours.append((x + dx[d], y + dy[d]))
        # 随机返回一个未生成的相邻单元格,没有则返回(-1,-1)
        if len(self.neighbours) > 0:
            return self.neighbours[random.randint(0, len(self.neighbours) - 1)]
        else:
            return -1, -1

    def remove_wall(self, x1, y1, x2, y2):
        # 移除两个相邻单元格之间的墙
        xd = self.maze[x1][y1].x - self.maze[x2][y2].x
        # 相邻单元格在x轴方向
        if xd == 1:
            self.maze[x1][y1].walls[3] = False
            self.maze[x2][y2].walls[2] = False
        elif xd == -1:
            self.maze[x1][y1].walls[2] = False
            self.maze[x2][y2].walls[3] = False
        # 相邻单元格在y轴方向
        yd = self.maze[x1][y1].y - self.maze[x2][y2].y
        if yd == 1:
            self.maze[x1][y1].walls[0] = False
            self.maze[x2][y2].walls[1] = False
        elif yd == -1:
            self.maze[x1][y1].walls[1] = False
            self.maze[x2][y2].walls[0] = False

    def create_maze(self):
        # 深度优先算法生成迷宫
        self.maze[self.current_x][self.current_y].generated = True
        next_cell = self.find_next_cell(self.current_x, self.current_y)
        if next_cell[0] >= 0 and next_cell[1] >= 0:
            self.stack.append((self.current_x, self.current_y))
            self.remove_wall(self.current_x, self.current_y, next_cell[0], next_cell[1])
            self.current_x = next_cell[0]
            self.current_y = next_cell[1]
        elif len(self.stack) > 0:
            previous = self.stack.pop()
            self.current_x = previous[0]
            self.current_y = previous[1]
        self.maze_generated = True
    
    # 新增方法:生成所有墙体的碰撞盒列表,迷宫生成完成后调用一次即可
    def generate_wall_rects(self):
        self.wall_rects.clear()
        for x in range(self.cols):
            for y in range(self.rows):
                cell = self.maze[x][y]
                screen_x = x * self.cell_size
                screen_y = y * self.cell_size
                # 墙的厚度和绘制时保持一致为5
                if cell.walls[0]:
                    self.wall_rects.append(pygame.Rect(screen_x, screen_y, self.cell_size, 5))
                if cell.walls[1]:
                    self.wall_rects.append(pygame.Rect(screen_x, screen_y + self.cell_size - 5, self.cell_size, 5))
                if cell.walls[2]:
                    self.wall_rects.append(pygame.Rect(screen_x + self.cell_size -5, screen_y, 5, self.cell_size))
                if cell.walls[3]:
                    self.wall_rects.append(pygame.Rect(screen_x, screen_y, 5, self.cell_size))

可行解决方案

方案1:基于单元格的移动校验(适合格子步进式移动)

不需要额外存储墙的坐标,直接复用现有Cell类的walls属性即可,逻辑如下:

  • 首先根据玩家当前像素坐标,换算出所在的单元格索引:
    cell_x = int(player_x // maze.cell_size)
    cell_y = int(player_y // maze.cell_size)
    current_cell = maze.maze[cell_x][cell_y]
    
  • 移动前先判断方向对应的墙是否存在,存在则禁止移动:
    • 向上移动(y坐标减小):检查current_cell.walls[0]是否为True
    • 向下移动(y坐标增大):检查current_cell.walls[1]是否为True
    • 向右移动(x坐标增大):检查current_cell.walls[2]是否为True
    • 向左移动(x坐标减小):检查current_cell.walls[3]是否为True

方案2:预生成墙体Rect碰撞盒(适合平滑移动)

如果玩家是像素级平滑移动,可以用上述代码中新增的generate_wall_rects方法,在迷宫生成完成后调用一次,生成所有墙体的碰撞Rect列表,后续碰撞检测直接调用pygame原生的碰撞接口即可:

# 碰撞检测逻辑示例
player_rect = pygame.Rect(player_x, player_y, player_width, player_height)
can_move = True
for wall_rect in maze.wall_rects:
    if player_rect.colliderect(wall_rect):
        # 发生碰撞,取消本次移动
        can_move = False
        break

内容的提问来源于stack exchange,提问作者Simon JH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 20:27:03