Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
202 views
in Technique[技术] by (71.8m points)

python - How do I prevent the player from moving through the walls in a maze in pygame?

I have a maze organized in a grid. Each cell of the grid stores the information about the walls to its right and bottom neighboring cell. The player is an object of a certain size whose bounding box is known. I want to move the player smoothly through the maze with the walls preventing them from going through.

Minimal and reproducible example:

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()
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Use mask collision. Draw the maze of a transparent 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)

Crate a pygame.Mask from the Surface with pygame.mask.from_surface:

maze_mask = pygame.mask.from_surface(maze_surf)

Create a mask form the player:

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)

Calculate the new position of the player:

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)

Use pygame.mask.Mask.overlap to see if the masks are intersects (see Pygame collision with masks is not working). Skip the movement when the mask of the maze intersects the player's mask:

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

See also Maze collision detection


Minimal example: 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()

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...