python - 为迷宫墙壁添加碰撞

标签 python pygame collision-detection maze

有人可以帮我给 Sprite 添加碰撞点吗?我有一个过去的代码,我在图像上分层了一个位图,但相同的代码不能很好地集成物理绘制线而不是检测图像上黑色/灰色的位置

import random
import pygame
pygame.init()

WHITE = (255,255,255)
GREY = (20,20,20)
BLACK = (0,0,0)
PURPLE = (100,0,100)
RED = (255,0,0)

size = (701,701)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("Maze Generator")

done = False

clock = pygame.time.Clock()

width = 25
cols = int(size[0] / width)
rows = int(size[1] / width)

stack = []

pos = (0,0)
class Player(pygame.sprite.Sprite):
    def __init__(self, image, pos, background):
        super().__init__()
        self.image = image
        self.pos = pygame.Vector2(pos)
        self.rect = self.image.get_rect(center=self.pos)
        self.background = background

    def update(self, events, dt):
        pressed = pygame.key.get_pressed()
        move = pygame.Vector2((0, 0))
        if pressed[pygame.K_w]: move += (0, -1)
        if pressed[pygame.K_a]: move += (-1, 0)
        if pressed[pygame.K_s]: move += (0, 1)
        if pressed[pygame.K_d]: move += (1, 0)
        #if move.length() > 0: move.normalise_ip()

        self.pos = self.pos + move*(dt/5)
        self.rect.center = self.pos        

        if self.background:

            new_rect.clamp_ip(self.background.get_rect())
            new_pos = new_rect.center

            hit_box = self.background.subsurface(new_rect)
            for x in range(new_rect.width):
                for y in range(new_rect.height):
                    if sum(hit_box.get_at((x, y))) < 500:
                        return

def load_background(filename=None):
    name = filename if filename else "background.jpg"
    background = pygame.image.load(name)
    background = pygame.transform.rotate(background, -90)
    background = pygame.transform.scale(background, (800,600))
    return background

def load_player(background):
    pimg = pygame.Surface((10, 10))
    pimg.fill((200, 20, 20))
    return Player(pimg, (25, 325), background)

class Cell():
    def __init__(self,x,y):
        global width
        self.x = x * width
        self.y = y * width

        self.visited = False
        self.current = False

        self.walls = [True,True,True,True] # top , right , bottom , left

        # neighbors
        self.neighbors = []

        self.top = 0
        self.right = 0
        self.bottom = 0
        self.left = 0

        self.next_cell = 0

    def draw(self):
        if self.current:
            pygame.draw.rect(screen,RED,(self.x,self.y,width,width))
        elif self.visited:
            pygame.draw.rect(screen,WHITE,(self.x,self.y,width,width))

            if self.walls[0]:
                pygame.draw.line(screen,BLACK,(self.x,self.y),((self.x + width),self.y),1) # top
            if self.walls[1]:
                pygame.draw.line(screen,BLACK,((self.x + width),self.y),((self.x + width),(self.y + width)),1) # right
            if self.walls[2]:
                pygame.draw.line(screen,BLACK,((self.x + width),(self.y + width)),(self.x,(self.y + width)),1) # bottom
            if self.walls[3]:
                pygame.draw.line(screen,BLACK,(self.x,(self.y + width)),(self.x,self.y),1) # left

    def checkNeighbors(self):
        #print("Top; y: " + str(int(self.y / width)) + ", y - 1: " + str(int(self.y / width) - 1))
        if int(self.y / width) - 1 >= 0:
            self.top = grid[int(self.y / width) - 1][int(self.x / width)]
        #print("Right; x: " + str(int(self.x / width)) + ", x + 1: " + str(int(self.x / width) + 1))
        if int(self.x / width) + 1 <= cols - 1:
            self.right = grid[int(self.y / width)][int(self.x / width) + 1]
        #print("Bottom; y: " + str(int(self.y / width)) + ", y + 1: " + str(int(self.y / width) + 1))
        if int(self.y / width) + 1 <= rows - 1:
            self.bottom = grid[int(self.y / width) + 1][int(self.x / width)]
        #print("Left; x: " + str(int(self.x / width)) + ", x - 1: " + str(int(self.x / width) - 1))
        if int(self.x / width) - 1 >= 0:
            self.left = grid[int(self.y / width)][int(self.x / width) - 1]
        #print("--------------------")

        if self.top != 0:
            if self.top.visited == False:
                self.neighbors.append(self.top)
        if self.right != 0:
            if self.right.visited == False:
                self.neighbors.append(self.right)
        if self.bottom != 0:
            if self.bottom.visited == False:
                self.neighbors.append(self.bottom)
        if self.left != 0:
            if self.left.visited == False:
                self.neighbors.append(self.left)

        if len(self.neighbors) > 0:
            self.next_cell = self.neighbors[random.randrange(0,len(self.neighbors))]
            return self.next_cell
        else:
            return False

def removeWalls(current_cell,next_cell):
    x = int(current_cell.x / width) - int(next_cell.x / width)
    y = int(current_cell.y / width) - int(next_cell.y / width)
    if x == -1: # right of current
        current_cell.walls[1] = False
        next_cell.walls[3] = False
    elif x == 1: # left of current
        current_cell.walls[3] = False
        next_cell.walls[1] = False
    elif y == -1: # bottom of current
        current_cell.walls[2] = False
        next_cell.walls[0] = False
    elif y == 1: # top of current
        current_cell.walls[0] = False
        next_cell.walls[2] = False

grid = []

for y in range(rows):
    grid.append([])
    for x in range(cols):
        grid[y].append(Cell(x,y))

current_cell = grid[0][0]
next_cell = 0

# -------- Main Program Loop -----------
def main():
    global current_cell

    player = None
    initialized = False
    current_maze = None
    dt = 0  
    screen_rect = screen.get_rect()
    clock = pygame.time.Clock()
    sprites = pygame.sprite.Group()

    if not initialized:
        #current_maze = 0
        background = load_background()
        background = None
        player = load_player(background)
        sprites.add(player)
        initialized = True

    play = False 
    while not done:
        # --- Main event loop
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return

        for y in range(rows):
            for x in range(cols):
                grid[y][x].draw()

        if play == True:
            pygame.init()

            while not done:
                events = pygame.event.get()

                for e in events:
                    if e.type == pygame.QUIT:
                        return

                player_x = player.pos[0]
                player_y = player.pos[1]

                sprites.update(None, dt)

                #screen.fill(pygame.Color('grey'))
                #screen.blit(background, (0, 0))

                sprites.draw(screen)
                pygame.display.flip()
                dt = clock.tick(60)
        else:
            current_cell.visited = True
            current_cell.current = True

            next_cell = current_cell.checkNeighbors()
            if next_cell != False:
                current_cell.neighbors = []
                stack.append(current_cell)
                removeWalls(current_cell,next_cell)
                current_cell.current = False
                current_cell = next_cell
            elif len(stack) > 0:
                current_cell.current = False
                current_cell = stack.pop()
            else:
                play = True
        pygame.display.flip()


main()
pygame.quit()

如果有人能帮助我或指出正确的方向,我会很高兴。


    end_rect = pygame.draw.rect(screen, RED, (800-width, 600-width, width, width))
    if player.rect.colliderect(end_rect):
                    screen.fill(BLACK)
                    end = True

最佳答案

确保 Player 对象位于 grid 单元格的中心。例如计算玩家的随机位置:

def load_player(background):
    pimg = pygame.Surface((10, 10))
    pimg.fill((200, 20, 20))
    
    px = random.randint(0, rows-1) * width + width//2
    py = random.randint(0, cols-1) * width + width//2
    return Player(pimg, (px, py), background)

要跟踪玩家,但突出显示玩家位置,当前玩家位置必须在玩家更新和重绘之前用不同的颜色着色:

pygame.draw.rect(screen, (255, 164, 164), player.rect)
sprites.update(None, dt)
sprites.draw(screen)

对于碰撞测试:

  • 必须确定玩家在网格中的当前位置。
  • 必须确定球员可能的新位置,具体取决于 Action 。
  • 如果从当前位置到新位置的轨道上有一堵墙,则必须跳过该运动
class Player(pygame.sprite.Sprite):
    def __init__(self, image, pos, background):
        super().__init__()
        self.image = image
        self.pos = pygame.Vector2(pos)
        self.rect = self.image.get_rect(center=self.pos)
        self.background = background

    def update(self, events, dt):
        pressed = pygame.key.get_pressed()
        move = pygame.Vector2((0, 0))

        # calculate maximum movement and current cell position  
        testdist = dt // 5 + 2
        cellx = self.rect.centerx // width
        celly = self.rect.centery // width
        minx = self.rect.left // width
        maxx = self.rect.right // width
        miny = self.rect.top // width
        maxy = self.rect.bottom // width
        
        # test move up
        if minx == maxx and pressed[pygame.K_w]:
            nexty = (self.rect.top-testdist) // width
            if celly == nexty or (nexty >= 0 and not grid[celly][cellx].walls[0]):
                move += (0, -1)

        # test move right
        elif miny == maxy and pressed[pygame.K_d]:
            nextx = (self.rect.right+testdist) // width
            if cellx == nextx or (nextx < cols and not grid[celly][cellx].walls[1]):
                move += (1, 0)
               
        # test move down
        elif minx == maxx and pressed[pygame.K_s]:
            nexty = (self.rect.bottom+testdist) // width
            if celly == nexty or (nexty < rows and not grid[celly][cellx].walls[2]):
                move += (0, 1)            

        # test move left
        elif miny == maxy and pressed[pygame.K_a]:
            nextx = (self.rect.left-testdist) // width
            if cellx == nextx or (nextx >= 0 and not grid[celly][cellx].walls[3]):
                move += (-1, 0)
        
        self.pos = self.pos + move*(dt/5)
        self.rect.center = self.pos        


如果你想重新开始迷宫,你必须摆脱嵌套的游戏循环。使用 1 个游戏循环和一个条件,说明游戏是处于play 状态还是正在初始化迷宫。

检查玩家是否达到目标可以通过pygame.Rect.colliderect()来完成:

finished = pygame.Rect(0, 0, width, width).colliderect(player.rect)

要重新启动,您必须重置网格:

grid = []
for y in range(rows):
    grid.append([])
    for x in range(cols):
        grid[y].append(Cell(x,y))
current_cell = grid[0][0]

将播放器设置到一个新的随机位置:

px = random.randint(0, rows-1) * width + width//2
py = random.randint(0, cols-1) * width + width//2
player.pos = pygame.Vector2(px, py)
player.rect = player.image.get_rect(center=player.pos)

清除背景并重置状态play:

screen.fill(0)
play = False

完整的主要代码:

def main():
    global current_cell, grid

    player = None
    initialized = False
    current_maze = None
    dt = 0  
    screen_rect = screen.get_rect()
    clock = pygame.time.Clock()
    sprites = pygame.sprite.Group()

    if not initialized:
        #current_maze = 0
        background = load_background()
        background = None
        player = load_player(background)
        sprites.add(player)
        initialized = True

    play = False 
    while not done:
        # --- Main event loop
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return

        if play == True:
            
            pygame.draw.rect(screen, (255, 164, 164), player.rect)
            sprites.update(None, dt)
            sprites.draw(screen)

            dt = clock.tick(60)

            finished = pygame.Rect(0, 0, width, width).colliderect(player.rect)
            if finished:
                # init new grid
                grid = []
                for y in range(rows):
                    grid.append([])
                    for x in range(cols):
                        grid[y].append(Cell(x,y))
                current_cell = grid[0][0]
                # create new random player positon
                px = random.randint(0, rows-1) * width + width//2
                py = random.randint(0, cols-1) * width + width//2
                player.pos = pygame.Vector2(px, py)
                player.rect = player.image.get_rect(center=player.pos)
                # clear screen
                screen.fill(0)
                play = False

        else:
            current_cell.visited = True
            current_cell.current = True

            next_cell = current_cell.checkNeighbors()
            if next_cell != False:
                current_cell.neighbors = []
                stack.append(current_cell)
                removeWalls(current_cell,next_cell)
                current_cell.current = False
                current_cell = next_cell
            elif len(stack) > 0:
                current_cell.current = False
                current_cell = stack.pop()
            else:
                play = True

            for y in range(rows):
                for x in range(cols):
                    grid[y][x].draw()

        pygame.display.flip()

另见 How do I prevent the player from moving through the walls in a maze?

关于python - 为迷宫墙壁添加碰撞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55833941/

相关文章:

python - 表单无效后django上下文表单未呈现

python - 如何在 Pygame 中制作完全不可见的 Sprite ?

Python Config Parser 找不到部分?

javascript - 放置一个圆,使其不会与任何其他圆发生碰撞

python - Visual Studio Code 不读取 python3 中的模块

python - 需要帮助找出此 UnicodeDecodeError 的解决方案

python - 如何替换Python字典中的二维列表值?

python - 使用 Pygame 的 Py2EXE 编译错误

ios - Sprite Kit 的 'didBeginContact' 函数不是很准确。我应该怎么办?

physics - 球与球碰撞