Reputation: 41
I have been working on a randomly generated maze game with enemies that move through the maze toward the player. However, I am having the issue of the player being able to move through the walls of the maze, the problem I have is that the walls of the maze aren't sprites and therefore cannot use sprite collide, or Rect collide. Is there another way I can stop the player from moving through the walls? The only option I can think of is to get the position of each wall used and prevent the player from moving into that value of x or y. Below is the visual representation of what I want to occur:
Maze code
class Cell(object):
def __init__(self, x, y, cell_size, screen, black, white, red, blue):
# position in matrix
self.x = x
self.y = y
# keeps track of which walls are still visible
self.walls = [True, True, True, True]
# checks if cell has been visited during generation
self.generated = False
# checks if cell is on path during solving
self.on_path = False
# checks if cell has been visited during solving
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):
# coordinates on screen
x = self.x * self.cell_size
y = self.y * self.cell_size
# draws a wall if it still exists
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)
# marks out white if generated during generation
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
def on_start(self):
# maintains the current state
# maze matrix of cell instances
self.maze = []
# stack of current cells on path
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
# if x and y are within the bounds of the walls
def find_next_cell(self, x, y):
# keeps track of valid neighbors
self.neighbours = []
# loop through these two arrays to find all 4 neighbor cells
dx, dy = [1, -1, 0, 0], [0, 0, 1, -1]
for d in range(4):
# add cell to neighbor list if it is in bounds and not generated
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]))
# returns a random cell in the neighbors list, or -1 -1 otherwise
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):
# x distance between original cell and neighbor cell
xd = self.maze[x1][y1].x - self.maze[x2][y2].x
# to the bottom
if xd == 1:
self.maze[x1][y1].walls[3] = False
self.maze[x2][y2].walls[1] = False
# to the top
elif xd == -1:
self.maze[x1][y1].walls[1] = False
self.maze[x2][y2].walls[3] = False
# y distance between original cell and neighbor cell
xy = self.maze[x1][y1].y - self.maze[x2][y2].y
# to the right
if xy == 1:
self.maze[x1][y1].walls[0] = False
self.maze[x2][y2].walls[2] = False
# to the left
elif xy == -1:
self.maze[x1][y1].walls[2] = False
self.maze[x2][y2].walls[0] = False
def create_maze(self):
# if self.maze_generated == False:
self.maze[self.current_x][self.current_y].generated = True
# self.maze[self.current_x][self.current_y].draw_current()
next_cell = self.find_next_cell(self.current_x, self.current_y)
# checks if a neighbor was returned
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]
# no neighbor, so go to the previous cell in the stack
elif len(self.stack) > 0:
previous = self.stack.pop()
self.current_x = previous[0]
self.current_y = previous[1]
# else:
self.maze_generated = True
Upvotes: 1
Views: 269
Reputation: 210909
Calculate the rows and columns of the corner points points of the player rectangle.
col_l = player.rect.left // CELL_SIZE
col_r = player.rect.right // CELL_SIZE
row_t = player.rect.top // CELL_SIZE
row_b = player.rect.bottom // CELL_SIZE
If the player moves, test if the player is entering a new cell. Skip the movement if there is a wall between the current cell and the new cell:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
new_rect = player.rect.move(-1, 0)
next_col = new_rect.left // CELL_SIZE
if col_l == next_col or not (m.maze[col_l][row_t].walls[3] or m.maze[col_l][row_b].walls[3]):
player.rect = new_rect
if keys[pygame.K_RIGHT]:
new_rect = player.rect.move(1, 0)
next_col = new_rect.right // CELL_SIZE
if col_r == next_col or not (m.maze[col_r][row_t].walls[1] or m.maze[col_r][row_b].walls[1]):
player.rect = new_rect
if keys[pygame.K_UP]:
new_rect = player.rect.move(0, -1)
next_row = new_rect.top // CELL_SIZE
if row_t == next_row or not (m.maze[col_l][row_t].walls[0] or m.maze[col_r][row_t].walls[0]):
player.rect = new_rect
if keys[pygame.K_DOWN]:
new_rect = player.rect.move(0, 1)
next_row = new_rect.bottom // CELL_SIZE
if row_b == next_row or not (m.maze[col_l][row_b].walls[2] or m.maze[col_r][row_b].walls[2]):
player.rect = new_rect
See also Adding collision to maze walls and How to implement barriers to stop the player moving through walls.
Complete example, base on your previous question How to generate my maze instantly so I don't have to watch it Generate?:
import pygame
import random
class Cell(object):
def __init__(self, x, y, cell_size, screen, black, white, red, blue):
# position in matrix
self.x = x
self.y = y
# keeps track of which walls are still visible
self.walls = [True, True, True, True]
# checks if cell has been visited during generation
self.generated = False
# checks if cell is on path during solving
self.on_path = False
# checks if cell has been visited during solving
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):
# coordinates on screen
x = self.x * self.cell_size
y = self.y * self.cell_size
# draws a wall if it still exists
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)
# marks out white if generated during generation
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
def on_start(self):
# maintains the current state
# maze matrix of cell instances
self.maze = []
# stack of current cells on path
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):
# keeps track of valid neighbors
self.neighbours = []
# loop through these two arrays to find all 4 neighbor cells
dx, dy = [1, -1, 0, 0], [0, 0, 1, -1]
for d in range(4):
# add cell to neighbor list if it is in bounds and not generated
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]))
# returns a random cell in the neighbors list, or -1 -1 otherwise
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):
# x distance between original cell and neighbor cell
xd = self.maze[x1][y1].x - self.maze[x2][y2].x
# to the bottom
if xd == 1:
self.maze[x1][y1].walls[3] = False
self.maze[x2][y2].walls[1] = False
# to the top
elif xd == -1:
self.maze[x1][y1].walls[1] = False
self.maze[x2][y2].walls[3] = False
# y distance between original cell and neighbor cell
xy = self.maze[x1][y1].y - self.maze[x2][y2].y
# to the right
if xy == 1:
self.maze[x1][y1].walls[0] = False
self.maze[x2][y2].walls[2] = False
# to the left
elif xy == -1:
self.maze[x1][y1].walls[2] = False
self.maze[x2][y2].walls[0] = False
def create_maze(self):
self.maze[self.current_x][self.current_y].generated = True
# self.maze[self.current_x][self.current_y].draw_current()
next_cell = self.find_next_cell(self.current_x, self.current_y)
# checks if a neighbor was returned
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]
# no neighbor, so go to the previous cell in the stack
elif len(self.stack) > 0:
previous = self.stack.pop()
self.current_x = previous[0]
self.current_y = previous[1]
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((20, 20))
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect(center = (20, 20))
def main():
WIDTH, HEIGHT = 800, 800
CELL_SIZE = 40
ROWS, COLUMNS = int(HEIGHT / CELL_SIZE), int(WIDTH / CELL_SIZE)
# color variables
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
# initialize pygame
pygame.init()
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Maze Gen")
CLOCK = pygame.time.Clock()
FPS = 60
m = Maze(SCREEN, CELL_SIZE, ROWS, COLUMNS, WHITE, BLACK, RED, BLUE)
m.on_start()
while True:
m.create_maze()
if len(m.stack) == 0:
break
player = Player()
all_sprites = pygame.sprite.Group(player)
running = True
while running:
CLOCK.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
col_l = player.rect.left // CELL_SIZE
col_r = player.rect.right // CELL_SIZE
row_t = player.rect.top // CELL_SIZE
row_b = player.rect.bottom // CELL_SIZE
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
new_rect = player.rect.move(-1, 0)
next_col = new_rect.left // CELL_SIZE
if col_l == next_col or not (m.maze[col_l][row_t].walls[3] or m.maze[col_l][row_b].walls[3]):
player.rect = new_rect
if keys[pygame.K_RIGHT]:
new_rect = player.rect.move(1, 0)
next_col = new_rect.right // CELL_SIZE
if col_r == next_col or not (m.maze[col_r][row_t].walls[1] or m.maze[col_r][row_b].walls[1]):
player.rect = new_rect
if keys[pygame.K_UP]:
new_rect = player.rect.move(0, -1)
next_row = new_rect.top // CELL_SIZE
if row_t == next_row or not (m.maze[col_l][row_t].walls[0] or m.maze[col_r][row_t].walls[0]):
player.rect = new_rect
if keys[pygame.K_DOWN]:
new_rect = player.rect.move(0, 1)
next_row = new_rect.bottom // CELL_SIZE
if row_b == next_row or not (m.maze[col_l][row_b].walls[2] or m.maze[col_r][row_b].walls[2]):
player.rect = new_rect
SCREEN.fill(WHITE)
for i in range(m.cols):
for j in range(m.rows):
m.maze[i][j].draw_cell()
all_sprites.draw(SCREEN)
pygame.display.flip()
if __name__ == "__main__":
main()
pygame.quit()
Upvotes: 2