Detect and destroy colliding objects

8

This is the code:

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.ImageS = pygame.image.load("sprites/disparo1.png")
        self.ImageSnake = pygame.image.load("sprites/cazador1 der.png") 


        self.rect = self.ImageSnake.get_rect()        
        self.rectS = self.ImageS.get_rect()

        self.rect.centerx = 80
        self.rect.centery = 80


        self.ShotList= []

        self.vel = 1
        self.velS = 5

        self.vidas = 3
        self.viviendo = True

        self.AS = False

    def draw(self, ventana):
        ventana.blit(self.ImageSnake, self.rect)

    def move(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     

    def shoot(self, ventana):
        keys = pygame.key.get_pressed() 
        if keys[K_SPACE] and not self.AS:         
           self.AS = True                       
           self.rectS.right = self.rect.right + 10 
           self.rectS.top = self.rect.centery     
        if self.AS:                     
           self.rectS.left = self.rectS.left + self.velS         
           if self.rectS.right > 1280:    
              self.AS = False         

    def drawShoot(self, ventana):
        if self.AS:                      
           ventana.blit(self.ImageS, self.rectS)  
           ventana.blit(self.ImageSnake, self.rect)
           pygame.display.flip()

class zombieN1(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)

        self.ImageEnemy1 = pygame.image.load("sprites/zombie1 izq.png")      
        self.rect = self.ImageEnemy1.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.viviendo = True
        self.vidas = 1 

    def movement(self):
        self.rect.left = self.rect.left - self.vel



    def destroy(self):
        pass

    def draw(self, ventana):
        ventana.blit(self.ImageEnemy1, self.rect)





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()
BG = pygame.image.load("sprites/fondo.png")

InGame = True
ronda1 = True


while True:

    Enemy1.destroy()

    for event in pygame.event.get():
        if event.type == QUIT:
           pygame.quit()
           sys.exit()

    if InGame == True:
        Player.move()  
        Player.shoot(ventana)

    ventana.fill((205,69,159))

    if ronda1 == True:
       Enemy1.draw(ventana)
       Enemy1.movement()
       Enemy1.follow()



    Player.draw(ventana)
    Player.drawShoot(ventana)


    pygame.display.update()


Game()

zombieN1 moves constantly to the left as seen in its def movement() . I want that when zombieN1 collides with the disparo(rectS) of Snake , zombieN1 is destroyed. Could any of you help me? If you need more information or explain me better, tell me. Use Python 3.6.3.

    
asked by ElAlien123 03.01.2018 в 22:42
source

1 answer

12

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

    
answered by 04.01.2018 / 00:45
source