【问题标题】:How do I detect collision in pygame?我如何检测pygame中的碰撞?
【发布时间】:2022-11-21 01:40:50
【问题描述】:

我使用下面的类制作了一个子弹列表和一个精灵列表。如何检测子弹是否与 sprite 发生碰撞,然后删除该 sprite 和子弹?

#Define the sprite class
class Sprite:

    def __init__(self,x,y, name):
        self.x=x

        self.y=y

        self.image = pygame.image.load(name)

        self.rect = self.image.get_rect()

    def render(self):
        window.blit(self.image, (self.x,self.y))


# Define the bullet class to create bullets          
class Bullet:

    def __init__(self,x,y):
        self.x = x + 23
        self.y = y
        self.bullet = pygame.image.load("user_bullet.BMP")
        self.rect = self.bullet.get_rect()

    def render(self):
        window.blit(self.bullet, (self.x, self.y))

【问题讨论】:

  • 我会注意到 pygame 中有一个 Sprite 类——我不确定在您的代码中重新定义它是个好主意。此外,它们真的是目标(需要一个更好的词),因为 sprite 只是一个在屏幕上具有图形表示的对象(因此你的 Bullet 也是一个 sprite)。

标签: python pygame collision-detection pygame-surface pygame2


【解决方案1】:

在 PyGame 中,碰撞检测是使用 pygame.Rect 对象完成的。 Rect 对象提供了多种检测对象之间碰撞的方法。即使是矩形和圆形物体(例如桨和球)之间的碰撞也可以通过两个矩形物体(桨和球的边界矩形)之间的碰撞来检测。

一些例子:

  • pygame.Rect.collidepoint:

    测试一个点是否在矩形内

    repl.it/@Rabbid76/PyGame-collidepoint

    import pygame
    
    pygame.init()
    window = pygame.display.set_mode((250, 250))
    rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(100, 100)
    
    run = True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
    
        point = pygame.mouse.get_pos()
        collide = rect.collidepoint(point)
        color = (255, 0, 0) if collide else (255, 255, 255)
    
        window.fill(0)
        pygame.draw.rect(window, color, rect)
        pygame.display.flip()
    
    pygame.quit()
    exit()
    
  • pygame.Rect.colliderect

    测试两个矩形是否重叠

    另见How to detect collisions between two rectangular objects or images in pygame

    repl.it/@Rabbid76/PyGame-colliderect

    import pygame
    
    pygame.init()
    window = pygame.display.set_mode((250, 250))
    rect1 = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
    rect2 = pygame.Rect(0, 0, 75, 75)
    
    run = True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
    
        rect2.center = pygame.mouse.get_pos()
        collide = rect1.colliderect(rect2)
        color = (255, 0, 0) if collide else (255, 255, 255)
    
        window.fill(0)
        pygame.draw.rect(window, color, rect1)
        pygame.draw.rect(window, (0, 255, 0), rect2, 6, 1)
        pygame.display.flip()
    
    pygame.quit()
    exit()
    

此外,pygame.Rect.collidelistpygame.Rect.collidelistall可用于矩形与矩形列表之间的碰撞测试。 pygame.Rect.collidedictpygame.Rect.collidedictall可用于矩形和矩形字典之间的碰撞测试。

pygame.sprite.Spritepygame.sprite.Group对象的碰撞,可以被pygame.sprite.spritecollide()pygame.sprite.groupcollide()pygame.sprite.spritecollideany()检测到。使用这些方法时,可以通过collided参数指定碰撞检测算法:

collided 参数是一个回调函数,用于计算两个精灵是否发生碰撞。

可能的collided callables 是collide_rectcollide_rect_ratiocollide_circlecollide_circle_ratiocollide_mask

一些例子:

  • pygame.sprite.spritecollide()

    repl.it/@Rabbid76/PyGame-spritecollide

    import pygame
    
    pygame.init()
    window = pygame.display.set_mode((250, 250))
    
    sprite1 = pygame.sprite.Sprite()
    sprite1.image = pygame.Surface((75, 75))
    sprite1.image.fill((255, 0, 0))
    sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
    sprite2 = pygame.sprite.Sprite()
    sprite2.image = pygame.Surface((75, 75))
    sprite2.image.fill((0, 255, 0))
    sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(75, 75)
    
    all_group = pygame.sprite.Group([sprite2, sprite1])
    test_group = pygame.sprite.Group(sprite2)
    
    run = True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
    
        sprite1.rect.center = pygame.mouse.get_pos()
        collide = pygame.sprite.spritecollide(sprite1, test_group, False)
    
        window.fill(0)
        all_group.draw(window)
        for s in collide:
            pygame.draw.rect(window, (255, 255, 255), s.rect, 5, 1)
        pygame.display.flip()
    
    pygame.quit()
    exit()
    

有关与面具的碰撞,请参阅How can I make a collision mask?Pygame mask collision

另见Collision and Intersection

  • pygame.sprite.spritecollide() / collide_circle

    repl.it/@Rabbid76/PyGame-spritecollidecollidecircle

    import pygame
    
    pygame.init()
    window = pygame.display.set_mode((250, 250))
    
    sprite1 = pygame.sprite.Sprite()
    sprite1.image = pygame.Surface((80, 80), pygame.SRCALPHA)
    pygame.draw.circle(sprite1.image, (255, 0, 0), (40, 40), 40)
    sprite1.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(40, 40)
    sprite2 = pygame.sprite.Sprite()
    sprite2.image = pygame.Surface((80, 89), pygame.SRCALPHA)
    pygame.draw.circle(sprite2.image, (0, 255, 0), (40, 40), 40)
    sprite2.rect = pygame.Rect(*window.get_rect().center, 0, 0).inflate(80, 80)
    
    all_group = pygame.sprite.Group([sprite2, sprite1])
    test_group = pygame.sprite.Group(sprite2)
    
    run = True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
    
        sprite1.rect.center = pygame.mouse.get_pos()
        collide = pygame.sprite.spritecollide(sprite1, test_group, False, pygame.sprite.collide_circle)
    
        window.fill(0)
        all_group.draw(window)
        for s in collide:
            pygame.draw.circle(window, (255, 255, 255), s.rect.center, s.rect.width // 2, 5)
        pygame.display.flip()
    
    pygame.quit()
    exit()
    

这对您的代码意味着什么?

pygame.Surface.get_rect.get_rect() 返回一个大小为表面对象,它总是从 (0, 0) 开始,因为表面对象没有位置。矩形的位置可以由关键字参数指定。例如,矩形的中心可以用关键字参数center指定。这些关键字参数在返回之前应用于 pygame.Rect 的属性(有关关键字参数的列表,请参阅 pygame.Rect)。
见 *Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?

您根本不需要SpriteBulletxy 属性。请改用 rect 属性的位置:

#Define the sprite class
class Sprite:
    def __init__(self, x, y, name):
        self.image = pygame.image.load(name)
        self.rect = self.image.get_rect(topleft = (x, y))

    def render(self):
        window.blit(self.image, self.rect)

# Define the bullet class to create bullets          
class Bullet:
    def __init__(self, x, y):
        self.bullet = pygame.image.load("user_bullet.BMP")
        self.rect = self.bullet.get_rect(topleft = (x + 23, y))

    def render(self):
        window.blit(self.bullet, self.rect)

使用pygame.Rect.colliderect() 检测Sprite Bullet 实例之间的冲突。
How to detect collisions between two rectangular objects or images in pygame

my_sprite = Sprite(sx, sy, name)
my_bullet = Bullet(by, by)
while True:
    # [...]

    if my_sprite.rect.colliderect(my_bullet.rect):
        printe("hit")

【讨论】:

    【解决方案2】:

    根据我对 pygame 的了解,您只需要使用 colliderect 方法检查两个矩形是否重叠。一种方法是在您的 Bullet 类中使用一个方法来检查冲突:

    def is_collided_with(self, sprite):
        return self.rect.colliderect(sprite.rect)
    

    然后你可以这样称呼它:

    sprite = Sprite(10, 10, 'my_sprite')
    bullet = Bullet(20, 10)
    if bullet.is_collided_with(sprite):
        print('collision!')
        bullet.kill()
        sprite.kill()
    

    【讨论】:

    • 请注意,如果子弹相对于目标的速度大于目标的每刻宽度,则子弹只能“传送”到目标的另一侧而不会击中目标。如果可能是这种情况,您可能需要检查代表子弹从前一帧到当前帧的轨迹的矩形,而不仅仅是子弹本身。
    【解决方案3】:

    对于您尝试使用内置方法执行的操作,有一个非常简单的方法。

    这是一个例子。

    import pygame
    import sys
    
    class Sprite(pygame.sprite.Sprite):
        def __init__(self, pos):
            pygame.sprite.Sprite.__init__(self)
            self.image = pygame.Surface([20, 20])
            self.image.fill((255, 0, 0))
            self.rect = self.image.get_rect()
    
            self.rect.center = pos
    
    def main():
        pygame.init()
        clock = pygame.time.Clock()
        fps = 50
        bg = [255, 255, 255]
        size =[200, 200]
    
    
        screen = pygame.display.set_mode(size)
    
        player = Sprite([40, 50])
        player.move = [pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN]
        player.vx = 5
        player.vy = 5
    
    
        wall = Sprite([100, 60])
    
        wall_group = pygame.sprite.Group()
        wall_group.add(wall)
    
        player_group = pygame.sprite.Group()
        player_group.add(player)
    
        # I added loop for a better exit from the game
        loop = 1
        while loop:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    loop = 0
    
            key = pygame.key.get_pressed()
    
            for i in range(2):
                if key[player.move[i]]:
                    player.rect.x += player.vx * [-1, 1][i]
    
            for i in range(2):
                if key[player.move[2:4][i]]:
                    player.rect.y += player.vy * [-1, 1][i]
    
            screen.fill(bg)
    
            # first parameter takes a single sprite
            # second parameter takes sprite groups
            # third parameter is a do kill command if true
            # all group objects colliding with the first parameter object will be
            # destroyed. The first parameter could be bullets and the second one
            # targets although the bullet is not destroyed but can be done with
            # simple trick bellow
            hit = pygame.sprite.spritecollide(player, wall_group, True)
    
            if hit:
                # if collision is detected call a function in your case destroy
                # bullet
                player.image.fill((255, 255, 255))
    
            player_group.draw(screen)
            wall_group.draw(screen)
    
            pygame.display.update()
            clock.tick(fps)
    
        pygame.quit()
        # sys.exit
    
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

      【解决方案4】:

      为项目符号创建一个组,然后将项目符号添加到该组中。

      我会做的是: 在玩家的课程中:

      def collideWithBullet(self):
          if pygame.sprite.spritecollideany(self, 'groupName'):
              print("CollideWithBullet!!")
              return True
      

      在某处的主循环中:

      def run(self):
          if self.player.collideWithBullet():
               print("Game Over")
      

      希望这对你有用!!!

      【讨论】:

        【解决方案5】:

        在 Sprite 类中,尝试添加一个 self.mask 属性
        self.mask = pygame.mask.from_surface(self.image)
        以及 Sprite 类中的 collide_mask 函数,代码如下:

            def collide_mask(self, mask):
                collided = False
                mask_outline = mask.outline()
                self.mask_outline = self.mask.outline()
                for point in range(len(mask_outline)):
                    mask_outline[point] = list(mask_outline[point])
                    mask_outline[point][0] += bullet.x
                    mask_outline[point][1] += bullet.y
                for point in range(len(self.mask_outline)):
                    self.mask_outline[point] = list(mask_outline[point])
                    self.mask_outline[point][0] += self.x
                    self.mask_outline[point][1] += self.y
                for point in mask_outline:
                    for self_mask_point in self.mask_outline:
                        if point = self_mask_point:
                            collided = True
                return collided
        

        【讨论】:

          猜你喜欢
          • 2015-06-20
          • 2019-10-10
          相关资源
          最近更新 更多