¿Cómo detecto una colisión en pygame?
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))
En PyGame, la detección de colisiones se realiza mediante pygame.Rect
objetos. El Rect
objeto 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
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.collidelist
y pygame.Rect.collidelistall
se puede utilizar para la prueba de colisión entre un rectángulo y una lista de rectángulos. pygame.Rect.collidedict
y pygame.Rect.collidedictall
puede usarse para la prueba de colisión entre un rectángulo y un diccionario de rectángulos.
La colisión de objetos pygame.sprite.Sprite
y pygame.sprite.Group
puede 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 collided
argumento:
El argumento colisionado es una función de devolución de llamada que se utiliza para calcular si dos sprites chocan.
Los posibles collided
invocables 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.Rect
antes de que se devuelva (consulte pygame.Rect
para 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 x
and de and en absoluto. Utilice la posición del atributo en su lugar: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)
Úselo pygame.Rect.colliderect()
para detectar colisiones entre instancias de Sprite
y 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")
Por lo que entiendo de pygame, solo necesitas verificar si los dos rectángulos se superponen usando el colliderect
método. Una forma de hacerlo es tener un método en tu Bullet
clase 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()
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()