0

im trying to make the enemies in my python arcade game to spawn over time, and progressivley spawn faster, rather than all just spawning at once, cause i want it to be a "how long can you last" kind of game, rather than one with an end goal. i cant figure out how to do this, my only idea is using time.sleep to sleep the program between enemy spawns, but that freezes the whole program instead of just the enemy spawning.

import arcade
import random
import math
import arcade.gui

SPRITE_SCALING = 0.35
SPRITE_SCALING_LASER = 0.8

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

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
        """

        # 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="Start Game", width=200)
        self.v_box.add(start_button.with_space_around(bottom=20))

        settings_button = arcade.gui.UIFlatButton(text="Settings", width=200)
        self.v_box.add(settings_button.with_space_around(bottom=20))

        # Again, method 1. Use a child class to handle events.
        quit_button = QuitButton(text="Quit", 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

        # --- Method 3 for handling click events,
        # use a decorator to handle on_click events
        @settings_button.event("on_click")
        def on_click_settings(event):
            print("Settings:", event)

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

        for i in range(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)

    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

    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()
riley
  • 53
  • 3
  • 1
    You would have to remove your loop that currently draws all the enemies at once and instead track a timer to determine if the desired interval has passed before spawning the next enemy. Does this answer help? https://stackoverflow.com/questions/52917306/i-have-been-trying-to-have-a-new-enemy-spawn-every-20-seconds-using-pygame – nigh_anxiety Jun 30 '22 at 22:35
  • ok, im using a timer to check how long it has been since the start of the program, then if 5 seconds have passed, spawn 5 enemies, then reset the total time. but i am getting this error - line 324, in on_update self.total_time += delta_time AttributeError: 'MyGame' object has no attribute 'total_time – riley Jul 04 '22 at 00:03
  • 1
    You can't use += on a non-initialized variable. So set `self.total_time = 0` before entering your main loop. Probably in MyGame.setup() – nigh_anxiety Jul 04 '22 at 00:23

0 Answers0