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
- 向上移动(y坐标减小):检查
方案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
相关产品推荐
相关产品推荐

