Python Forum
[PyGame] Explosion hits enemies, they explode as well
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] Explosion hits enemies, they explode as well
#1
Hello,
I'm currently programming a small game and, apart from 2 topics, I'm almost finished. There is one topic I would like to ask about today. When an enemy is hit by a missile, an explosion animation occurs. I would like the final image of the explosion to collide with other enemies and cause them to explode as well. Unfortunately I haven't been able to fully implement it yet.

In the class Explosion all explosions with the size 1 to 4 are yellow (I use yellow images).
When the rocket Explosion hits some enemies, the enemies shall explode with a red animation. The reason is that you can differentiate the small explosions of the enemies from the big one of the rocket explosion. If they would have all the same color then you perhaps can't notice it.

In the class RocketExplosion I'm trying to check for collisions between the last image of the rocket explosion ("explosion7.png") and the enemies in the update method.

But I think the check for collisions should be in the class Explosion in the for loop (# rocket hits alien). When the rocket hits an alien the big explosion (size 4) is created.
There the picture "explosion7.png" occurs. "explosion7.png" shall be a sprite and at this place the collision detection, should be done.

It would be great, if I could get a hint how this could be done.

Thanks a lot in advance for your help!!
import pygame
from PIL import Image
from pygame import mixer
from pygame.locals import *
import random, math
from itertools import product

pygame.mixer.pre_init(44100, -16, 2, 512)
mixer.init()
pygame.init()
# define fps
clock = pygame.time.Clock()
fps = 60
screen_width = 1280
screen_height = 720
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Space Invaders')
# define fonts
font30 = pygame.font.SysFont('Constantia', 30)
font40 = pygame.font.SysFont('Constantia', 40)

alien_cooldown = 1000  # bullet cooldown in milliseconds
last_count = pygame.time.get_ticks()
last_alien_shot = pygame.time.get_ticks()

# define colours
red = (255, 0, 0)
green = (0, 255, 0)
white = (255, 255, 255)

# load image
bg = pygame.image.load("bg.png")
screen_rect = bg.get_rect()


def draw_bg():
    screen.blit(bg, (0, 0))


# define function for creating text
def draw_text(text, font, text_col, x, y):
    img = font.render(text, True, text_col)
    screen.blit(img, (x, y))


################ space invaders things ###############################################################################
# create spaceship class
class Spaceship(pygame.sprite.Sprite):
    def __init__(self, x, y, health):
        pygame.sprite.Sprite.__init__(self)
        self.x = x
        self.y = y
        self.health_start = health
        self.health_remaining = health
        self.image = pygame.image.load("ship.png")
        self.rect = self.image.get_rect()
        self.rect.center = x, y
        self.xmin = self.rect.width // 2  # Compute Spaceship x range.
        self.xmax = screen_width - self.xmin
        self.last_shot = pygame.time.get_ticks()

    def move(self, xpos):
        self.xpos = xpos
        self.rect.centerx = max(self.xmin, min(self.xmax, xpos))

    def update(self):
        # set a cooldown variable
        cooldown = 500  # milliseconds
        game_over = 0
        # record current time
        time_now = pygame.time.get_ticks()
        # shoot, get key press
        key = pygame.key.get_pressed()
        if key[pygame.K_SPACE] and time_now - self.last_shot > cooldown:
            # single bullet
            single_bullet = SingleBullets(self.rect.centerx, self.rect.top)
            single_bullet_group.add(single_bullet)
            self.last_shot = time_now
        # for later use (double bullets)
        # bullet_1 = Bullets(self.rect.centerx - 43, self.rect.top)
        # bullet_2 = Bullets(self.rect.centerx + 43, self.rect.top)

        # update mask
        self.mask = pygame.mask.from_surface(self.image)

        # draw health bar
        pygame.draw.rect(screen, red, (self.rect.x, (self.rect.bottom + 10), self.rect.width, 15))
        if self.health_remaining > 0:
            pygame.draw.rect(screen, green, (
                self.rect.x, (self.rect.bottom + 10),
                int(self.rect.width * (self.health_remaining / self.health_start)),
                15))
        elif self.health_remaining <= 0:
            explosion = Explosion(self.rect.centerx, self.rect.centery, 3, "yellow")
            explosion_group.add(explosion)
            self.kill()
            game_over = -1
        return game_over


# create player
spaceship = Spaceship(screen_width / 2, screen_height - 100, 3)


# create SingleBullets class
class SingleBullets(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.x = x
        self.y = y
        weapon_list = ["bullet.png", "flash.png", "rocket.png"]
        self.chosen = random.choice(weapon_list)
        self.image = pygame.image.load(self.chosen)
        self.rect = self.image.get_rect()
        self.rect.center = x, y

    def update(self):
        self.rect.y -= 5
        if self.rect.bottom < 0:
            self.kill()
        hits = pygame.sprite.spritecollide(self, alien_group, True, pygame.sprite.collide_mask)
        if hits:
            self.kill()
            for alien in hits:
                x, y = alien.hit()
                if self.chosen == "rocket.png":
                    explosion = Explosion(x, y, 4, "yellow")
                else:
                    explosion = Explosion(x, y, 2, "yellow")
                explosion_group.add(explosion)


# create RocketExplosion class
# for hits between aliens and rocket explosion
class RocketExplosion(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        # this is the last image of the rocket's explosion, it
        self.image = pygame.image.load("explosion7.png")
        self.rect = self.image.get_rect()
        self.rect.center = x, y

    def update(self):
        hits_rocket_expl_and_alien_group = pygame.sprite.spritecollide(self, alien_group, True,
                                                                       pygame.sprite.collide_mask)
        if hits_rocket_expl_and_alien_group:
            for alien in hits_rocket_expl_and_alien_group:
                x, y = alien.hit()
                explosion = Explosion(x, y, 2, "red")
                alien_red_explosion_group.add(explosion)




# create Aliens class
class Aliens(pygame.sprite.Sprite):
    def __init__(self, x, y, move_direction):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("alien" + str(random.randint(1, 6)) + ".png")
        self.x = x
        self.y = y
        self.move_direction = move_direction
        self.rect = self.image.get_rect()
        self.rect.center = x, y

    def hit(self):
        x = self.rect.centerx
        y = self.rect.centery
        self.kill()
        return x, y

    def update(self, move_direction):
        self.rect.x += self.move_direction
        if self.rect.right >= screen_width or self.rect.left <= 0:
            self.move_direction = -self.move_direction
            self.rect.y += 10
        # update mask
        self.mask = pygame.mask.from_surface(self.image)


# create Alien Bullets class
class Alien_Bullets(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("alien_bullet.png")
        self.rect = self.image.get_rect()
        self.rect.center = x, y

    def update(self):
        self.rect.y += 2
        if self.rect.top > screen_height:
            self.kill()
        if pygame.sprite.spritecollide(self, spaceship_group, False, pygame.sprite.collide_mask):
            self.kill()
            # explosion2_fx.play()
            # reduce spaceship health
            spaceship.health_remaining -= 1
            explosion = Explosion(self.rect.centerx, self.rect.centery, 1, "yellow")
            explosion_group.add(explosion)


# create Explosion class
class Explosion(pygame.sprite.Sprite):
    def __init__(self, x, y, size, color):
        pygame.sprite.Sprite.__init__(self)
        self.images = []
        if color == "yellow":
            for num in range(1, 8):
                img = pygame.image.load(f"explosion{num}.png")
                # ship is hit
                if size == 1:
                    img = pygame.transform.scale(img, (20, 20))
                # alien is hit
                if size == 2:
                    img = pygame.transform.scale(img, (100, 100))
                # ship gameover
                if size == 3:
                    img = pygame.transform.scale(img, (160, 160))
                # rocket hits alien
                if size == 4:
                    img = pygame.transform.scale(img, (500, 500))
                # add the image to the list
                self.images.append(img)
                self.index = 0
                self.image = self.images[self.index]
                self.rect = self.image.get_rect()
                self.rect.center = x, y
                self.counter = 0
                # add the image to the list
        if color == "red":
            # rocketExplosion hits alien, it occurs a red explosion
            for num1 in range(1, 8):
                img = pygame.image.load(f"explosion2_{num1}.png")
                img = pygame.transform.scale(img, (100, 100))
                # add the image to the list
                self.images.append(img)
                self.index = 0
                self.image = self.images[self.index]
                self.rect = self.image.get_rect()
                self.rect.center = x, y
                self.counter = 0

    def update(self):
        explosion_speed = 3
        # update explosion animation
        self.counter += 1

        if self.counter >= explosion_speed and self.index < len(self.images) - 1:
            self.counter = 0
            self.index += 1
            self.image = self.images[self.index]

        # if the animation is complete, delete explosion
        if self.index >= len(self.images) - 1 and self.counter >= explosion_speed:
            self.kill()


# create sprite groups
spaceship_group = pygame.sprite.Group()
spaceship_group.add(spaceship)

paddle_group = pygame.sprite.Group()

ball_group = pygame.sprite.Group()

single_bullet_group = pygame.sprite.Group()

flash_group = pygame.sprite.Group()

alien_group = pygame.sprite.Group()

alien_bullet_group = pygame.sprite.Group()

explosion_group = pygame.sprite.Group()

rocket_explosion_group = pygame.sprite.Group()
# a different red explosion
alien_red_explosion_group = pygame.sprite.Group()

lives_group = pygame.sprite.Group()


def create_aliens(rows, cols, move_direction):
    # generate aliens
    for row in range(rows):
        for item in range(cols):
            alien = Aliens(100 + item * 100, 100 + row * 100, move_direction)
            alien_group.add(alien)


def play_space_invaders(level, move_direction):
    game_over = 0
    create_aliens(4, 10, move_direction)
    last_count = pygame.time.get_ticks()
    last_alien_shot = pygame.time.get_ticks()
    countdown = 3
    run = True
    while run:
        clock.tick(fps)
        # draw background
        draw_bg()
        # space invaders single bullets level
        if countdown == 0:
            # create random alien bullets
            # record current time
            time_now = pygame.time.get_ticks()
            # shoot
            if time_now - last_alien_shot > alien_cooldown and len(alien_bullet_group) < 5 and len(
                    alien_group) > 0:
                attacking_alien = random.choice(alien_group.sprites())
                alien_bullet = Alien_Bullets(attacking_alien.rect.centerx, attacking_alien.rect.bottom)
                alien_bullet_group.add(alien_bullet)
                last_alien_shot = time_now

            # check if all the aliens have been killed
            if len(alien_group) == 0:
                game_over = 1
                level += 1
                return level
            if game_over == 0:
                # update spaceship
                game_over = spaceship.update()
                # update sprite groups
                single_bullet_group.update()
                alien_group.update(move_direction)
                alien_bullet_group.update()
                explosion_group.update()
                rocket_explosion_group.update()
                alien_red_explosion_group.update()
            else:
                if game_over == -1:
                    draw_text('GAME OVER!', font40, white, int(screen_width / 2 - 100),
                              int(screen_height / 2 + 50))
                if game_over == 1:
                    draw_text('YOU WIN!', font40, white, int(screen_width / 2 - 100),
                              int(screen_height / 2 + 50))

        if countdown > 0:
            draw_text('GET READY!', font40, white, int(screen_width / 2 - 110), int(screen_height / 2 + 50))
            draw_text(str(countdown), font40, white, int(screen_width / 2 - 10), int(screen_height / 2 + 100))
            count_timer = pygame.time.get_ticks()
            if count_timer - last_count > 1000:
                countdown -= 1
                last_count = count_timer

        # update explosion group
        explosion_group.update()
        rocket_explosion_group.update()
        alien_red_explosion_group.update()
        # draw sprite groups
        spaceship_group.draw(screen)
        single_bullet_group.draw(screen)
        alien_group.draw(screen)
        alien_bullet_group.draw(screen)
        explosion_group.draw(screen)
        rocket_explosion_group.draw(screen)
        alien_red_explosion_group.draw(screen)

        # event handlers
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            elif event.type == pygame.MOUSEMOTION:
                # space invaders level
                spaceship.move(event.pos[0])
        pygame.display.flip()
    pygame.quit()
Reply
#2
I haven't looked through your code but, you could implement a radius around the explosion. Anything within or around the radius could be counted as a hit. I will look through some old code that I wrote for my shmup game. I think I used a radius variable for scoring.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#3
Here is the character class. You can get the whole code from my github or from here.

from modules.mysprites import allsprites
from modules.armory import Missile, Laser
from modules.effects import Explosion
import pygame, os
from random import choice, randint, randrange

# Create needed sprite groups for collision detections
player_sprite = pygame.sprite.Group()
weapon_sprite = pygame.sprite.Group()
mob_sprite = pygame.sprite.Group()
mob_weapon = pygame.sprite.Group()

class Player(pygame.sprite.Sprite):
    ''' Class for creating the player '''
    def __init__(self, path):
        pygame.sprite.Sprite.__init__(self)
        self.path = path
        img = pygame.image.load(f'{path}/media/images/ships/myship.png')
        self.image = pygame.transform.scale(img, (50,50))
        self.image.set_colorkey('black')
        self.rect = self.image.get_rect()
        self.radius = 25
        self.rect.centerx = 640
        self.rect.bottom = 705
        self.speedx = 0
        self.hidden = False
        self.life = 100
        self.lives = 3
        self.score = 0
        self.type = 'player'
        self.laser_fire = False
        self.laser_timer = None
        self.hide_timer = pygame.time.get_ticks()
        player_sprite.add(self)
        allsprites.add(self)

    def hide(self):
        ''' Method for hiding player when killed '''
        self.hidden = True
        self.hide_timer = pygame.time.get_ticks()
        self.rect.center = (640, 800)

    def missile(self):
        ''' Method for creating the missile when fired '''
        missile = Missile(self.rect.centerx, self.rect.top, self.path)
        weapon_sprite.add(missile)
        allsprites.add(missile)      

    def laser(self):
        ''' Method for firing the laser '''
        laser = Laser(self.rect.centerx, self.rect.top, self.path)
        laser.speedy = -9
        weapon_sprite.add(laser)
        allsprites.add(laser)
        

    def update(self):
        ''' Update all movements '''
        if self.hidden and pygame.time.get_ticks() - self.hide_timer > 1000:
            self.hidden = False
            self.rect.centerx = 640
            self.rect.bottom = 705

        # Movement
        self.speedx = 0
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_LEFT] or keystate[pygame.K_a]:
            self.speedx = -8
        if keystate[pygame.K_RIGHT] or keystate[pygame.K_d]:
            self.speedx = 8

        self.rect.x += self.speedx

        if self.rect.right > 1265:
            self.rect.right = 1265
        if self.rect.left < 15:
            self.rect.left = 15

        # Creates the explosion when player dies
        if self.life <= 0:
            self.explosion = Explosion(self.path, self.rect.center, user=True)

    
class Mob(pygame.sprite.Sprite):
    ''' Class for creating the mobs '''
    def __init__(self, path):
        pygame.sprite.Sprite.__init__(self)
        self.path = path
        excludes = ['myship.png', 'myship2.png']
        ships = os.listdir(f'{path}/media/images/ships')
        ships = [pygame.image.load(f'{path}/media/images/ships/{ship}') \
                 for ship in ships if ship not in excludes]
        
        self.image = pygame.transform.scale(choice(ships), \
                                            (randint(40,50), randint(45,50)))
        
        self.image.set_colorkey('black')
        self.rect = self.image.get_rect()
        self.radius = (self.rect.width * .80)
        self.rect.x = randrange(self.rect.width, (1280 - self.rect.width))
        self.rect.y = randrange(-30, -5)
        self.speedx = randrange(-3, 3)
        self.speedy = randrange(2, 6)

        mob_sprite.add(self)

    def laser(self):
        laser = Laser(self.rect.centerx, self.rect.bottom, self.path)
        laser.speedy = self.speedy + 9
        mob_weapon.add(laser)

    def newmob(self):
        ''' Methos for creating new mobs '''
        mob = Mob(self.path)

    def update(self):
        ''' Method for updating everything '''
        self.rect.x += self.speedx
        self.rect.y += self.speedy

        if self.rect.y  == randrange(100, 500):
            self.laser()

        if self.rect.top > 730 or self.rect.left < 0 or self.rect.right > 1280:
            self.rect.x = randrange(0, 1280-self.rect.width)
            self.rect.y = randrange(-30, -5)
            self.speedx = randrange(-3, 3)
            self.speedy = randrange(5, 8)

        if self.rect.top > 720:
            self.kill()
            self.newmob()
flash77 likes this post
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#4
I think sticking with sprites is the easiest way to go. Expand the rocket explosion sprite size and check for intersection between the explosion sprite and the spaceships.
flash77 likes this post
Reply
#5
Dear menator01 and deanhystad,

thank you very much for your effort and for the code provided!! I think that's great...
But then I preferred to stick with sprites, like deanhystad mentioned.

I managed it...

I customized the Explosion class and the SingleBullets class.

Now I only have 1 big topic and 1 small thing that I need to address. Then I would be done with the game.

I think I'll have to ask another question on this big topic soon, but that remains to be seen...

Thank you for your kind support! Smile Smile

# create SingleBullets class
class SingleBullets(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.x = x
        self.y = y
        weapon_list = ["bullet.png", "flash.png", "rocket.png"]
        self.chosen = random.choice(weapon_list)
        self.image = pygame.image.load(self.chosen)
        self.rect = self.image.get_rect()
        self.rect.center = x, y

    def update(self):
        self.rect.y -= 5
        if self.rect.bottom < 0:
            self.kill()
        hits = pygame.sprite.spritecollide(self, alien_group, True, pygame.sprite.collide_mask)
        if hits:
            self.kill()
            for alien in hits:
                x, y = alien.hit()
                if self.chosen == "rocket.png":
                    explosion = Explosion(x, y, 4, "yellow")
                    explosion_group.add(explosion)
                    hits_rocket_expl_and_aliens = pygame.sprite.spritecollide(explosion, alien_group, True,pygame.sprite.collide_mask)
                    if hits_rocket_expl_and_aliens:
                        for alien in hits_rocket_expl_and_aliens:
                            x, y = alien.hit()
                            explosion_red = Explosion(x, y, 2, "red")
                            explosion_group.add(explosion_red)
                else:
                    explosion = Explosion(x, y, 2, "yellow")
                    explosion_group.add(explosion)
# create Explosion class
class Explosion(pygame.sprite.Sprite):
    def __init__(self, x, y, size, color):
        pygame.sprite.Sprite.__init__(self)
        self.images = []
        if color == "yellow":
            for num in range(1, 8):
                img = pygame.image.load(f"explosion{num}.png")
                # ship is hit
                if size == 1:
                    img = pygame.transform.scale(img, (20, 20))
                # alien is hit
                if size == 2:
                    img = pygame.transform.scale(img, (100, 100))
                # ship gameover
                if size == 3:
                    img = pygame.transform.scale(img, (160, 160))
                # rocket hits alien
                if size == 4:
                    img = pygame.transform.scale(img, (500, 500))
                # add the image to the list
                self.images.append(img)
                self.index = 0
                self.image = self.images[self.index]
                self.rect = self.image.get_rect()
                self.rect.center = x, y
                self.counter = 0
                # add the image to the list
        if color == "red":
            # rocketExplosion hits alien, it occurs a red explosion
            for num1 in range(1, 8):
                img = pygame.image.load(f"explosion2_{num1}.png")
                img = pygame.transform.scale(img, (100, 100))
                # add the image to the list
                self.images.append(img)
                self.index = 0
                self.image = self.images[self.index]
                self.rect = self.image.get_rect()
                self.rect.center = x, y
                self.counter = 0

    def update(self):
        explosion_speed = 3
        # update explosion animation
        self.counter += 1

        if self.counter >= explosion_speed and self.index < len(self.images) - 1:
            self.counter = 0
            self.index += 1
            self.image = self.images[self.index]

        # if the animation is complete, delete explosion
        if self.index >= len(self.images) - 1 and self.counter >= explosion_speed:
            self.kill()
menator01 likes this post
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] explosion-animation hespa 5 4,576 Dec-01-2021, 11:19 AM
Last Post: metulburr
  [PyGame] Sprite Animation to create an explosion Russ_CW 2 4,133 Oct-23-2020, 04:57 PM
Last Post: Russ_CW
  [PyGame] Timing the spawning enemies MichaelEssey 2 4,872 Aug-22-2020, 05:51 AM
Last Post: SheeppOSU
  spawning enemies in pygame Elberg 2 5,845 Mar-05-2020, 09:45 AM
Last Post: Windspar
  [PyGame] multiple hits ! tyler 7 4,183 Jul-03-2018, 12:38 PM
Last Post: metulburr

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020