Nov-30-2021, 07:07 PM
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:
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()