Python Forum
[PyGame] end boss aiming at player
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] end boss aiming at player
#1
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!!

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

Attached Files

Thumbnail(s)
       
Reply
#2
EndBossGun.aim() should not blit image to screen. That is done by endBossGunGroup.draw(). aim() should rotate self.image in EndBossGun.
        # rotate gun
        self.image = pygame.transform.rotate(gun_original, angle + 90)
The EndBossGun should keep a copy of the original image instead of using a global variable.
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.gun_image = pygame.image.load("gun3.png")
        self.rect = self.image.get_rect()
        self.rect.center = x, y
        self.last_shot = pygame.time.get_ticks()
flash77 and XavierPlatinum like this post
Reply
#3
Dear deanhystad,

thank you very much, it works...

Did I implement it right?

In line 37 I used self.gun_image.
(When I use self.image it occurs a pygame error out of memory.)

Is the use of line 39 correct?

level = 0
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.gun_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
        # calculate gun's angle
        x_dist = p - self.x
        y_dist = -(screen_height - 100 - self.y)
        angle = math.degrees(math.atan2(y_dist, x_dist))
        # rotate gun
        self.image = pygame.transform.rotate(self.gun_image, angle + 90)
        self.rect = self.image.get_rect()
        self.rect.center = self.x, self.y


end_boss = EndBoss(screen_width / 2, 195, 300)
end_boss_gun = EndBossGun(screen_width / 2, 195)
Reply
#4
Perhaps these couple changes will help the memory problem. You are loading the image twice. Try:
  self.gun_image = pygame.image.load("gun3.png")
  self.image = self.gun_image.copy()
      


 self.image = pygame.transform.rotate(self.gun_image.copy(), angle + 90)
I think the way you had it, you were constantly rotating the same image, which quickly causes problems. The solution is to make a fresh copy of the image every time, and rotate that copy to the correct angle.
flash77 likes this post
Reply
#5
Rotating an image over and over results in an ever growing image. This is because images are rectangles aligned with the screen x, y grid. Rotating the image makes the corners go outside the current bounds, and the image has to grow.

If you want to rotate an image, keep an unrotated copy to use as the argument for rotate(). This image is always the same size, limiting the size of the rotated image.

There is no reason to call copy(). Rotate leaves the source image unaltered, returning a new image
flash77 likes this post
Reply
#6
Hi,

Thanks to you both for your answers!

I didn't know about the described problem when rotating the same image again and again.

Now I was able to fix the problem, thanks again...
Reply
#7
Quote:I didn't know about the described problem when rotating the same image again and again.
This is why you use your head. Take a post-it note or recipe card and place it on a piece of paper. Draw an outline around the note using a pencil, rotate 45 degrees and draw again using a pen. Are there parts of the pen outline that fit outside the pencil outline? Keeping parallel with lines for the pencil outline draw a rectangle that completely encompasses the pen outline. See how it is larger than the pencil outline. Imagine doing this over and over and how quickly the outline would grow.

Just because you are writing code doesn't mean you should only be thinking about code. Try to make mental models, or even simple physical models of what you are doing to help understand. The more you know about the problem you are trying to solve, the better your solution will be.
flash77 likes this post
Reply
#8
Quote:Just because you are writing code doesn't mean you should only be thinking about code. Try to make mental models, or even simple physical models of what you are doing to help understand. The more you know about the problem you are trying to solve, the better your solution will be.

That's right, I often use models... But in this case I didn't...
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] Game boss causing huge slowdown michael1789 21 7,916 Mar-09-2020, 02:07 AM
Last Post: michael1789

Forum Jump:

User Panel Messages

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