There are methods of the class pygame.sprite
thought to do just that, detect collisions and destroy the sprites that do it. Both pygame.sprite.spritecollide
and pygame.sprite.groupcollide
allow this to be done. The first allows detecting the collision of a sprite with a group of sprites, the second detects collisions between two groups.
Personally I would make two changes to your code, especially if you intend to have multiple "bullets" and multiple "zombies" at the same time on the screen:
-
Your sprite "shot" should have its own class. Video games are one of the reasons why POO exists. Having a class of your own will allow you (in addition to encapsulating the code much better) to handle different number of shots and of different types very easily if you require it, as well as to handle collisions and other events much more easily.
-
Use sprite groups ( pygame.sprite.Group()
). If you create groups to store all the sprites that have the same characteristics (enemies, shots, etc.), the task of detecting collisions is much easier thanks to the methods indicated above.
I ended up changing a lot of things, but an example of what you could do is the following:
import pygame,sys
from pygame.locals import *
from random import randint
ancho = 1280
alto = 720
class Snake(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/cazador1 der.png")
self.rect = self.image.get_rect()
self.rect.centerx = 80
self.rect.centery = 80
self.vel = 1
self.vidas = 3
self.viviendo = True
def update(self):
keys = pygame.key.get_pressed()
if keys[K_DOWN]:
self.rect.y += self.vel
if self.rect.bottom > 720:
self.rect.bottom = 720
if keys[K_UP]:
self.rect.y -= self.vel
if self.rect.top < 0:
self.rect.top = 0
if keys[K_RIGHT]:
self.rect.x += self.vel
if self.rect.right > 1280:
self.rect.right = 1280
if keys[K_LEFT]:
self.rect.x -= self.vel
if self.rect.left < 0:
self.rect.left = 0
if keys[K_SPACE] and not bullets:
bullet = Bullet(self.rect.right, self.rect.centery)
all_sprites.add(bullet)
bullets.add(bullet)
class ZombieN1(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/zombie1 izq.png")
self.rect = self.image.get_rect()
self.s = Snake()
self.y = self.s.rect.top
self.x = randint(1016,1280)
self.rect.top = self.y
self.rect.right = self.x
self.vel = 1
def update(self):
self.rect.left = self.rect.left - self.vel
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/disparo1.png")
self.rect = self.image.get_rect()
self.rect.centery = y
self.rect.centerx = x
self.vel = 3
def update(self):
self.rect.right += self.vel
# Destruir cuando se salga de la ventana (lado derecho)
if self.rect.right > 1280:
self.kill()
all_sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
zombies = pygame.sprite.Group()
def Game():
pygame.init()
pygame.key.set_repeat(1,25)
ventana = pygame.display.set_mode((ancho, alto))
pygame.display.set_caption("Game")
player = Snake()
enemy1 = ZombieN1()
all_sprites.add(enemy1)
zombies.add(enemy1)
all_sprites.add(player)
BG = pygame.image.load("sprites/fondo.png")
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# Comprobamos si algún disparo colisiona con algún enemigo
## En caso afirmativo se destruyen ambos
pygame.sprite.groupcollide(zombies, bullets, True, True)
# Actualizamos todos los sprites del juego
all_sprites.update()
ventana.fill((205,69,159))
# Dibujamos todos los sprites
all_sprites.draw(ventana)
pygame.display.flip()
pygame.quit()
Game()
I used groupcollide
since as a rule you have several enemies and several shots at the same time:
pygame.sprite.groupcollide(zombies, bullets, True, True)
The first two arguments are the two groups of sprites that you must watch for collisions, the next two arguments indicate if when one of each group collides you must destroy them.
Keep in mind that groupcollide
returns a dictionary with the collisions it detects, which you can use to handle them:
colisiones = pygame.sprite.groupcollide(zombies, bullets, True, True)
The colisiones
dictionary will be in the form:
{<zombieN1 sprite(in 0 groups)>: [<Bullet sprite(in 0 groups)>]}
pygame.sprite.collide_rect (sprite1, sprite2)
If you are only going to have one enemy and one shot and you are not going to use groups, you can use something like:
if pygame.sprite.collide_rect(sprite_disparo, sprite_enemigo):
# Lo que quieras hacer, como destruir (sprite_enemigo.kill())
The same idea you can apply with your character and enemies to detect collisions.
With the above code and sprites worthy of a Blender XD master we have the following:
Edit:
If you want an enemy to have several lives you should only create that attribute in your class, then iterate over the dictionary that returns groupcollide
and each zombie subtract a life, calling your method kill
when its life attribute is less or equal to 0.
A very flexible option is to define a health
attribute in the enemy's class and make it an integer. As damage causes you subtract a certain amount of life, causing it to be destroyed when it reaches zero. The beauty of this is that you can use different "weapons" that cause different damage.
I'll give you an example using this idea, in this case the health will be 300 and for each bullet you lose 100. Bullet damage is defined in the attribute damage
of your class. You could have another type of "bullet" that would cause 50 damage, another 10, etc. I use a property
next to a setter
so that it is the object itself that is destroyed when its life reaches zero, the object oriented programming is very nice for these things:)
-
In your class Bullet
add in __init__
the attribute self.damage = 100
-
In class ZombieN1
add the attribute self._health = 300
in its __init__
and then define the property health
and the setter :
@property
def health(self):
return self._health
@health.setter
def health(self, value):
self._health = value
if self._health <= 0:
self.kill()
-
And finally, in while True
change the line with groupcollide
by:
# Comprobamos si algún disparo colisiona con algún enemigo
## En caso afirmativo se destruye la bala y se le resta el daño de esta al zombie
colls = pygame.sprite.groupcollide(zombies, bullets, False, True)
for zombie, bullets_ in colls.items():
zombie.health -= sum(bullet.damage for bullet in bullets_)
Everything would be like this:
import pygame,sys
from pygame.locals import *
from random import randint
ancho = 1280
alto = 720
class Snake(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/cazador1 der.png")
self.rect = self.image.get_rect()
self.rect.centerx = 80
self.rect.centery = 80
self.vel = 1
self.vidas = 3
self.viviendo = True
def update(self):
keys = pygame.key.get_pressed()
if keys[K_DOWN]:
self.rect.y += self.vel
if self.rect.bottom > 720:
self.rect.bottom = 720
if keys[K_UP]:
self.rect.y -= self.vel
if self.rect.top < 0:
self.rect.top = 0
if keys[K_RIGHT]:
self.rect.x += self.vel
if self.rect.right > 1280:
self.rect.right = 1280
if keys[K_LEFT]:
self.rect.x -= self.vel
if self.rect.left < 0:
self.rect.left = 0
if keys[K_SPACE] and not bullets:
bullet = Bullet(self.rect.right, self.rect.centery)
all_sprites.add(bullet)
bullets.add(bullet)
class ZombieN1(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/zombie1 izq.png")
self.rect = self.image.get_rect()
self.s = Snake()
self.y = self.s.rect.top
self.x = randint(1016,1280)
self.rect.top = self.y
self.rect.right = self.x
self.vel = 1
self._health = 300
def update(self):
self.rect.left = self.rect.left - self.vel
@property
def health(self):
return self._health
@health.setter
def health(self, value):
self._health = value
if self._health <= 0:
self.kill()
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("sprites/disparo1.png")
self.rect = self.image.get_rect()
self.rect.centery = y
self.rect.centerx = x
self.vel = 3
self.damage = 100
def update(self):
self.rect.right += self.vel
# Destruir cuando se salga de la ventana (lado derecho)
if self.rect.right > 1280:
self.kill()
all_sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
zombies = pygame.sprite.Group()
def Game():
pygame.init()
pygame.key.set_repeat(1,25)
ventana = pygame.display.set_mode((ancho, alto))
pygame.display.set_caption("Game")
player = Snake()
enemy1 = ZombieN1()
all_sprites.add(enemy1)
zombies.add(enemy1)
all_sprites.add(player)
BG = pygame.image.load("sprites/fondo.png")
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# Comprobamos si algún disparo colisiona con algún enemigo
## En caso afirmativo se destruye la bala y se le resta el daño de esta al zombie
colls = pygame.sprite.groupcollide(zombies, bullets, False, True)
for zombie, bullets_ in colls.items():
zombie.health -= sum(bullet.damage for bullet in bullets_)
# Actualizamos todos los sprites del juego
all_sprites.update()
ventana.fill((205,69,159))
# Dibujamos todos los sprites
all_sprites.draw(ventana)
pygame.display.flip()
pygame.quit()
Game()