简体   繁体   English

如何检测 pygame 中的碰撞?

[英]How do I detect collision in pygame?

I have made a list of bullets and a list of sprites using the classes below.我使用下面的类制作了一个子弹列表和一个精灵列表。 How do I detect if a bullet collides with a sprite and then delete that sprite and the bullet?如何检测子弹是否与 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))

In PyGame, collision detection is done using pygame.Rect objects.在 PyGame 中,碰撞检测是使用pygame.Rect对象完成的。 The Rect object offers various methods for detecting collisions between objects. Rect对象提供了多种方法来检测对象之间的碰撞。 Even the collision between a rectangular and circular object such as a paddle and a ball can be detected by a collision between two rectangular objects, the paddle and the bounding rectangle of the ball.甚至矩形和圆形物体(例如球拍和球)之间的碰撞也可以通过两个矩形物体(球拍和球的边界矩形)之间的碰撞来检测。

Some examples:一些例子:

Furthermore, pygame.Rect.collidelist and pygame.Rect.collidelistall can be used for the collision test between a rectangle and a list of rectangles.此外, pygame.Rect.collidelistpygame.Rect.collidelistall可用于矩形和矩形列表之间的碰撞测试。 pygame.Rect.collidedict and pygame.Rect.collidedictall can be used for the collision test between a rectangle and a dictionary of rectangles. pygame.Rect.collidedictpygame.Rect.collidedictall可用于矩形和矩形字典之间的碰撞测试。

The collision of pygame.sprite.Sprite andpygame.sprite.Group objects, can be detected by pygame.sprite.spritecollide() , pygame.sprite.groupcollide() or pygame.sprite.spritecollideany() . pygame.sprite.Spritepygame.sprite.Group对象的碰撞,可以通过pygame.sprite.spritecollide()pygame.sprite.groupcollide()pygame.sprite.spritecollideany()检测。 When using these methods, the collision detection algorithm can be specified by the collided argument:使用这些方法时,碰撞检测算法可以由collided参数指定:

The collided argument is a callback function used to calculate if two sprites are colliding. collided 参数是一个回调函数,用于计算两个精灵是否发生碰撞。

Possible collided callables are collide_rect , collide_rect_ratio , collide_circle , collide_circle_ratio , collide_mask可能的collided可调用对象是collide_rectcollide_rect_ratiocollide_circlecollide_circle_ratiocollide_mask

Some examples:一些例子:

  • pygame.sprite.spritecollide()

    repl.it/@Rabbid76/PyGame-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()

For a collision with masks, see How can I make a collision mask?对于与掩码的碰撞,请参阅如何制作碰撞掩码? or Pygame mask collisionPygame 蒙版碰撞

See also Collision and Intersection另见碰撞和相交

  • pygame.sprite.spritecollide() / collide_circle pygame.sprite.spritecollide() / collide_circle

    repl.it/@Rabbid76/PyGame-spritecollidecollidecircle 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()

What does this all mean for your code?这对您的代码意味着什么?

pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. pygame.Surface.get_rect.get_rect()返回一个具有Surface对象大小的矩形,该矩形始终从 (0, 0) 开始,因为Surface对象没有位置。 The position of the rectangle can be specified by a keyword argument.矩形的位置可以由关键字参数指定。 For example, the centre of the rectangle can be specified with the keyword argument center .例如,可以使用关键字参数center指定矩形的中心。 These keyword arguments are applied to the attributes of the pygame.Rect before it is returned (see pygame.Rect for a list of the keyword arguments).这些关键字参数在返回之前应用于pygame.Rect的属性(有关关键字参数的列表,请参见pygame.Rect )。
See * Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?请参阅 * 为什么我的碰撞测试总是返回“真”,为什么图像矩形的位置总是错误 (0, 0)?

You do not need the x and y attributes of Sprite and Bullet at all.您根本不需要SpriteBulletxy属性。 Use the position of the rect attribute instead:改用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)

Use pygame.Rect.colliderect() to detect collisions between instances of Sprite and Bullet .使用pygame.Rect.colliderect()来检测SpriteBullet实例之间的碰撞。
See How to detect collisions between two rectangular objects or images in pygame :请参阅如何在 pygame 中检测两个矩形对象或图像之间的碰撞

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

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

From what I understand of pygame you just need to check if the two rectangles overlap using the colliderect method.根据我对 pygame 的了解,您只需要使用colliderect方法检查两个矩形是否重叠。 One way to do it is to have a method in your Bullet class that checks for collisions:一种方法是在Bullet类中使用一个方法来检查碰撞:

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

Then you can call it like:然后你可以这样称呼它:

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

There is a very simple method for what you are trying to do using built in methods.对于您尝试使用内置方法执行的操作,有一个非常简单的方法。

here is an example.这是一个例子。

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

Make a group for the bullets, and then add the bullets to the group.为项目符号创建一个组,然后将项目符号添加到该组。

What I would do is this: In the class for the player:我要做的是:在玩家的课堂上:

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

And in the main loop somewhere:在某处的主循环中:

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

Hopefully that works for you!!!希望这对你有用!!!

Inside the Sprite class, try adding a self.mask attribute with在 Sprite 类中,尝试添加一个self.mask属性
self.mask = pygame.mask.from_surface(self.image)
and a collide_mask function inside of the Sprite class with this code:和 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM