Search code examples
pythonarcade

i am working on a game in python arcade for a school asessment, and i have a timer to spawn enemies over time, it tries to spawn them in menu screen


im working on a python arcade game, where you run around a top-down world, shooting zombies that spawn over time. i am using a timer to spawn enemies every few seconds, where each spawn, one more enemy spawns, and the time between spawns gets lowered by 0.005 seconds. but when im in the menu screen, the timer still goes, and when it tries to spawn an enemy, it doenst work, so the game crashes. how do i fix?

import arcade
import random
import math
import arcade.gui
import time 
import timeit


SPRITE_SCALING = 0.35
SPRITE_SCALING_LASER = 0.8

SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 720
SCREEN_TITLE = "zombier shooter"


BULLET_SPEED = 30
MOVEMENT_SPEED = 5
SPRITE_SPEED = 1


INDICATOR_BAR_OFFSET = 32
ENEMY_ATTACK_COOLDOWN = 1
PLAYER_HEALTH = 5

SCENE_MENU = 'SCENE_MENU'
SCENE_GAME = 'SCENE_GAME'


class QuitButton(arcade.gui.UIFlatButton):
    def on_click(self, event: arcade.gui.UIOnClickEvent):
        arcade.exit()


class Player(arcade.Sprite):

    def update(self):
        """ moves the player """
        # move player.
        self.center_x += self.change_x
        self.center_y += self.change_y

        # check for out of bounds
        if self.left < 0:
            self.left = 0
        elif self.right > SCREEN_WIDTH - 1:
            self.right = SCREEN_WIDTH - 1

        if self.bottom < 0:
            self.bottom = 0
        elif self.top > SCREEN_HEIGHT - 1:
            self.top = SCREEN_HEIGHT - 1


class Enemy(arcade.Sprite):
    """
    This class represents the enemies on our screen.
    """

    def follow_sprite(self, player_sprite):
        """
        This function will move the current sprite towards whatever
        other sprite is specified as a parameter.
        """

        if self.center_y < player_sprite.center_y:
            self.center_y += min(SPRITE_SPEED, player_sprite.center_y - self.center_y)
        elif self.center_y > player_sprite.center_y:
            self.center_y -= min(SPRITE_SPEED, self.center_y - player_sprite.center_y)

        if self.center_x < player_sprite.center_x:
            self.center_x += min(SPRITE_SPEED, player_sprite.center_x - self.center_x)
        elif self.center_x > player_sprite.center_x:
            self.center_x -= min(SPRITE_SPEED, self.center_x - player_sprite.center_x)


class MyGame(arcade.Window):
    """
    main game class
    """

    def __init__(self, width, height, title):
        """
        initialises stuff
        """

        self.enemy_count = 1
        self.enemy_diff = 3

        #setup timer
        self.total_time = 0.0

        # call the parent class initializer
        super().__init__(width, height, title)

        self.scene = SCENE_MENU

        # variables that will hold sprite lists
        self.player_list = None

        # set up the player info
        self.player_sprite = None

        # track the current state of what key is pressed

        self.left_pressed = False

        self.right_pressed = False

        self.up_pressed = False

        self.down_pressed = False

        # --- Required for all code that uses UI element,
        # a UIManager to handle the UI.
        self.manager = arcade.gui.UIManager()
        self.manager.enable()

        # Set background color
        arcade.set_background_color(arcade.color.DARK_BLUE_GRAY)

        # Create a vertical BoxGroup to align buttons
        self.v_box = arcade.gui.UIBoxLayout()

        # Create the buttons
        start_button = arcade.gui.UIFlatButton(text="Try and Survive?", width=200)
        self.v_box.add(start_button.with_space_around(bottom=20))


        # Again, method 1. Use a child class to handle events.
        quit_button = QuitButton(text="Too Scared?", width=200)
        self.v_box.add(quit_button)

        # --- Method 2 for handling click events,
        # assign self.on_click_start as callback
        start_button.on_click = self.on_click_start


        # Create a widget to hold the v_box widget, that will center the buttons
        self.manager.add(
            arcade.gui.UIAnchorWidget(
                anchor_x="center_x",
                anchor_y="center_y",
                child=self.v_box)
        )

        

    def setup(self):
        """ Set up the game and initialize the variables. """

        # sprite lists
        self.player_list = arcade.SpriteList()
        self.enemy_list = arcade.SpriteList()
        self.bullet_list = arcade.SpriteList()

        # setup score
        self.score = 0
        self.score_text = None

        # setup health info
        self.health = 5
        self.health_text = None
        self.dead = None

        # set up the player
        self.player_sprite = Player(":resources:images/animated_characters/female_person/femalePerson_idle.png", SPRITE_SCALING)
        self.player_sprite.center_x = 50
        self.player_sprite.center_y = 50
        self.player_list.append(self.player_sprite)

        

    def on_draw(self):
        """ render the screen. """

        # clear the screen
        self.clear()
        

        if self.scene == SCENE_MENU:
            self.manager.draw()

        elif self.scene == SCENE_GAME:
            # draw all the sprites.
            self.player_list.draw()
            self.enemy_list.draw()
            self.bullet_list.draw()

            # put score text on the screen
            output = f"Score: {self.score}"
            arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)

            # put helth text on the screen
            output = f"Health: {self.health}"
            arcade.draw_text(output, 10, 40, arcade.color.WHITE, 14)

            if self.health <= 0:
                self.player_sprite.remove_from_sprite_lists()
                # put u died text on the screen
                output = f"YOU DIED"
                arcade.draw_text(output, 500, 400, arcade.color.RED, 50)
                output = f"Click to Exit"
                arcade.draw_text(output, 550, 300, arcade.color.BLACK, 30)


    def on_click_start(self, event):
        self.setup()
        self.scene = SCENE_GAME
        self.manager.disable()
        print("Start:", event)

    def on_mouse_press(self, x, y, button, modifiers):
        """ Called whenever the mouse button is clicked. """

        if self.health <= 0:
            exit()

        # create a bullet
        bullet = arcade.Sprite(":resources:images/space_shooter/laserBlue01.png", SPRITE_SCALING_LASER)

        # Position the bullet at the player's current location
        start_x = self.player_sprite.center_x
        start_y = self.player_sprite.center_y
        bullet.center_x = start_x
        bullet.center_y = start_y

        # Get from the mouse the destination location for the bullet
        # IMPORTANT! If you have a scrolling screen, you will also need
        # to add in self.view_bottom and self.view_left.
        dest_x = x
        dest_y = y

        # Do math to calculate how to get the bullet to the destination.
        # Calculation the angle in radians between the start points
        # and end points. This is the angle the bullet will travel.
        x_diff = dest_x - start_x
        y_diff = dest_y - start_y
        angle = math.atan2(y_diff, x_diff)

        # Angle the bullet sprite so it doesn't look like it is flying
        # sideways.
        bullet.angle = math.degrees(angle)
        print(f"Bullet angle: {bullet.angle:.2f}")

        # Taking into account the angle, calculate our change_x
        # and change_y. Velocity is how fast the bullet travels.
        bullet.change_x = math.cos(angle) * BULLET_SPEED
        bullet.change_y = math.sin(angle) * BULLET_SPEED

        # Add the bullet to the appropriate lists
        self.bullet_list.append(bullet)

    def update_player_speed(self):

        # calculate speed based on the keys pressed

        self.player_sprite.change_x = 0

        self.player_sprite.change_y = 0

        if self.up_pressed and not self.down_pressed:

            self.player_sprite.change_y = MOVEMENT_SPEED

        elif self.down_pressed and not self.up_pressed:

            self.player_sprite.change_y = -MOVEMENT_SPEED

        if self.left_pressed and not self.right_pressed:

            self.player_sprite.change_x = -MOVEMENT_SPEED

        elif self.right_pressed and not self.left_pressed:

            self.player_sprite.change_x = MOVEMENT_SPEED

    def on_update(self, delta_time):
        """ updates values n stuff """

        if self.scene == SCENE_GAME:

            # call update to move the sprite
            self.player_list.update()

            # Call update on all sprites
            self.bullet_list.update()

            # go through each bullet
            for bullet in self.bullet_list:

                # check each bullet to see if it hit a zombie
                hit_list = arcade.check_for_collision_with_list(bullet, self.enemy_list)

                # if it did, remove the bullet
                if len(hit_list) > 0:
                    bullet.remove_from_sprite_lists()

                # for each enemy we hit with a bullet, remove enemy and add to the score
                for enemy in hit_list:
                    enemy.remove_from_sprite_lists()
                    self.score += 1

                # if bullet goes off screen, then remove it
                if bullet.bottom > self.width or bullet.top < 0 or bullet.right < 0 or bullet.left > self.width:
                    bullet.remove_from_sprite_lists()

            for enemy in self.enemy_list:
                Enemy.follow_sprite(enemy, self.player_sprite)

            # create a list of all sprites that had a collision with the player.
            hit_list = arcade.check_for_collision_with_list(self.player_sprite, self.enemy_list)

            # go through each sprite, if it got hit, then remove the sprite and lower score and health
            for enemy in hit_list:
                enemy.remove_from_sprite_lists()
                self.score -= 1
                self.health -= 1

        # Accumulate the total time
        self.total_time += delta_time

        # Calculate minutes
        minutes = int(self.total_time) // 60

        # Calculate seconds by using a modulus (remainder)
        seconds = int(self.total_time) % 60

        # Calculate 100s of a second
        seconds_100s = int((self.total_time - seconds) * 100)


        if self.total_time > self.enemy_diff:
            for i in range(self.enemy_count):
                # enemy texture
                enemy = arcade.Sprite(":resources:images/animated_characters/zombie/zombie_idle.png", SPRITE_SCALING)

                enemy.center_x = random.randrange(SCREEN_WIDTH)
                enemy.center_y = random.randrange(SCREEN_HEIGHT)

                self.enemy_list.append(enemy)
            self.total_time = 0.0
            self.enemy_diff -= 0.005
            self.enemy_count += 1
            

    def on_key_press(self, key, modifiers):

        """called when user presses a key. """

        if key == arcade.key.UP:

            self.up_pressed = True

            self.update_player_speed()

        elif key == arcade.key.DOWN:

            self.down_pressed = True

            self.update_player_speed()

        elif key == arcade.key.LEFT:

            self.left_pressed = True

            self.update_player_speed()

        elif key == arcade.key.RIGHT:

            self.right_pressed = True

            self.update_player_speed()

    def on_key_release(self, key, modifiers):

        """called when user releases a key. """

        if key == arcade.key.UP:

            self.up_pressed = False

            self.update_player_speed()

        elif key == arcade.key.DOWN:

            self.down_pressed = False

            self.update_player_speed()

        elif key == arcade.key.LEFT:

            self.left_pressed = False

            self.update_player_speed()

        elif key == arcade.key.RIGHT:

            self.right_pressed = False

            self.update_player_speed()


def main():
    """ Main function """
    MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
    arcade.run()


if __name__ == "__main__":
    main()

Solution

  • Within your on_update() method move this if block

            if self.total_time > self.enemy_diff:
                for i in range(self.enemy_count):
                    # enemy texture
                    enemy = arcade.Sprite(":resources:images/animated_characters/zombie/zombie_idle.png", SPRITE_SCALING)
    
                    enemy.center_x = random.randrange(SCREEN_WIDTH)
                    enemy.center_y = random.randrange(SCREEN_HEIGHT)
    
                    self.enemy_list.append(enemy)
                self.total_time = 0.0
                self.enemy_diff -= 0.005
                self.enemy_count += 1
    
    

    to be inside the if self.scene == SCENE_GAME: block. Then it won't try to draw enemies while you're in SCENE_MENU.