如何防止玩家在 pygame 的迷宫中穿过墙壁?
How do I prevent the player from moving through the walls in a maze in pygame?
我有一个以网格形式组织的迷宫。网格的每个单元格都存储有关其右侧和底部相邻单元格的墙壁的信息。玩家是一个特定大小的对象,其边界框是已知的。我想让玩家顺利地穿过迷宫,墙壁阻止他们通过。
最小且可重现的示例:
import pygame, random
class Maze:
def __init__(self, rows = 9, columns = 9):
self.size = (columns, rows)
self.walls = [[[True, True] for _ in range(self.size[1])] for __ in range(self.size[0])]
visited = [[False for _ in range(self.size[1])] for __ in range(self.size[0])]
i, j = (self.size[0]+1) // 2, (self.size[1]+1) // 2
visited[i][j] = True
stack = [(i, j)]
while stack:
current = stack.pop()
i, j = current
nl = [n for n in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]
if 0 <= n[0] < self.size[0] and 0 <= n[1] < self.size[1] and not visited[n[0]][n[1]]]
if nl:
stack.insert(0, current)
next = random.choice(nl)
self.walls[min(next[0], current[0])][min(next[1], current[1])][abs(next[1]-current[1])] = False
visited[next[0]][next[1]] = True
stack.insert(0, next)
def draw_maze(surf, maze, x, y, l, color, width):
lines = [((x, y), (x + l * len(maze.walls), y)), ((x, y), (x, y + l * len(maze.walls[0])))]
for i, row in enumerate(maze.walls):
for j, cell in enumerate(row):
if cell[0]: lines += [((x + i*l + l, y + j*l), (x + i*l + l, y + j*l + l))]
if cell[1]: lines += [((x + i*l, y + j*l + l), (x + i*l + l, y + j*l + l))]
for line in lines:
pygame.draw.line(surf, color, *line, width)
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
maze = Maze()
player_rect = pygame.Rect(190, 190, 20, 20)
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
player_rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 3
player_rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 3
window.fill(0)
draw_maze(window, maze, 20, 20, 40, (196, 196, 196), 3)
pygame.draw.circle(window, (255, 255, 0), player_rect.center, player_rect.width//2)
pygame.display.flip()
pygame.quit()
exit()
使用遮罩碰撞。画一个透明的迷宫pygame.Surface
:
cell_size = 40
maze = Maze()
maze_surf = pygame.Surface((maze.size[0]*cell_size, maze.size[1]*cell_size), pygame.SRCALPHA)
draw_maze(maze_surf, maze, 0, 0, cell_size, (196, 196, 196), 3)
创建 pygame.Mask
from the Surface with pygame.mask.from_surface
:
maze_mask = pygame.mask.from_surface(maze_surf)
为玩家创建面具:
player_rect = pygame.Rect(190, 190, 20, 20)
player_surf = pygame.Surface(player_rect.size, pygame.SRCALPHA)
pygame.draw.circle(player_surf, (255, 255, 0), (player_rect.width//2, player_rect.height//2), player_rect.width//2)
player_mask = pygame.mask.from_surface(player_surf)
计算玩家的新位置:
keys = pygame.key.get_pressed()
new_rect = player_rect.move(
(keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 3,
(keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 3)
使用pygame.mask.Mask.overlap
to see if the masks are intersects (see )。跳过迷宫蒙版与玩家蒙版相交时的动作:
offset = (new_rect.x - maze_pos[0]), (new_rect.y - maze_pos[1])
if not maze_mask.overlap(player_mask, offset):
player_rect = new_rect
最小示例: repl.it/@Rabbid76/PyGame-Maze-MaskCollision
import pygame, random
class Maze:
def __init__(self, rows = 9, columns = 9):
self.size = (columns, rows)
self.walls = [[[True, True] for _ in range(self.size[1])] for __ in range(self.size[0])]
visited = [[False for _ in range(self.size[1])] for __ in range(self.size[0])]
i, j = (self.size[0]+1) // 2, (self.size[1]+1) // 2
visited[i][j] = True
stack = [(i, j)]
while stack:
current = stack.pop()
i, j = current
nl = [n for n in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]
if 0 <= n[0] < self.size[0] and 0 <= n[1] < self.size[1] and not visited[n[0]][n[1]]]
if nl:
stack.insert(0, current)
next = random.choice(nl)
self.walls[min(next[0], current[0])][min(next[1], current[1])][abs(next[1]-current[1])] = False
visited[next[0]][next[1]] = True
stack.insert(0, next)
def draw_maze(surf, maze, x, y, l, color, width):
lines = [((x, y), (x + l * len(maze.walls), y)), ((x, y), (x, y + l * len(maze.walls[0])))]
for i, row in enumerate(maze.walls):
for j, cell in enumerate(row):
if cell[0]: lines += [((x + i*l + l, y + j*l), (x + i*l + l, y + j*l + l))]
if cell[1]: lines += [((x + i*l, y + j*l + l), (x + i*l + l, y + j*l + l))]
for line in lines:
pygame.draw.line(surf, color, *line, width)
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
maze_pos = 20, 20
cell_size = 40
maze = Maze()
maze_surf = pygame.Surface((maze.size[0]*cell_size, maze.size[1]*cell_size), pygame.SRCALPHA)
draw_maze(maze_surf, maze, 0, 0, cell_size, (196, 196, 196), 3)
maze_mask = pygame.mask.from_surface(maze_surf)
player_rect = pygame.Rect(190, 190, 20, 20)
player_surf = pygame.Surface(player_rect.size, pygame.SRCALPHA)
pygame.draw.circle(player_surf, (255, 255, 0), (player_rect.width//2, player_rect.height//2), player_rect.width//2)
player_mask = pygame.mask.from_surface(player_surf)
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
new_rect = player_rect.move(
(keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 3,
(keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 3)
offset = (new_rect.x - maze_pos[0]), (new_rect.y - maze_pos[1])
if not maze_mask.overlap(player_mask, offset):
player_rect = new_rect
window.fill(0)
window.blit(maze_surf, maze_pos)
window.blit(player_surf, player_rect)
pygame.display.flip()
pygame.quit()
exit()
实现简单的逻辑,测试玩家移动时玩家路径中是否有墙。当检测到与墙壁的碰撞时丢弃运动。
向 Maze
class 添加方法以检查单元格与其相邻单元格之间的墙:
class Maze:
# [...]
def wall_left(self, i, j):
return i < 1 or self.walls[i-1][j][0]
def wall_right(self, i, j):
return i >= self.size[0] or self.walls[i][j][0]
def wall_top(self, i, j):
return j < 1 or self.walls[i][j-1][1]
def wall_bottom(self, i, j):
return j >= self.size[0] or self.walls[i][j][1]
计算玩家边界框角点的行数和列数。
i0 = (player_rect.left - maze_pos[0]) // cell_size
i1 = (player_rect.right - maze_pos[0]) // cell_size
j0 = (player_rect.top - maze_pos[1]) // cell_size
j1 = (player_rect.bottom - maze_pos[1]) // cell_size
随着玩家的移动,测试玩家是否正在进入一个新的单元格。使用 Maze
class 中的新方法来测试玩家路径中是否有墙。如果路径被墙挡住,则跳过移动:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
new_rect = player_rect.move(-3, 0)
ni = (new_rect.left - maze_pos[0]) // cell_size
if i0 == ni or not (maze.wall_left(i0, j0) or maze.wall_left(i0, j1) or (j0 != j1 and maze.wall_bottom(ni, j0))):
player_rect = new_rect
if keys[pygame.K_RIGHT]:
new_rect = player_rect.move(3, 0)
ni = (new_rect.right - maze_pos[0]) // cell_size
if i1 == ni or not (maze.wall_right(i1, j0) or maze.wall_right(i1, j1) or (j0 != j1 and maze.wall_bottom(ni, j0))):
player_rect = new_rect
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
new_rect = player_rect.move(0, -3)
nj = (new_rect.top - maze_pos[1]) // cell_size
if j0 == nj or not (maze.wall_top(i0, j0) or maze.wall_top(i1, j0) or (i0 != i1 and maze.wall_right(i0, nj))):
player_rect = new_rect
if keys[pygame.K_DOWN]:
new_rect = player_rect.move(0, 3)
nj = (new_rect.bottom - maze_pos[1]) // cell_size
if j1 == nj or not (maze.wall_bottom(i0, j1) or maze.wall_bottom(i1, j1) or (i0 != i1 and maze.wall_right(i0, nj))):
player_rect = new_rect
最小示例: repl.it/@Rabbid76/PyGame-Maze-CollisionLogic
import pygame, random
class Maze:
def __init__(self, rows = 9, columns = 9):
self.size = (columns, rows)
self.walls = [[[True, True] for _ in range(self.size[1])] for __ in range(self.size[0])]
visited = [[False for _ in range(self.size[1])] for __ in range(self.size[0])]
i, j = (self.size[0]+1) // 2, (self.size[1]+1) // 2
visited[i][j] = True
stack = [(i, j)]
while stack:
current = stack.pop()
i, j = current
nl = [n for n in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]
if 0 <= n[0] < self.size[0] and 0 <= n[1] < self.size[1] and not visited[n[0]][n[1]]]
if nl:
stack.insert(0, current)
next = random.choice(nl)
self.walls[min(next[0], current[0])][min(next[1], current[1])][abs(next[1]-current[1])] = False
visited[next[0]][next[1]] = True
stack.insert(0, next)
def wall_left(self, i, j):
return i < 1 or self.walls[i-1][j][0]
def wall_right(self, i, j):
return i >= self.size[0] or self.walls[i][j][0]
def wall_top(self, i, j):
return j < 1 or self.walls[i][j-1][1]
def wall_bottom(self, i, j):
return j >= self.size[0] or self.walls[i][j][1]
def draw_maze(surf, maze, x, y, l, color, width):
lines = [((x, y), (x + l * len(maze.walls), y)), ((x, y), (x, y + l * len(maze.walls[0])))]
for i, row in enumerate(maze.walls):
for j, cell in enumerate(row):
if cell[0]: lines += [((x + i*l + l, y + j*l), (x + i*l + l, y + j*l + l))]
if cell[1]: lines += [((x + i*l, y + j*l + l), (x + i*l + l, y + j*l + l))]
for line in lines:
pygame.draw.line(surf, color, *line, width)
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
maze = Maze()
player_rect = pygame.Rect(190, 190, 20, 20)
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
maze_pos = 20, 20
cell_size = 40
i0 = (player_rect.left - maze_pos[0]) // cell_size
i1 = (player_rect.right - maze_pos[0]) // cell_size
j0 = (player_rect.top - maze_pos[1]) // cell_size
j1 = (player_rect.bottom - maze_pos[1]) // cell_size
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
new_rect = player_rect.move(-3, 0)
ni = (new_rect.left - maze_pos[0]) // cell_size
if i0 == ni or not (maze.wall_left(i0, j0) or maze.wall_left(i0, j1) or (j0 != j1 and maze.wall_bottom(ni, j0))):
player_rect = new_rect
if keys[pygame.K_RIGHT]:
new_rect = player_rect.move(3, 0)
ni = (new_rect.right - maze_pos[0]) // cell_size
if i1 == ni or not (maze.wall_right(i1, j0) or maze.wall_right(i1, j1) or (j0 != j1 and maze.wall_bottom(ni, j0))):
player_rect = new_rect
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
new_rect = player_rect.move(0, -3)
nj = (new_rect.top - maze_pos[1]) // cell_size
if j0 == nj or not (maze.wall_top(i0, j0) or maze.wall_top(i1, j0) or (i0 != i1 and maze.wall_right(i0, nj))):
player_rect = new_rect
if keys[pygame.K_DOWN]:
new_rect = player_rect.move(0, 3)
nj = (new_rect.bottom - maze_pos[1]) // cell_size
if j1 == nj or not (maze.wall_bottom(i0, j1) or maze.wall_bottom(i1, j1) or (i0 != i1 and maze.wall_right(i0, nj))):
player_rect = new_rect
window.fill(0)
draw_maze(window, maze, 20, 20, cell_size, (196, 196, 196), 3)
pygame.draw.circle(window, (255, 255, 0), player_rect.center, player_rect.width//2)
pygame.display.flip()
pygame.quit()
exit()
两种@Rabbid76 的解决方案都很好,但我想提供另一种方法。
在这种方法中,你需要两个变量x
和y
来存储之前的位置,即碰撞前的位置。然后您需要将所有迷宫线的矩形存储在列表中。
现在,通过遍历列表并使用 Rect.colliderect
或使用 pygame 的 Rect.collidelist
(注意:如果没有碰撞,collidelist
将 return -1
)。如果它们发生碰撞,将当前位置重置为之前的位置。
代码:
import pygame, random
class Maze:
def __init__(self, rows = 9, columns = 9):
self.size = (columns, rows)
self.walls = [[[True, True] for _ in range(self.size[1])] for __ in range(self.size[0])]
visited = [[False for _ in range(self.size[1])] for __ in range(self.size[0])]
i, j = (self.size[0]+1) // 2, (self.size[1]+1) // 2
visited[i][j] = True
stack = [(i, j)]
while stack:
current = stack.pop()
i, j = current
nl = [n for n in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]
if 0 <= n[0] < self.size[0] and 0 <= n[1] < self.size[1] and not visited[n[0]][n[1]]]
if nl:
stack.insert(0, current)
next = random.choice(nl)
self.walls[min(next[0], current[0])][min(next[1], current[1])][abs(next[1]-current[1])] = False
visited[next[0]][next[1]] = True
stack.insert(0, next)
def draw_maze(surf, maze, x, y, l, color, width):
lines = maze_lines(maze, x, y, l)
for line in lines:
pygame.draw.line(surf, color, *line, width)
def maze_lines(maze, x, y, l):
lines = [((x, y), (x + l * len(maze.walls), y)), ((x, y), (x, y + l * len(maze.walls[0])))]
for i, row in enumerate(maze.walls):
for j, cell in enumerate(row):
if cell[0]: lines += [((x + i * l + l, y + j * l), (x + i * l + l, y + j * l + l))]
if cell[1]: lines += [((x + i * l, y + j * l + l), (x + i * l + l, y + j * l + l))]
return lines
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
maze = Maze()
player_rect = pygame.Rect(190, 190, 20, 20)
line_rects = [pygame.draw.line(window, (0, 0, 0), *line) for line in maze_lines(maze, 20, 20, 40)] # first store all the line's rect in the list
prev_x, prev_y = 0, 0
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
prev_x, prev_y = player_rect.x, player_rect.y
player_rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 3
player_rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 3
# if any(x.colliderect(player_rect) for x in line_rects):
if player_rect.collidelist(line_rects) != -1:
player_rect.x = prev_x
player_rect.y = prev_y
window.fill(0)
draw_maze(window, maze, 20, 20, 40, (196, 196, 196), 3)
pygame.draw.circle(window, (255, 255, 0), player_rect.center, player_rect.width//2)
pygame.display.flip()
pygame.quit()
exit()
输出:
我有一个以网格形式组织的迷宫。网格的每个单元格都存储有关其右侧和底部相邻单元格的墙壁的信息。玩家是一个特定大小的对象,其边界框是已知的。我想让玩家顺利地穿过迷宫,墙壁阻止他们通过。
最小且可重现的示例:
import pygame, random
class Maze:
def __init__(self, rows = 9, columns = 9):
self.size = (columns, rows)
self.walls = [[[True, True] for _ in range(self.size[1])] for __ in range(self.size[0])]
visited = [[False for _ in range(self.size[1])] for __ in range(self.size[0])]
i, j = (self.size[0]+1) // 2, (self.size[1]+1) // 2
visited[i][j] = True
stack = [(i, j)]
while stack:
current = stack.pop()
i, j = current
nl = [n for n in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]
if 0 <= n[0] < self.size[0] and 0 <= n[1] < self.size[1] and not visited[n[0]][n[1]]]
if nl:
stack.insert(0, current)
next = random.choice(nl)
self.walls[min(next[0], current[0])][min(next[1], current[1])][abs(next[1]-current[1])] = False
visited[next[0]][next[1]] = True
stack.insert(0, next)
def draw_maze(surf, maze, x, y, l, color, width):
lines = [((x, y), (x + l * len(maze.walls), y)), ((x, y), (x, y + l * len(maze.walls[0])))]
for i, row in enumerate(maze.walls):
for j, cell in enumerate(row):
if cell[0]: lines += [((x + i*l + l, y + j*l), (x + i*l + l, y + j*l + l))]
if cell[1]: lines += [((x + i*l, y + j*l + l), (x + i*l + l, y + j*l + l))]
for line in lines:
pygame.draw.line(surf, color, *line, width)
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
maze = Maze()
player_rect = pygame.Rect(190, 190, 20, 20)
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
player_rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 3
player_rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 3
window.fill(0)
draw_maze(window, maze, 20, 20, 40, (196, 196, 196), 3)
pygame.draw.circle(window, (255, 255, 0), player_rect.center, player_rect.width//2)
pygame.display.flip()
pygame.quit()
exit()
使用遮罩碰撞。画一个透明的迷宫pygame.Surface
:
cell_size = 40
maze = Maze()
maze_surf = pygame.Surface((maze.size[0]*cell_size, maze.size[1]*cell_size), pygame.SRCALPHA)
draw_maze(maze_surf, maze, 0, 0, cell_size, (196, 196, 196), 3)
创建 pygame.Mask
from the Surface with pygame.mask.from_surface
:
maze_mask = pygame.mask.from_surface(maze_surf)
为玩家创建面具:
player_rect = pygame.Rect(190, 190, 20, 20)
player_surf = pygame.Surface(player_rect.size, pygame.SRCALPHA)
pygame.draw.circle(player_surf, (255, 255, 0), (player_rect.width//2, player_rect.height//2), player_rect.width//2)
player_mask = pygame.mask.from_surface(player_surf)
计算玩家的新位置:
keys = pygame.key.get_pressed()
new_rect = player_rect.move(
(keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 3,
(keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 3)
使用pygame.mask.Mask.overlap
to see if the masks are intersects (see
offset = (new_rect.x - maze_pos[0]), (new_rect.y - maze_pos[1])
if not maze_mask.overlap(player_mask, offset):
player_rect = new_rect
最小示例:
import pygame, random
class Maze:
def __init__(self, rows = 9, columns = 9):
self.size = (columns, rows)
self.walls = [[[True, True] for _ in range(self.size[1])] for __ in range(self.size[0])]
visited = [[False for _ in range(self.size[1])] for __ in range(self.size[0])]
i, j = (self.size[0]+1) // 2, (self.size[1]+1) // 2
visited[i][j] = True
stack = [(i, j)]
while stack:
current = stack.pop()
i, j = current
nl = [n for n in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]
if 0 <= n[0] < self.size[0] and 0 <= n[1] < self.size[1] and not visited[n[0]][n[1]]]
if nl:
stack.insert(0, current)
next = random.choice(nl)
self.walls[min(next[0], current[0])][min(next[1], current[1])][abs(next[1]-current[1])] = False
visited[next[0]][next[1]] = True
stack.insert(0, next)
def draw_maze(surf, maze, x, y, l, color, width):
lines = [((x, y), (x + l * len(maze.walls), y)), ((x, y), (x, y + l * len(maze.walls[0])))]
for i, row in enumerate(maze.walls):
for j, cell in enumerate(row):
if cell[0]: lines += [((x + i*l + l, y + j*l), (x + i*l + l, y + j*l + l))]
if cell[1]: lines += [((x + i*l, y + j*l + l), (x + i*l + l, y + j*l + l))]
for line in lines:
pygame.draw.line(surf, color, *line, width)
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
maze_pos = 20, 20
cell_size = 40
maze = Maze()
maze_surf = pygame.Surface((maze.size[0]*cell_size, maze.size[1]*cell_size), pygame.SRCALPHA)
draw_maze(maze_surf, maze, 0, 0, cell_size, (196, 196, 196), 3)
maze_mask = pygame.mask.from_surface(maze_surf)
player_rect = pygame.Rect(190, 190, 20, 20)
player_surf = pygame.Surface(player_rect.size, pygame.SRCALPHA)
pygame.draw.circle(player_surf, (255, 255, 0), (player_rect.width//2, player_rect.height//2), player_rect.width//2)
player_mask = pygame.mask.from_surface(player_surf)
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
new_rect = player_rect.move(
(keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 3,
(keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 3)
offset = (new_rect.x - maze_pos[0]), (new_rect.y - maze_pos[1])
if not maze_mask.overlap(player_mask, offset):
player_rect = new_rect
window.fill(0)
window.blit(maze_surf, maze_pos)
window.blit(player_surf, player_rect)
pygame.display.flip()
pygame.quit()
exit()
实现简单的逻辑,测试玩家移动时玩家路径中是否有墙。当检测到与墙壁的碰撞时丢弃运动。
向 Maze
class 添加方法以检查单元格与其相邻单元格之间的墙:
class Maze:
# [...]
def wall_left(self, i, j):
return i < 1 or self.walls[i-1][j][0]
def wall_right(self, i, j):
return i >= self.size[0] or self.walls[i][j][0]
def wall_top(self, i, j):
return j < 1 or self.walls[i][j-1][1]
def wall_bottom(self, i, j):
return j >= self.size[0] or self.walls[i][j][1]
计算玩家边界框角点的行数和列数。
i0 = (player_rect.left - maze_pos[0]) // cell_size
i1 = (player_rect.right - maze_pos[0]) // cell_size
j0 = (player_rect.top - maze_pos[1]) // cell_size
j1 = (player_rect.bottom - maze_pos[1]) // cell_size
随着玩家的移动,测试玩家是否正在进入一个新的单元格。使用 Maze
class 中的新方法来测试玩家路径中是否有墙。如果路径被墙挡住,则跳过移动:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
new_rect = player_rect.move(-3, 0)
ni = (new_rect.left - maze_pos[0]) // cell_size
if i0 == ni or not (maze.wall_left(i0, j0) or maze.wall_left(i0, j1) or (j0 != j1 and maze.wall_bottom(ni, j0))):
player_rect = new_rect
if keys[pygame.K_RIGHT]:
new_rect = player_rect.move(3, 0)
ni = (new_rect.right - maze_pos[0]) // cell_size
if i1 == ni or not (maze.wall_right(i1, j0) or maze.wall_right(i1, j1) or (j0 != j1 and maze.wall_bottom(ni, j0))):
player_rect = new_rect
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
new_rect = player_rect.move(0, -3)
nj = (new_rect.top - maze_pos[1]) // cell_size
if j0 == nj or not (maze.wall_top(i0, j0) or maze.wall_top(i1, j0) or (i0 != i1 and maze.wall_right(i0, nj))):
player_rect = new_rect
if keys[pygame.K_DOWN]:
new_rect = player_rect.move(0, 3)
nj = (new_rect.bottom - maze_pos[1]) // cell_size
if j1 == nj or not (maze.wall_bottom(i0, j1) or maze.wall_bottom(i1, j1) or (i0 != i1 and maze.wall_right(i0, nj))):
player_rect = new_rect
最小示例:
import pygame, random
class Maze:
def __init__(self, rows = 9, columns = 9):
self.size = (columns, rows)
self.walls = [[[True, True] for _ in range(self.size[1])] for __ in range(self.size[0])]
visited = [[False for _ in range(self.size[1])] for __ in range(self.size[0])]
i, j = (self.size[0]+1) // 2, (self.size[1]+1) // 2
visited[i][j] = True
stack = [(i, j)]
while stack:
current = stack.pop()
i, j = current
nl = [n for n in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]
if 0 <= n[0] < self.size[0] and 0 <= n[1] < self.size[1] and not visited[n[0]][n[1]]]
if nl:
stack.insert(0, current)
next = random.choice(nl)
self.walls[min(next[0], current[0])][min(next[1], current[1])][abs(next[1]-current[1])] = False
visited[next[0]][next[1]] = True
stack.insert(0, next)
def wall_left(self, i, j):
return i < 1 or self.walls[i-1][j][0]
def wall_right(self, i, j):
return i >= self.size[0] or self.walls[i][j][0]
def wall_top(self, i, j):
return j < 1 or self.walls[i][j-1][1]
def wall_bottom(self, i, j):
return j >= self.size[0] or self.walls[i][j][1]
def draw_maze(surf, maze, x, y, l, color, width):
lines = [((x, y), (x + l * len(maze.walls), y)), ((x, y), (x, y + l * len(maze.walls[0])))]
for i, row in enumerate(maze.walls):
for j, cell in enumerate(row):
if cell[0]: lines += [((x + i*l + l, y + j*l), (x + i*l + l, y + j*l + l))]
if cell[1]: lines += [((x + i*l, y + j*l + l), (x + i*l + l, y + j*l + l))]
for line in lines:
pygame.draw.line(surf, color, *line, width)
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
maze = Maze()
player_rect = pygame.Rect(190, 190, 20, 20)
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
maze_pos = 20, 20
cell_size = 40
i0 = (player_rect.left - maze_pos[0]) // cell_size
i1 = (player_rect.right - maze_pos[0]) // cell_size
j0 = (player_rect.top - maze_pos[1]) // cell_size
j1 = (player_rect.bottom - maze_pos[1]) // cell_size
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
new_rect = player_rect.move(-3, 0)
ni = (new_rect.left - maze_pos[0]) // cell_size
if i0 == ni or not (maze.wall_left(i0, j0) or maze.wall_left(i0, j1) or (j0 != j1 and maze.wall_bottom(ni, j0))):
player_rect = new_rect
if keys[pygame.K_RIGHT]:
new_rect = player_rect.move(3, 0)
ni = (new_rect.right - maze_pos[0]) // cell_size
if i1 == ni or not (maze.wall_right(i1, j0) or maze.wall_right(i1, j1) or (j0 != j1 and maze.wall_bottom(ni, j0))):
player_rect = new_rect
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
new_rect = player_rect.move(0, -3)
nj = (new_rect.top - maze_pos[1]) // cell_size
if j0 == nj or not (maze.wall_top(i0, j0) or maze.wall_top(i1, j0) or (i0 != i1 and maze.wall_right(i0, nj))):
player_rect = new_rect
if keys[pygame.K_DOWN]:
new_rect = player_rect.move(0, 3)
nj = (new_rect.bottom - maze_pos[1]) // cell_size
if j1 == nj or not (maze.wall_bottom(i0, j1) or maze.wall_bottom(i1, j1) or (i0 != i1 and maze.wall_right(i0, nj))):
player_rect = new_rect
window.fill(0)
draw_maze(window, maze, 20, 20, cell_size, (196, 196, 196), 3)
pygame.draw.circle(window, (255, 255, 0), player_rect.center, player_rect.width//2)
pygame.display.flip()
pygame.quit()
exit()
两种@Rabbid76 的解决方案都很好,但我想提供另一种方法。
在这种方法中,你需要两个变量x
和y
来存储之前的位置,即碰撞前的位置。然后您需要将所有迷宫线的矩形存储在列表中。
现在,通过遍历列表并使用 Rect.colliderect
或使用 pygame 的 Rect.collidelist
(注意:如果没有碰撞,collidelist
将 return -1
)。如果它们发生碰撞,将当前位置重置为之前的位置。
代码:
import pygame, random
class Maze:
def __init__(self, rows = 9, columns = 9):
self.size = (columns, rows)
self.walls = [[[True, True] for _ in range(self.size[1])] for __ in range(self.size[0])]
visited = [[False for _ in range(self.size[1])] for __ in range(self.size[0])]
i, j = (self.size[0]+1) // 2, (self.size[1]+1) // 2
visited[i][j] = True
stack = [(i, j)]
while stack:
current = stack.pop()
i, j = current
nl = [n for n in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]
if 0 <= n[0] < self.size[0] and 0 <= n[1] < self.size[1] and not visited[n[0]][n[1]]]
if nl:
stack.insert(0, current)
next = random.choice(nl)
self.walls[min(next[0], current[0])][min(next[1], current[1])][abs(next[1]-current[1])] = False
visited[next[0]][next[1]] = True
stack.insert(0, next)
def draw_maze(surf, maze, x, y, l, color, width):
lines = maze_lines(maze, x, y, l)
for line in lines:
pygame.draw.line(surf, color, *line, width)
def maze_lines(maze, x, y, l):
lines = [((x, y), (x + l * len(maze.walls), y)), ((x, y), (x, y + l * len(maze.walls[0])))]
for i, row in enumerate(maze.walls):
for j, cell in enumerate(row):
if cell[0]: lines += [((x + i * l + l, y + j * l), (x + i * l + l, y + j * l + l))]
if cell[1]: lines += [((x + i * l, y + j * l + l), (x + i * l + l, y + j * l + l))]
return lines
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
maze = Maze()
player_rect = pygame.Rect(190, 190, 20, 20)
line_rects = [pygame.draw.line(window, (0, 0, 0), *line) for line in maze_lines(maze, 20, 20, 40)] # first store all the line's rect in the list
prev_x, prev_y = 0, 0
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
prev_x, prev_y = player_rect.x, player_rect.y
player_rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 3
player_rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 3
# if any(x.colliderect(player_rect) for x in line_rects):
if player_rect.collidelist(line_rects) != -1:
player_rect.x = prev_x
player_rect.y = prev_y
window.fill(0)
draw_maze(window, maze, 20, 20, 40, (196, 196, 196), 3)
pygame.draw.circle(window, (255, 255, 0), player_rect.center, player_rect.width//2)
pygame.display.flip()
pygame.quit()
exit()
输出: