May-25-2024, 06:06 PM
Hi,
I managed in my game that the end boss is aiming at the actual player's position with his gun.
I have a question about my class "EndBossGun()" in line 693 to 714 and would like to politely ask for help.
In the __init__ method I set the inital state of the gun. In the aim method the gun rotates.
The problem is that when the gun is rotating the initial position is shown too.
When the player is moving and the end boss is aiming at him, it should be shown the gun's actual position and it shouldn't be shown the inital state too.
You can see the situation in the 2 attached pictures.
The second problem is that the gun display sometimes flickers (when the player moves a bit and is in the screen's corner).
How could this be fixed?
I would like to thank you very much in advance for the help!!
I managed in my game that the end boss is aiming at the actual player's position with his gun.
I have a question about my class "EndBossGun()" in line 693 to 714 and would like to politely ask for help.
In the __init__ method I set the inital state of the gun. In the aim method the gun rotates.
The problem is that when the gun is rotating the initial position is shown too.
When the player is moving and the end boss is aiming at him, it should be shown the gun's actual position and it shouldn't be shown the inital state too.
You can see the situation in the 2 attached pictures.
The second problem is that the gun display sometimes flickers (when the player moves a bit and is in the screen's corner).
How could this be fixed?
I would like to thank you very much in advance for the help!!
import os 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) 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) else: explosion = Explosion(x, y, 2) 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) self.image = pygame.image.load("explosion7.png") self.rect = self.image.get_rect() self.rect.center = x, y def check(self, x, y): hits_rocket_expl_and_alien_group = pygame.sprite.spritecollide(self, alien_group, False, 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_fx.play() explosion = Explosion(x, y, 2) 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) explosion_group.add(explosion) # create Explosion class class Explosion(pygame.sprite.Sprite): def __init__(self, x, y, size): pygame.sprite.Sprite.__init__(self) self.images = [] 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: if num == 7: img = pygame.transform.scale(img, (500, 500)) rocket_Expl = RocketExplosion(x, y) rocket_Expl.check(x, y) # 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() 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() 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() # 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) # 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() # breakout things ############################################################# TEXT_COLOR = (255, 255, 255) FOREGROUND = (0, 0, 0) # Recolor image pixels that are this color TRANSPARENT = (255, 255, 255) # Make image pixels this color transparent BALL_COLOR = (255, 255, 255) PADDLE_COLOR = (255, 255, 255) BALL_IMAGE = "ball.png" PADDLE_IMAGE = "paddle.png" def create_image(file, color=None): """ Create image from a file. If color is specified, replace all FOREGROUND pixels with color pixels. Modify image so TRANSPARENT colored pixels are transparent. """ if color: # Recolor the image image = Image.open(file).convert("RGB") for xy in product(range(image.width), range(image.height)): if image.getpixel(xy) == FOREGROUND: image.putpixel(xy, color) image = pygame.image.fromstring(image.tobytes(), image.size, "RGB") else: image = pygame.image.load(file) image.set_colorkey(TRANSPARENT) return image.convert() class EnhancedSprite(pygame.sprite.Sprite): def __init__(self, image, group=None, **kwargs): super().__init__(**kwargs) self.image = image self.rect = image.get_rect() if group is not None: group.add(self) def at(self, x, y): """Convenience method for setting my position""" self.x = x self.y = y return self # Properties below expose properties of my rectangle so you can use # self.x = 10 or self.centery = 30 instead of self.rect.x = 10 @property def x(self): return self.rect.x @x.setter def x(self, value): self.rect.x = value @property def y(self): return self.rect.y @y.setter def y(self, value): self.rect.y = value @property def centerx(self): return self.rect.centerx @centerx.setter def centerx(self, value): self.rect.centerx = value @property def centery(self): return self.rect.centery @centery.setter def centery(self, value): self.rect.centery = value @property def right(self): return self.rect.right @right.setter def right(self, value): self.rect.right = value @property def bottom(self): return self.rect.bottom @bottom.setter def bottom(self, value): self.rect.bottom = value @property def width(self): return self.rect.width @property def height(self): return self.rect.height class Paddle(EnhancedSprite): """The sprite the player moves around to redirect the ball""" group = pygame.sprite.Group() def __init__(self, x, y, health): super().__init__(create_image(PADDLE_IMAGE, PADDLE_COLOR), self.group) self.x = x self.y = y self.health_start = health self.health_remaining = health self.image = pygame.image.load("paddle.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): """Move to follow the cursor. Clamp to window bounds""" self.rect.centerx = max(self.xmin, min(self.xmax, xpos)) class LifeCounter(): """Keep track of lives count. Display lives remaining using ball image""" def __init__(self, x, y, count=5): self.x, self.y = x, y self.image = create_image(BALL_IMAGE, BALL_COLOR) self.spacing = self.image.get_width() + 5 self.group = pygame.sprite.Group() self.reset(count) def reset(self, count): """Reset number of lives""" self.count = count for c in range(count - 1): EnhancedSprite(self.image, self.group).at(self.x + c * self.spacing, self.y) def __len__(self): """Return number of lives remaining""" return self.count def kill(self): """Reduce number of lives""" if self.count > 1: self.group.sprites()[-1].kill() self.count = max(0, self.count - 1) class Ball(EnhancedSprite): """Ball bounces around colliding with walls, paddles and bricks""" group = pygame.sprite.Group() def __init__(self, paddle, lives, speed=5): super().__init__(create_image(BALL_IMAGE, BALL_COLOR), self.group) self.paddle = paddle self.lives = lives self.speed = speed self.dx = self.dy = 0 self.xfloat = self.yfloat = 0 self.xmax = screen_width - self.rect.width self.ymax = paddle.bottom - self.rect.height self.reset(0) def at(self, x, y): self.xfloat = x self.yfloat = y return super().at(x, y) def reset(self, score=None): """Reset for a new game""" self.active = False if score is not None: self.score = score def start(self): """Start moving the ball in a random direction""" angle = random.random() - 0.5 # Launch angle limited to about +/-60 degrees self.dx = self.speed * math.sin(angle) self.dy = -self.speed * math.cos(angle) self.active = True def move(self): """Update the ball position. Check for collisions with bricks, walls and the paddle""" hit_status = 0 if not self.active: # Sit on top of the paddle self.at(self.paddle.centerx - self.width // 2, self.paddle.y - self.height - 2) return self # Did I hit some bricks? Update the bricks and the score x1, y1 = self.xfloat, self.yfloat x2, y2 = x1 + self.dx, y1 + self.dy if (xhits := pygame.sprite.spritecollide(self.at(x2, y1), alien_group, True, pygame.sprite.collide_mask)): self.dx = -self.dx hit_status += 1 if (yhits := pygame.sprite.spritecollide(self.at(x1, y2), alien_group, True, pygame.sprite.collide_mask)): self.dy = -self.dy hit_status += 2 # hits = pygame.sprite.spritecollide(self, alien_group, True, pygame.sprite.collide_mask) if xhits or yhits: for alien in xhits or yhits: x, y = alien.hit() # explosion_fx.play() explosion = Explosion(x, y, 2) explosion_group.add(explosion) # if (hits := set(xhits) or set(yhits)): # for alien in hits: # self.score += alien.hit() # Did I hit a wall? if x2 <= 0 or x2 >= self.xmax: self.dx = -self.dx hit_status += 4 if y2 <= 0: self.dy = abs(self.dy) hit_status += 8 # Did I get past the paddle? if (y2 >= self.paddle.y) and ((self.x > self.paddle.right) or (self.right < self.paddle.x)): self.lives.kill() self.active = False elif self.dy > 0 and pygame.Rect.colliderect(self.at(x2, y2).rect, self.paddle.rect): # I hit the paddle. Compute angle of reflection bangle = math.atan2(-self.dx, self.dy) # Ball angle of approach pangle = math.atan2(self.centerx - self.paddle.centerx, 30) # Paddle angle rangle = (pangle - bangle) / 2 # Angle of reflection self.dx = math.sin(rangle) * self.speed self.dy = -math.cos(rangle) * self.speed hit_status += 16 if hit_status > 0: self.at(x1, y1) else: self.at(x2, y2) def play_breakout(level, move_direction): game_over = 0 paddle = Paddle(screen_width / 2, screen_height - 100, 3) paddle_group.add(paddle) lives = LifeCounter(10, screen_height - 30) ball = Ball(paddle, lives) ball_group.add(ball) create_aliens(1, 4, 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() # breakout level if countdown == 0: # create random alien bullets # record current time time_now = pygame.time.get_ticks() # in breakout levels aliens shouldn't shoot # check if all the aliens have been killed if len(alien_group) == 0: paddle.kill() ball.kill() game_over = 1 level += 1 return level if game_over == 0: # update paddle game_over = paddle.move(event.pos[0]) # update sprite groups alien_group.update(move_direction) 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 alien_group.update(move_direction) explosion_group.update() paddle_group.update() ball_group.update() # draw sprite groups alien_group.draw(screen) explosion_group.draw(screen) paddle_group.draw(screen) ball_group.draw(screen) # event handlers for event in pygame.event.get(): if event.type == pygame.QUIT: run = False elif event.type == pygame.MOUSEMOTION: # breakout level paddle.move(event.pos[0]) elif event.type == pygame.MOUSEBUTTONUP: if not ball.active: ball.start() ball.move() pygame.display.flip() pygame.quit() ##### end boss things ############################################################## level = 0 gun_original = pygame.image.load("gun3.png").convert_alpha() end_boss_cooldown = 3000 # bullet cooldown in milliseconds # create end boss class class EndBoss(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_4.png") self.rect = self.image.get_rect() self.rect.center = x, y # create end boss class class EndBossGun(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) self.x = x self.y = y self.image = pygame.image.load("gun3.png") self.rect = self.image.get_rect() self.rect.center = x, y self.last_shot = pygame.time.get_ticks() def aim(self, p): # rotate end_boss' gun x = screen_width / 2 y = 195 # calculate gun's angle x_dist = p[0] - x y_dist = -(screen_height - 100 - y) # - because pygame y coordinates increase down the screen angle = math.degrees(math.atan2(y_dist, x_dist)) # rotate gun gun = pygame.transform.rotate(gun_original, angle + 90) gun_rect = gun.get_rect(center=(x, y)) screen.blit(gun, gun_rect) end_boss = EndBoss(screen_width / 2, 195, 300) end_boss_gun = EndBossGun(screen_width / 2, 195) end_boss_group = pygame.sprite.Group() end_boss_group.add(end_boss) end_boss_gun_group = pygame.sprite.Group() end_boss_gun_group.add(end_boss_gun) end_boss_bullet_group = pygame.sprite.Group() def play_end_boss_level(): game_over = 0 last_count = pygame.time.get_ticks() last_end_boss_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_end_boss_shot > end_boss_cooldown and len(end_boss_bullet_group) < 5 and len( # end_boss_group) > 0: # # end_boss_bullet_1, end_boss_bullet_2 # end_boss_bullet_1 = Alien_Bullets(end_boss_gun.rect.centerx - 4, end_boss_gun.rect.bottom) # end_boss_bullet_2 = Alien_Bullets(end_boss_gun.rect.centerx + 4, end_boss_gun.rect.bottom) # end_boss_bullet_group.add(end_boss_bullet_1, end_boss_bullet_2) # last_end_boss_shot = time_now # check if all the aliens have been killed if len(end_boss_group) == 0: game_over = 1 if game_over == 0: # update spaceship game_over = spaceship.update() # update sprite groups single_bullet_group.update() end_boss_group.update() end_boss_bullet_group.update() 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() # 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) end_boss_group.draw(screen) end_boss_gun_group.draw(screen) end_boss_bullet_group.draw(screen) # event handlers for event in pygame.event.get(): if event.type == pygame.QUIT: run = False elif event.type == pygame.MOUSEMOTION: # end_boss level spaceship.move(event.pos[0]) end_boss_gun.aim(event.pos) pygame.display.flip() pygame.quit() #################################################################################### # game_over = 0 # 0 is no game over, 1 means player has won, -1 means player has lost space_invaders_levels = [1, 3, 5] breakout_levels = [2, 4] end_boss_level = [6] level = 6 move_direction = 1 play_end_boss_level() # for i in range(1, 6): # if level not in breakout_levels: # level = play_space_invaders(level, move_direction) # else: # level = play_breakout(level, move_direction) # if level in end_boss_level: # play_end_boss_level()