Exemple #1
0
class GameView(arcade.View):
    """
    Main application class. The game runs because of this class.

    Methods in this class:
    - Constructor (__init__(self))
    - setup(self)
    - on_draw(self)
    - on_key_press(self, key, modifiers)
    - on_key_release(self, key, modifiers)
    - on_update(self, delta_time)
    """

    def __init__(self):
        """establishes the variables and resources that the rest of the code will use"""

        # Call the parent class and set up the window
        super().__init__()

        arcade.set_background_color(arcade.csscolor.CORNFLOWER_BLUE)

        # Set the path to start with this program
        file_path = os.path.dirname(os.path.abspath(__file__))
        os.chdir(file_path)

        # 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
        self.jump_needs_reset = False
        self.game_start_time = time.time()
        self.game_end_time = 0

        # Lists for the game
        self.coin_list = None
        self.player_list = None
        self.enemy_list = None
        self.box_list = None
        self.wall_list = None
        self.key_list = None
        self.bullet_list = None
        self.booster_list = None
        self.finish_list = None
        
        self.my_bullet_list = None
        self.power_up_list = None
        self.dead_list = None
        self.can_shoot = False
        self.character_face_direction = "default"

        self.game_over = False
        #self.moving_platforms_list = None
        
        # Separate variable that holds the player sprite
        self.player_sprite = None

        # Our 'physics' engine
        self.physics_engine = None

        # Used to keep track of our scrolling
        self.view_bottom = 0
        self.view_left = 0
        self.frame_count = 0
        self.end_of_map = 0

        # Keep track of the score
        self.score = 0
        self.kills = 0

        # Load sounds
        self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
        self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")
        self.more_hp_sound = arcade.load_sound(":resources:sounds/upgrade4.wav")
        self.PowerUp_sound = arcade.load_sound(":resources:sounds/upgrade5.wav")
        self.game_over = arcade.load_sound(":resources:sounds/gameover1.wav")
        # self.game_over_sound = arcade.load_sound(":resources:sounds/gameover2.wav")
        self.enemy_dies_sound = arcade.load_sound(":resources:sounds/error5.wav")
        self.you_hurt_sound = arcade.load_sound(":resources:sounds/hurt3.wav")
        self.enemy_shoots_sound = arcade.load_sound(":resources:sounds/jump5.wav")


    def setup(self):
        """ Set up the game here. Call this function to restart the game. 
        Assigns each variable a starting value"""

        # Used to keep track of our scrolling
        self.view_bottom = 0
        self.view_left = 0

        # Keep track of the score
        self.score = 0
        self.kills = 0

        # Create the Sprite lists
        self.player_list = arcade.SpriteList()
        self.background_list = arcade.SpriteList()
        self.wall_list = arcade.SpriteList()
        self.coin_list = arcade.SpriteList()
        self.enemy_list = arcade.SpriteList()
        self.bullet_list = arcade.SpriteList()
        self.finish_list = arcade.SpriteList()
        
        self.my_bullet_list = arcade.SpriteList()
        self.power_up_list = arcade.SpriteList()
        self.dead_list = arcade.SpriteList()
       #  self.moving_platforms_list = arcade.SpriteList()

        # Set up the player, specifically placing it at these coordinates.
        self.player_sprite = Player(max_health= 10)

        self.player_sprite.center_x = CONSTANTS.PLAYER_START_X
        self.player_sprite.center_y = CONSTANTS.PLAYER_START_Y
        self.player_list.append(self.player_sprite)

        self.power_up = arcade.Sprite(":resources:images/items/gemRed.png", 0.5)
        self.power_up.center_x = CONSTANTS.PLAYER_START_X +500  #5250
        self.power_up.center_y = CONSTANTS.PLAYER_START_Y + 2000 #5800
        self.power_up_list.append(self.power_up)        

        # --- Load in a map from the tiled editor ---

        # Name of the layer in the file that has our platforms/walls
        platforms_layer_name = 'Platforms'
        #moving_platforms_layer_name = 'Moving Platforms'

        # Name of the layer that has items for pick-up
        coins_layer_name = 'Coins'

        # Map name
        map_name = f"adventure_level.tmx"
        my_map = arcade.tilemap.read_tmx(map_name)

        # Calculate the right edge of the my_map in pixels
        self.end_of_map = my_map.map_size.width * CONSTANTS.GRID_PIXEL_SIZE

        # -- Platforms
        self.wall_list = arcade.tilemap.process_layer(my_map,
                                                      platforms_layer_name,
                                                      CONSTANTS.TILE_SCALING)

        # -- Background objects
        self.background_list = arcade.tilemap.process_layer(my_map, "Background", CONSTANTS.TILE_SCALING)

        # -- Background objects
        self.ladder_list = arcade.tilemap.process_layer(my_map, "Ladders", CONSTANTS.TILE_SCALING, use_spatial_hash=True)

        # -- Coins
        self.coin_list = arcade.tilemap.process_layer(my_map, coins_layer_name,
                                                      CONSTANTS.TILE_SCALING,
                                                     use_spatial_hash=True)
        self.enemy_list = enemies(arcade.tilemap.process_layer(my_map,"Enemies", CONSTANTS.TILE_SCALING), 3)

        self.finish_list = arcade.tilemap.process_layer(my_map,"Finish", CONSTANTS.TILE_SCALING)

        # Set the background color
        if my_map.background_color:
            arcade.set_background_color(my_map.background_color)


        self.booster_list = arcade.tilemap.process_layer(my_map, "Boosters",
                                                      CONSTANTS.TILE_SCALING,
                                                     use_spatial_hash=True)

        # Create the 'physics engine'
        self.physics_engine = arcade.PhysicsEnginePlatformer(self.player_sprite,
                                                             self.wall_list,
                                                             gravity_constant= CONSTANTS.GRAVITY , 
                                                             ladders=self.ladder_list
                                                             )


    def on_draw(self):
        """ Render the screen. """
        arcade.start_render()

        # Draw our sprites
        
        self.background_list.draw()
        self.wall_list.draw()
        self.ladder_list.draw()
        self.coin_list.draw()
        self.player_list.draw()
        self.player_sprite.draw_health_bar()
        self.player_sprite.draw_health_number()
        self.enemy_list.draw()
        self.bullet_list.draw()
        self.enemy_list.draw_health_bar()
        self.enemy_list.draw_health_number()
        self.booster_list.draw()
        self.finish_list.draw()
        
        self.my_bullet_list.draw()
        self.power_up_list.draw()
        self.dead_list.draw()
        
        # Draw our score on the screen, scrolling it with the viewport
        score_text = f"Score: {self.score}"
        arcade.draw_text(score_text, 10 + self.view_left, 10 + self.view_bottom,
                         arcade.csscolor.BLACK, 18)
    
    def process_keychange(self):
        """
        Called when we change a key up/down or we move on/off a ladder.
        """
        # Process up/down
        if self.up_pressed and not self.down_pressed:
            if self.physics_engine.is_on_ladder():
                self.player_sprite.change_y = CONSTANTS.PLAYER_MOVEMENT_SPEED
            elif self.physics_engine.can_jump(y_distance=10) and not self.jump_needs_reset:
                self.player_sprite.change_y = CONSTANTS.PLAYER_JUMP_SPEED
                self.jump_needs_reset = True
                arcade.play_sound(self.jump_sound)
        elif self.down_pressed and not self.up_pressed:
            if self.physics_engine.is_on_ladder():
                self.player_sprite.change_y = -CONSTANTS.PLAYER_MOVEMENT_SPEED

        # Process up/down when on a ladder and no movement
        if self.physics_engine.is_on_ladder():
            if not self.up_pressed and not self.down_pressed:
                self.player_sprite.change_y = 0
            elif self.up_pressed and self.down_pressed:
                self.player_sprite.change_y = 0
        
        # Process left/right
        if self.right_pressed and not self.left_pressed:
            self.player_sprite.change_x = CONSTANTS.PLAYER_MOVEMENT_SPEED
        elif self.left_pressed and not self.right_pressed:
            self.player_sprite.change_x = -CONSTANTS.PLAYER_MOVEMENT_SPEED
        else:
            self.player_sprite.change_x = 0

    def on_key_press(self, key, modifiers):
        """Called whenever a key is pressed. """

        if key == arcade.key.UP or key == arcade.key.W:
            self.up_pressed = True
            self.character_face_direction = "up"
        elif key == arcade.key.DOWN or key == arcade.key.S:
            self.down_pressed = True
            self.character_face_direction = "down"
        elif key == arcade.key.LEFT or key == arcade.key.A:
            self.left_pressed = True
            self.character_face_direction = "left"
        elif key == arcade.key.RIGHT or key == arcade.key.D:
            self.right_pressed = True
            self.character_face_direction = "right"
        elif key == arcade.key.ESCAPE:
            # pass self, the current view, to preserve this view's state
            pause = PauseView(self)
            self.window.show_view(pause)

        # the player can shoot after they get the power up item and if they press the SPACE bar
        if (key == arcade.key.SPACE) and (self.can_shoot):
            #create and place the bullet
            my_bullet = arcade.Sprite(":resources:images/space_shooter/laserRed01.png")
            my_bullet.center_y = self.player_sprite.center_y - 20

            #set the direction the bullet will go
            if self.character_face_direction == "left":
                my_bullet.center_x = self.player_sprite.center_x - 20
                my_bullet.angle = 90
                my_bullet.change_x =  -CONSTANTS.MY_BULLET_SPEED  
            elif self.character_face_direction == "right":
                my_bullet.center_x = self.player_sprite.center_x + 20
                my_bullet.angle = -90
                my_bullet.change_x =  CONSTANTS.MY_BULLET_SPEED
            elif self.character_face_direction == "up":
                my_bullet.center_x = self.player_sprite.center_x
                my_bullet.angle = 0
                my_bullet.change_x = 0
                my_bullet.change_y = CONSTANTS.MY_BULLET_SPEED
            elif self.character_face_direction == "down":
                my_bullet.center_x = self.player_sprite.center_x
                my_bullet.angle = 180
                my_bullet.change_x = 0
                my_bullet.change_y = -CONSTANTS.MY_BULLET_SPEED

            #add bullet to list so it is drawn when called
            self.my_bullet_list.append(my_bullet)               

        self.process_keychange()

    def on_key_release(self, key, modifiers):
        """Called when the user releases a key. """

        if key == arcade.key.UP or key == arcade.key.W:
            self.up_pressed = False
            self.jump_needs_reset = False
        elif key == arcade.key.DOWN or key == arcade.key.S:
            self.down_pressed = False
        elif key == arcade.key.LEFT or key == arcade.key.A:
            self.left_pressed = False
        elif key == arcade.key.RIGHT or key == arcade.key.D:
            self.right_pressed = False

        self.process_keychange()

    def on_update(self, delta_time):
        """ Movement and game logic. 
        Determines the change that occurs in the game as time progresses and variables change """

        #if player health =0 kill them
        if self.player_sprite.cur_health ==0:     
            death = GameOverView(self)
            self.window.show_view(death)

        if self.player_sprite.center_y < -5:     
            death = GameOverView(self)
            self.window.show_view(death)

        # Move the player with the physics engine
        self.physics_engine.update()
        self.frame_count += 1
        
        self.enemy_list.shoot(self.player_sprite.center_x, self.player_sprite.center_y, self.frame_count, self.bullet_list)

        # Get rid of the bullet when it flies off-screen
        for bullet in self.bullet_list:
            if bullet.top < 0:
                bullet.remove_from_sprite_lists()

            # Check this bullet to see if it hit a wall
            wallhit_list = arcade.check_for_collision_with_list(bullet, self.wall_list)

            # If it did, get rid of the bullet
            if len(wallhit_list) > 0:
                bullet.remove_from_sprite_lists()

            #Check this bullet to see if it hit at grave stone
            gravehit_list = arcade.check_for_collision_with_list(bullet, self.dead_list)
            if len(gravehit_list) > 0:
                bullet.remove_from_sprite_lists()


            playerhit_list = arcade.check_for_collision_with_list(bullet, self.player_list)

            # If it did, get rid of the bullet
            if len(playerhit_list) > 0:
                bullet.remove_from_sprite_lists()

            # For every coin we hit, add to the score and remove the coin
            if arcade.check_for_collision(bullet, self.player_sprite):
                
                self.player_sprite.cur_health -= 1
                arcade.play_sound(self.you_hurt_sound)
                

            # If the bullet flies off-screen, remove it.
            if bullet.top < 0 or bullet.right < 0:
                bullet.remove_from_sprite_lists()

        for my_bullet in self.my_bullet_list:
            if my_bullet.top < 0:
                my_bullet.remove_from_sprite_lists()

            # Check this bullet to see if it hit a wall
            wallhit_list2 = arcade.check_for_collision_with_list(my_bullet, self.wall_list)
            # If it did, get rid of the bullet
            if len(wallhit_list2) > 0:
                my_bullet.remove_from_sprite_lists()

            #Check this bullet to see if it hit an enemy
            enemyhit_list = arcade.check_for_collision_with_list(my_bullet, self.enemy_list)

            # If it did, get rid of the bullet
            for enemy in enemyhit_list:
                if (len(enemyhit_list) > 0) and (not enemy.dead):
                    my_bullet.remove_from_sprite_lists()
                    enemy.cur_health -= 1
                    if (enemy.cur_health <=0) and (not enemy.dead):
                        #creates tombstone sprite when enemy dies
                        dead = arcade.Sprite("png/Object/tombstone2.png", 0.3)
                        dead.center_x = enemy.center_x
                        dead.center_y = enemy.center_y+4.5
                        arcade.play_sound(self.enemy_dies_sound)
                        self.dead_list.append(dead)                            
                        #change state of enemy to dead so your bullets pass through 
                        setattr(enemy,'dead',True)
                        self.dead_list.append(enemy)
                        enemy.remove_from_sprite_lists()   
                        del enemy                         
                
            
            # If the bullet flies off-screen, remove it.
            if my_bullet.top < 0 or my_bullet.right < 0:
                my_bullet.remove_from_sprite_lists()

        self.bullet_list.update()
        self.my_bullet_list.update()
        self.enemy_list.update()
        
        #Allow Player to shoot once they touch the power gem
        if arcade.check_for_collision_with_list(self.player_sprite,self.power_up_list):
            self.can_shoot = True
            self.power_up.remove_from_sprite_lists()   
            arcade.play_sound(self.PowerUp_sound)     

        # Update animations
        if self.physics_engine.can_jump():
            self.player_sprite.can_jump = False
        else:
            self.player_sprite.can_jump = True
        
        if self.physics_engine.is_on_ladder() and not self.physics_engine.can_jump():
            self.player_sprite.is_on_ladder = True
            self.process_keychange()
        else:
            self.player_sprite.is_on_ladder = False
            self.process_keychange()
        
        self.coin_list.update_animation(delta_time)
        self.enemy_list.update_animation(delta_time)
        self.background_list.update_animation(delta_time)
        self.player_list.update_animation(delta_time)

        # Update walls, used with moving platforms
        self.wall_list.update()

        # See if the moving wall hit a boundary and needs to reverse direction.
        for wall in self.wall_list:

            if wall.boundary_right and wall.right > wall.boundary_right and wall.change_x > 0:
                wall.change_x *= -1
            if wall.boundary_left and wall.left < wall.boundary_left and wall.change_x < 0:
                wall.change_x *= -1
            if wall.boundary_top and wall.top > wall.boundary_top and wall.change_y > 0:
                wall.change_y *= -1
            if wall.boundary_bottom and wall.bottom < wall.boundary_bottom and wall.change_y < 0:
                wall.change_y *= -1

        # See if we hit any coins
        coin_hit_list = arcade.check_for_collision_with_list(self.player_sprite,
                                                             self.coin_list)

        # Loop through each coin we hit (if any) and remove it
        for coin in coin_hit_list:

            # Figure out how many points this coin is worth
            self.score += 1
            if self.score%50 ==0:
                self.player_sprite.cur_health += 3
                # self.player_sprite.cur_health = self.player_sprite.max_health
                arcade.play_sound(self.more_hp_sound)


            # Remove the coin
            coin.remove_from_sprite_lists()
            arcade.play_sound(self.collect_coin_sound)



        # See if we hit any health boost items
        boost_hit_list = arcade.check_for_collision_with_list(self.player_sprite,
                                                             self.booster_list)

        # Loop through each health_boost we hit (if any) and remove it
        for boost in boost_hit_list:
            hp_diff = self.player_sprite.max_health - self.player_sprite.cur_health
            # how much health do you get
            if hp_diff  >= 3:
                self.player_sprite.cur_health += 3
            else:
                self.player_sprite.cur_health += hp_diff

            # Remove the healt boost item
            boost.remove_from_sprite_lists()
            arcade.play_sound(self.more_hp_sound)

        if arcade.check_for_collision_with_list(self.player_sprite,self.finish_list):
            ## Insert won game screen here!
            self.game_end_time = time.time()
            game_time = self.game_end_time - self.game_start_time
            win = FinishView(self, self.score, len(self.dead_list), game_time)
            self.window.show_view(win)
            

        # Track if we need to change the viewport
        changed_viewport = False

        # --- Manage Scrolling ---

        # Scroll left
        left_boundary = self.view_left + CONSTANTS.LEFT_VIEWPORT_MARGIN
        if self.player_sprite.left < left_boundary:
            self.view_left -= left_boundary - self.player_sprite.left
            changed_viewport = True

        # Scroll right
        right_boundary = self.view_left + CONSTANTS.SCREEN_WIDTH - CONSTANTS.RIGHT_VIEWPORT_MARGIN
        if self.player_sprite.right > right_boundary:
            self.view_left += self.player_sprite.right - right_boundary
            changed_viewport = True

        # Scroll up
        top_boundary = self.view_bottom + CONSTANTS.SCREEN_HEIGHT - CONSTANTS.TOP_VIEWPORT_MARGIN
        if self.player_sprite.top > top_boundary:
            self.view_bottom += self.player_sprite.top - top_boundary
            changed_viewport = True

        # Scroll down
        bottom_boundary = self.view_bottom + CONSTANTS.BOTTOM_VIEWPORT_MARGIN
        if self.player_sprite.bottom < bottom_boundary:
            self.view_bottom -= bottom_boundary - self.player_sprite.bottom
            changed_viewport = True

        if changed_viewport:
            # Only scroll to integers. Otherwise we end up with pixels that
            # don't line up on the screen
            self.view_bottom = int(self.view_bottom)
            self.view_left = int(self.view_left)

            # Do the scrolling
            arcade.set_viewport(self.view_left,
                                CONSTANTS.SCREEN_WIDTH + self.view_left,
                                self.view_bottom,
                                CONSTANTS.SCREEN_HEIGHT + self.view_bottom)
        
        if not self.game_over:
            # Move the enemies
            self.enemy_list.update()

            # Update the player using the physics engine
            self.physics_engine.update()

            # See if the player hit a worm. If so, game over.
            if self.player_sprite.get_death:
                self.game_over = True
                self.player_sprite.remove_from_sprite_lists()