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