如何在 pygame 中检测碰撞?

问题描述 投票:0回答:5

我已经使用下面的类制作了一个子弹列表和一个精灵列表。我如何检测子弹是否与 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))
python pygame collision-detection pygame-surface pygame2
5个回答
78
投票

在 PyGame 中,碰撞检测是使用

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

一些例子:

  • pygame.Rect.collidepoint

    测试点是否在矩形内

    repl.it/@Rabbid76/PyGame-碰撞点

    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

    测试两个矩形是否重叠

    另见如何在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.collidelist
pygame.Rect.collidelistall
可用于矩形与矩形列表之间的碰撞测试。
pygame.Rect.collidedict
pygame.Rect.collidedictall
可用于矩形与矩形字典之间的碰撞测试。

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

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

可能的

collided
可调用项是
collide_rect
collide_rect_ratio
collide_circle
collide_circle_ratio
collide_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()
    

关于与面具的碰撞,请参阅如何制作碰撞面具?Pygame面具碰撞

另见碰撞与交叉

  • 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(80, 80)
    sprite1.radius = 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)
    sprite2.radius = 40 
    
    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()
返回一个具有 Surface 对象大小的矩形,它始终从 (0, 0) 开始,因为 Surface 对象没有位置。矩形的位置可以由关键字参数指定。例如,可以使用关键字参数
center
指定矩形的中心。这些关键字参数在返回之前应用于
pygame.Rect
的属性(有关关键字参数列表,请参阅
pygame.Rect
)。
请参阅 *为什么我的碰撞测试总是返回“真”,为什么图像矩形的位置总是错误的 (0, 0)?

您根本不需要

x
y
Sprite
Bullet
属性。使用
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")

14
投票

根据我对 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()

6
投票

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

这里有一个例子。

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()

1
投票

为子弹创建一个组,然后将子弹添加到组中。

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

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

在主循环中的某处:

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

希望这对你有用!!!


0
投票

在 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
© www.soinside.com 2019 - 2024. All rights reserved.