如何检测pygame中的碰撞?

Posted

技术标签:

【中文标题】如何检测pygame中的碰撞?【英文标题】:How do I detect collision in pygame? 【发布时间】:2015-06-20 20:25:01 【问题描述】:

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

#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 类 - 我不确定在您的代码中重新定义它是一个好主意。此外,它们是否真的是目标(因为需要一个更好的词),因为精灵只是一个在屏幕上具有图形表示的对象(因此您的 Bullet 也是一个精灵)。 【参考方案1】:

在 PyGame 中,可以使用pygame.Rect 对象来完成基本的碰撞检测。 Rect 对象提供了多种方法来检测对象之间的碰撞。请注意,即使在 Pong 游戏中,矩形对象与圆形对象(例如桨和球)的碰撞也可以通过两个矩形对象(桨叶和边界矩形)之间的碰撞来粗略检测。球。

一些例子:

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 可调用对象是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 made 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() 返回一个具有 Surface 对象大小的矩形,该矩形始终从 (0, 0) 开始,因为 Surface 对象没有位置。矩形的位置可以由关键字参数指定。例如,可以使用关键字参数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")

【讨论】:

@Shihab collidelist【参考方案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")

希望对你有用!!!

【讨论】:

以上是关于如何检测pygame中的碰撞?的主要内容,如果未能解决你的问题,请参考以下文章

pygame碰撞检测函数python3中的语法错误[重复]

pygame.mask原理及使用pygame.mask实现精准碰撞检测

pygame.mask原理及使用pygame.mask实现精准碰撞检测

pygame.mask原理及使用pygame.mask实现精准碰撞检测

Python进阶篇十一Pygame的精灵和碰撞检测

pygame之精灵碰撞检测