Step 13 - More Types of Layers#

For this example, we’ll switch to a different built-in map that has more layers we can do things with.

In our setup function, load this map instead of the one from Chapter 12:

self.tile_map = arcade.load_tilemap(":resources:tiled_maps/map2_level_1.json", scaling=TILE_SCALING, layer_options=layer_options)

You can run this and check out the map we will be working with this chapter. You’ll notice in addition to the normal platforms and coins we’ve had. We now have some extra signs and decoration objects, as well as a pit of lava.

Back in chapter 6 we made use of our setup function to reset the game. Let’s go ahead and use that system here to reset the game when the player touches the lava pit. You can remove the section for resetting when the Escape key is pressed if you want, or you can leave it in place. We can also play a game over sound when this happens.

Let’s first add a new sound to our __init__ function for this:

self.gameover_sound = arcade.load_sound(":resources:sounds/gameover1.wav")

In order to do this, we’ll add this code in our on_update function:

if arcade.check_for_collision_with_list(
    self.player_sprite, self.scene["Don't Touch"]
):
    arcade.play_sound(self.gameover_sound)
    self.setup()

The map we are using here has some extra layers in it we haven’t used yet. In the code above we made use of the Don't Touch to reset the game when the player touches it. In this section we will make use of two other layers in our new map, Background and Foreground.

We will use these layers as a way to separate objects that should be drawn in front of our player, and objects that should be drawn behind the player. In our setup function, before we create the player sprite, add this code.

self.scene.add_sprite_list_after("Player", "Foreground")

This code will cause our player spritelist to be inserted at a specific point in the Scene. Causing spritelists which are in front of it to be drawn before it, and ones behind it to be drawn after. By doing this we can make objects appear to be in front of or behind our player like the images below:

../../_images/13_foreground.png ../../_images/13_background.png

Source Code#

More Layers#
  1"""
  2Platformer Game
  3
  4python -m arcade.examples.platform_tutorial.13_more_layers
  5"""
  6import arcade
  7
  8# Constants
  9SCREEN_WIDTH = 800
 10SCREEN_HEIGHT = 600
 11SCREEN_TITLE = "Platformer"
 12
 13# Constants used to scale our sprites from their original size
 14TILE_SCALING = 0.5
 15COIN_SCALING = 0.5
 16
 17# Movement speed of player, in pixels per frame
 18PLAYER_MOVEMENT_SPEED = 5
 19GRAVITY = 1
 20PLAYER_JUMP_SPEED = 20
 21
 22
 23class MyGame(arcade.Window):
 24    """
 25    Main application class.
 26    """
 27
 28    def __init__(self):
 29
 30        # Call the parent class and set up the window
 31        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
 32
 33        # Variable to hold our texture for our player
 34        self.player_texture = None
 35
 36        # Separate variable that holds the player sprite
 37        self.player_sprite = None
 38
 39        # Variable to hold our Tiled Map
 40        self.tile_map = None
 41
 42        # Replacing all of our SpriteLists with a Scene variable
 43        self.scene = None
 44
 45        # A variable to store our camera object
 46        self.camera = None
 47
 48        # A variable to store our gui camera object
 49        self.gui_camera = None
 50
 51        # This variable will store our score as an integer.
 52        self.score = 0
 53
 54        # This variable will store the text for score that we will draw to the screen.
 55        self.score_text = None
 56
 57        # Load sounds
 58        self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
 59        self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")
 60        self.gameover_sound = arcade.load_sound(":resources:sounds/gameover1.wav")
 61
 62    def setup(self):
 63        """Set up the game here. Call this function to restart the game."""
 64        layer_options = {
 65            "Platforms": {
 66                "use_spatial_hash": True
 67            }
 68        }
 69
 70        # Load our TileMap
 71        self.tile_map = arcade.load_tilemap(":resources:tiled_maps/map2_level_1.json", scaling=TILE_SCALING, layer_options=layer_options)
 72
 73        # Create our Scene Based on the TileMap
 74        self.scene = arcade.Scene.from_tilemap(self.tile_map)
 75
 76        self.player_texture = arcade.load_texture(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png")
 77
 78        # Add Player Spritelist before "Foreground" layer. This will make the foreground
 79        # be drawn after the player, making it appear to be in front of the Player.
 80        # Setting before using scene.add_sprite allows us to define where the SpriteList
 81        # will be in the draw order. If we just use add_sprite, it will be appended to the
 82        # end of the order.
 83        self.scene.add_sprite_list_after("Player", "Foreground")
 84
 85        self.player_sprite = arcade.Sprite(self.player_texture)
 86        self.player_sprite.center_x = 128
 87        self.player_sprite.center_y = 128
 88        self.scene.add_sprite("Player", self.player_sprite)
 89
 90        # Create a Platformer Physics Engine, this will handle moving our
 91        # player as well as collisions between the player sprite and
 92        # whatever SpriteList we specify for the walls.
 93        # It is important to supply static to the walls parameter. There is a
 94        # platforms parameter that is intended for moving platforms.
 95        # If a platform is supposed to move, and is added to the walls list,
 96        # it will not be moved.
 97        self.physics_engine = arcade.PhysicsEnginePlatformer(
 98            self.player_sprite, walls=self.scene["Platforms"], gravity_constant=GRAVITY
 99        )
100
101        # Initialize our camera, setting a viewport the size of our window.
102        self.camera = arcade.SimpleCamera(viewport=(0, 0, self.width, self.height))
103
104        # Initialize our gui camera, initial settings are the same as our world camera.
105        self.gui_camera = arcade.SimpleCamera(viewport=(0, 0, self.width, self.height))
106
107        # Reset our score to 0
108        self.score = 0
109
110        # Initialize our arcade.Text object for score
111        self.score_text = arcade.Text(f"Score: {self.score}", start_x = 0, start_y = 5)
112
113        self.background_color = arcade.csscolor.CORNFLOWER_BLUE
114
115    def on_draw(self):
116        """Render the screen."""
117
118        # Clear the screen to the background color
119        self.clear()
120
121        # Activate our camera before drawing
122        self.camera.use()
123
124        # Draw our Scene
125        self.scene.draw()
126
127        # Activate our GUI camera
128        self.gui_camera.use()
129
130        # Draw our Score
131        self.score_text.draw()
132
133    def on_update(self, delta_time):
134        """Movement and Game Logic"""
135
136        # Move the player using our physics engine
137        self.physics_engine.update()
138
139        # See if we hit any coins
140        coin_hit_list = arcade.check_for_collision_with_list(
141            self.player_sprite, self.scene["Coins"]
142        )
143
144        # Loop through each coin we hit (if any) and remove it
145        for coin in coin_hit_list:
146            # Remove the coin
147            coin.remove_from_sprite_lists()
148            arcade.play_sound(self.collect_coin_sound)
149            self.score += 75
150            self.score_text.text = f"Score: {self.score}"
151
152        if arcade.check_for_collision_with_list(
153            self.player_sprite, self.scene["Don't Touch"]
154        ):
155            arcade.play_sound(self.gameover_sound)
156            self.setup()
157
158        # Center our camera on the player
159        self.camera.center(self.player_sprite.position)
160
161    def on_key_press(self, key, modifiers):
162        """Called whenever a key is pressed."""
163
164        if key == arcade.key.ESCAPE:
165            self.setup()
166
167        if key == arcade.key.UP or key == arcade.key.W:
168            if self.physics_engine.can_jump():
169                self.player_sprite.change_y = PLAYER_JUMP_SPEED
170                arcade.play_sound(self.jump_sound)
171
172        if key == arcade.key.LEFT or key == arcade.key.A:
173            self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
174        elif key == arcade.key.RIGHT or key == arcade.key.D:
175            self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
176
177    def on_key_release(self, key, modifiers):
178        """Called whenever a key is released."""
179
180        if key == arcade.key.LEFT or key == arcade.key.A:
181            self.player_sprite.change_x = 0
182        elif key == arcade.key.RIGHT or key == arcade.key.D:
183            self.player_sprite.change_x = 0
184
185
186def main():
187    """Main function"""
188    window = MyGame()
189    window.setup()
190    arcade.run()
191
192
193if __name__ == "__main__":
194    main()