¿Cómo detecto una colisión en pygame?

Resuelto Mike Schmidt asked hace 9 años • 5 respuestas

He hecho una lista de viñetas y una lista de sprites usando las clases siguientes. ¿Cómo puedo detectar si una bala choca con un objeto y luego eliminar ese objeto y la bala?

#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))
Mike Schmidt avatar Apr 15 '15 09:04 Mike Schmidt
Aceptado

En PyGame, la detección de colisiones se realiza mediante pygame.Rectobjetos. El Rectobjeto ofrece varios métodos para detectar colisiones entre objetos. Incluso la colisión entre un objeto rectangular y circular, como una paleta y una pelota, puede detectarse mediante una colisión entre dos objetos rectangulares, la paleta y el rectángulo delimitador de la pelota.

Algunos ejemplos:

  • pygame.Rect.collidepoint:

    Probar si un punto está dentro de un rectángulo

    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

    Prueba si dos rectángulos se superponen

    Ver también Cómo detectar colisiones entre dos objetos o imágenes rectangulares en pygame

    repl.it/@Rabbid76/PyGame-colliderect

    colisionar

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

Además, pygame.Rect.collidelisty pygame.Rect.collidelistallse puede utilizar para la prueba de colisión entre un rectángulo y una lista de rectángulos. pygame.Rect.collidedicty pygame.Rect.collidedictallpuede usarse para la prueba de colisión entre un rectángulo y un diccionario de rectángulos.

La colisión de objetos pygame.sprite.Spritey pygame.sprite.Grouppuede ser detectada por pygame.sprite.spritecollide(), pygame.sprite.groupcollide()o pygame.sprite.spritecollideany(). Cuando se utilizan estos métodos, el algoritmo de detección de colisiones se puede especificar mediante el collidedargumento:

El argumento colisionado es una función de devolución de llamada que se utiliza para calcular si dos sprites chocan.

Los posibles collidedinvocables son collide_rect, collide_rect_ratio, collide_circle, collide_circle_ratio,collide_mask

Algunos ejemplos:

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

Para una colisión con máscaras, consulte ¿Cómo puedo hacer una máscara de colisión? o colisión de máscara de Pygame

Ver también Colisión e Intersección

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

¿Qué significa todo esto para tu código?

pygame.Surface.get_rect.get_rect()devuelve un rectángulo con el tamaño del objeto Superficie , que siempre comienza en (0, 0) ya que un objeto Superficie no tiene posición. La posición del rectángulo se puede especificar mediante un argumento de palabra clave. Por ejemplo, el centro del rectángulo se puede especificar con el argumento de palabra clave center. Estos argumentos de palabras clave se aplican a los atributos de pygame.Rectantes de que se devuelva (consulte pygame.Rectpara obtener una lista de los argumentos de palabras clave).
Ver * ¿Por qué mi prueba de colisión siempre devuelve "verdadero" y por qué la posición del rectángulo de la imagen siempre es incorrecta (0, 0)?

No necesita los atributos xand de and en absoluto. Utilice la posición del atributo en su lugar:ySpriteBulletrect

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

Úselo pygame.Rect.colliderect()para detectar colisiones entre instancias de Spritey Bullet.
Vea Cómo detectar colisiones entre dos objetos o imágenes rectangulares en pygame :

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

    if my_sprite.rect.colliderect(my_bullet.rect):
        printe("hit")
Rabbid76 avatar Nov 29 '2020 20:11 Rabbid76

Por lo que entiendo de pygame, solo necesitas verificar si los dos rectángulos se superponen usando el colliderectmétodo. Una forma de hacerlo es tener un método en tu Bulletclase que verifique las colisiones:

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

Entonces puedes llamarlo así:

sprite = Sprite(10, 10, 'my_sprite')
bullet = Bullet(20, 10)
if bullet.is_collided_with(sprite):
    print('collision!')
    bullet.kill()
    sprite.kill()
101 avatar Apr 15 '2015 04:04 101

Existe un método muy simple para lo que intenta hacer utilizando métodos integrados.

Aquí hay un ejemplo.

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()
Nex_Lite avatar Oct 31 '2016 08:10 Nex_Lite