Python Forum
[PyGame] explosion-animation
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] explosion-animation
#1
Dear forum members,
I have worked through the pygame tutorial by metulburr (without
state maschines) and now wanted to add a nice explosion to the laser (player)-enemy-collision.
First: I use the code of the tutorial and for the explosion the class of the thread of Russ_CW.
Problem: I try frame-based and time-based animation.
The program runs perfectly when sprite groups are used.
However, my goal was to get by without sprite groups:
In this case, the animation - both time-based and frame-based - runs so fast that it appears as a single frame.
What am I doing wrong and where is my thinking error?
I would be grateful for help and suggestions

code:
import pygame
import math

pygame.init()

screen = pygame.display.set_mode((800,600))
screen_rect = screen.get_rect()

BLACK = (0, 0, 0)
FPS  = 60
   
def enemy_image_load():
    image = pygame.image.load('data/enemy.png').convert()
    image.set_colorkey((255,0,255))
    transformed_image = pygame.transform.rotate(image, 180)
    orig_image = pygame.transform.scale(transformed_image, (40,80))
    mask = pygame.mask.from_surface(orig_image)
    return (orig_image, mask)
    

class Enemy:
    def __init__(self, images, screen_rect):
        self.screen_rect = screen_rect
        self.image = images[0]
        self.mask = images[1]
        start_buffer = 0
        self.rect = self.image.get_rect(
            center=(screen_rect.centerx, screen_rect.centery + start_buffer)
        )
        self.distance_above_player = 100 
        self.speed = 2
        self.bullet_color = (255,0,0)
        self.is_hit = False
        self.range_to_fire = False
        self.timer = 0.0
        self.bullets = [ ]
        self.dead = False
           
    def pos_towards_player(self, player_rect):
        c = math.sqrt((player_rect.x - self.rect.x) ** 2 + (player_rect.y - self.distance_above_player  - self.rect.y) ** 2)
        try:
            x = (player_rect.x - self.rect.x) / c
            y = ((player_rect.y - self.distance_above_player)  - self.rect.y) / c
        except ZeroDivisionError: 
            return False
        return (x,y)
           
    def update(self, dt, player):
        new_pos = self.pos_towards_player(player.rect)
        if new_pos: #if not ZeroDivisonError
            self.rect.x, self.rect.y = (self.rect.x + new_pos[0] * self.speed, self.rect.y + new_pos[1] * self.speed)
           
        self.check_attack_ability(player)
        if self.range_to_fire:  
            if pygame.time.get_ticks() - self.timer > 1500.0:
                self.timer = pygame.time.get_ticks()
                self.bullets.append(Laser(self.rect.center, self.bullet_color))
                   
        self.update_bullets(player)
                   
    def draw(self, surf):
        if self.bullets:
            for bullet in self.bullets:
                surf.blit(bullet.image, bullet.rect)
        surf.blit(self.image, self.rect)
           
    def check_attack_ability(self, player):
        #if player is lower than enemy
        if player.rect.y >= self.rect.y: 
            try:
                offset_x =  self.rect.x - player.rect.x
                offset_y =  self.rect.y - player.rect.y
                d = int(math.degrees(math.atan(offset_x / offset_y)))
            except ZeroDivisionError: #player is above enemy
                return
            #if player is within 15 degrees lower of enemy
            if math.fabs(d) <= 15: 
                self.range_to_fire = True
            else:
                self.range_to_fire = False
                   
    def update_bullets(self, player):
        if self.bullets:
            for obj in self.bullets[:]:
                obj.update('down')
                #check collision
                if obj.rect.colliderect(player.rect):
                    offset_x =  obj.rect.x - player.rect.x 
                    offset_y =  obj.rect.y - player.rect.y
                    if player.mask.overlap(obj.mask, (offset_x, offset_y)):
                        player.take_damage(1)
                        self.bullets.remove(obj)
           
class Laser:
    def __init__(self, loc, screen_rect):
        self.screen_rect = screen_rect
        self.image = pygame.Surface((5,40)).convert_alpha()
        #self.image.set_colorkey((255,0,255))
        self.mask = pygame.mask.from_surface(self.image)
        self.image.fill((255,255,0))
        self.rect = self.image.get_rect(center=loc)
        self.speed = 5
     
    def update(self,direction='up'):
        if direction == 'down':
            self.rect.y += self.speed
        else:
            self.rect.y -= self.speed
     
    def render(self, surf):
        surf.blit(self.image, self.rect)
     
class Player:
    def __init__(self, screen_rect):
        self.screen_rect = screen_rect
        self.image = pygame.image.load('data/spaceship.png').convert()
        self.image.set_colorkey((255,0,255))
        self.mask = pygame.mask.from_surface(self.image)
        self.transformed_image = pygame.transform.rotate(self.image, 180)
        self.transformed_image = pygame.transform.scale(self.transformed_image, (63,108))
        start_buffer = 300
        self.rect = self.image.get_rect(
            center=(screen_rect.centerx, screen_rect.centery + start_buffer)
        )
        self.dx = 300
        self.dy = 300
        self.lasers = []
        self.timer = 0.0
        self.laser_delay = 500
        self.add_laser = False
        self.damage = 10
         
    def take_damage(self, value):
        self.damage -= value
     
    def get_event(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                if self.add_laser:
                    self.lasers.append(Laser(self.rect.center, self.screen_rect))
                    self.add_laser = False
     
    def update(self, keys, dt, enemies):
        self.rect.clamp_ip(self.screen_rect)
        if keys[pygame.K_LEFT]:
            self.rect.x -= self.dx * dt
        if keys[pygame.K_RIGHT]:
            self.rect.x += self.dx * dt
        if keys[pygame.K_UP]:
            self.rect.y -= self.dy * dt
        if keys[pygame.K_DOWN]:
            self.rect.y += self.dy * dt
        if pygame.time.get_ticks() - self.timer > self.laser_delay:
            self.timer = pygame.time.get_ticks()
            self.add_laser = True
        self.check_laser_collision(enemies)
         
    def check_laser_collision(self, enemies):
        for laser in self.lasers[:]:
            laser.update()
            for e in enemies:
                if laser.rect.colliderect(e.rect):
                    offset_x =  laser.rect.x - e.rect.x 
                    offset_y =  laser.rect.y - e.rect.y
                    if e.mask.overlap(laser.mask, (offset_x, offset_y)):
                        # alternativ-Code without spritegroup
                        # time_based
                        #expl = Explosion(e.rect.centerx, e.rect.centery)
                        #expl.update(delta_time)
                        #expl.draw()
                        # frame_based
                        #expl = Explosion(e.rect.centerx, e.rect.centery)
                        #expl.update()
                        #expl.draw()
                        
                        # with Spritegroup
                        expl = Explosion(e.rect.centerx, e.rect.centery)
                        explosion_group.add(expl)
                        
                        e.dead = True
                        self.lasers.remove(laser)
             
    def draw(self, surf):
        for laser in self.lasers:
            laser.render(surf)
        surf.blit(self.transformed_image, self.rect)
        
        
class Explosion(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.images = []
        for num in range(1, 6):
            img = pygame.image.load(f"img/exp{num}.png")
            img = pygame.transform.scale(img, (120, 120))
            self.images.append(img)
        
        self.timer = None
        self.index = 0
        self.image = self.images[self.index]
        self.rect = self.image.get_rect()
        self.rect.center = [x, y]
        
        self.animation_time =  .05
        self.current_time = 0
        
        self.animation_frames = 6
        self.current_frame = 0

    #def update(self):
        #  frame_based 
        #explosion_speed = 4 
        #self.current_frame += 1
        #if self.current_frame >= explosion_speed and self.index < len(self.images) - 1:
        #    self.current_frame = 0
        #    self.index += 1
        #    self.image = self.images[self.index]
        #if self.index >= len(self.images) - 1 and self.current_frame >= explosion_speed:
        #    self.kill()
    
    def update(self, delta_time):
        
        # time_based
        self.current_time += delta_time
        if self.current_time >= self.animation_time and self.index < len(self.images)-1:
            self.current_time = 0
            self.index += 1
            self.image = self.images[self.index]

        if self.index >= len(self.images)-1 and self.current_time >= self.animation_time:
            self.kill()
     def draw(self):
        screen.blit(self.image, self.rect)

screen = pygame.display.set_mode((800,600))
screen_rect = screen.get_rect()
player = Player(screen_rect)
ENEMY_IMAGE = enemy_image_load()
enemies = []
enemies.append(Enemy(ENEMY_IMAGE, screen_rect))
explosion_group = pygame.sprite.Group()

clock = pygame.time.Clock()
done = False

while not done:
    #clock.tick(FPS)
    keys = pygame.key.get_pressed()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        player.get_event(event)
    screen.fill((0,0,0))
    
    delta_time = clock.tick(60)/1000.0
    explosion_group.draw(screen)
    # frame_based
    #explosion_group.update()
    # time_based
    explosion_group.update(delta_time)
     player.update(keys, delta_time, enemies)
    for e in enemies[:]:
        e.update(delta_time, player)
        if e.dead:
            enemies.remove(e) # remove: Listenmethode
        e.draw(screen)
    player.draw(screen)
    pygame.display.update()
Reply


Messages In This Thread
explosion-animation - by hespa - Nov-30-2021, 07:07 PM
RE: explosion-animation - by BashBedlam - Nov-30-2021, 08:12 PM
RE: explosion-animation - by hespa - Nov-30-2021, 11:00 PM
RE: explosion-animation - by metulburr - Dec-01-2021, 01:10 AM
RE: explosion-animation - by hespa - Dec-01-2021, 08:43 AM
RE: explosion-animation - by metulburr - Dec-01-2021, 11:19 AM

Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] Sprite Animation to create an explosion Russ_CW 2 4,071 Oct-23-2020, 04:57 PM
Last Post: Russ_CW

Forum Jump:

User Panel Messages

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