# Step 10 - Multiple Levels and Other Layers#

Now that we’ve seen the basics of loading a Tiled map, we’ll give another example with some more features. In this example we’ll add the following things:

• New layers including foreground, background, and “Don’t Touch”

• The background layer will appear behind the player

• The foreground layer will appear in front of the player

• The Don’t Touch layer will cause the player to be reset to the start

• The player resets to the start if they fall off the map

• If the player gets to the right side of the map, the program attempts to load the next map

• This is achieved by naming the maps with incrementing numbers, something like “map_01.json”, “map_02.json”, etc. Then having a level attribute to track which number we’re on and increasing it and re-running the setup function.

To start things off, let’s add a few constants at the top of our game. The first one we need to define is the size of a sprite in pixels. Along with that we need to know the grid size in pixels. These are used to calculate the end of the level.

Multiple Levels - Constants#
```TILE_SCALING = 0.5
COIN_SCALING = 0.5
```

Next we need to define a starting position for the player, and then since we’re starting to have a larger number of layers in our game, it will be best to store their names in variables in case we need to change them later.

Multiple Levels - Constants#
```PLAYER_JUMP_SPEED = 20

# Player starting position
PLAYER_START_X = 64
PLAYER_START_Y = 225

# Layer Names from our TileMap
LAYER_NAME_PLATFORMS = "Platforms"
LAYER_NAME_COINS = "Coins"
LAYER_NAME_FOREGROUND = "Foreground"
```

Then in the `__init__` function we’ll add two new values. One to know where the right edge of the map is, and one to keep track of what level we’re on, and add a new game over sound.

Multiple Levels - Init Function#
```        self.reset_score = True

# Where is the right edge of the map?
self.end_of_map = 0

# Level
self.level = 1

```

Also in our `__init__` function we’ll need a variable to tell us if we need to reset the score. This will be the case if the player fails the level. However, now that the player can pass a level, we need to keep the score when calling our `setup` function for the new level. Otherwise it will reset the score back to 0

Multiple Levels - Init Function#
```        self.score = 0

# Do we need to reset the score?
```

Then in our `setup` function we’ll change up our map name variable to use that new level attribute, and add some extra layer specific options for the new layers we’ve added to our map.

Multiple Levels - Setup Function#
```        self.camera = arcade.SimpleCamera(viewport=viewport)

# Map name
map_name = f":resources:tiled_maps/map2_level_{self.level}.json"

# Layer Specific Options for the Tilemap
layer_options = {
LAYER_NAME_PLATFORMS: {
"use_spatial_hash": True,
},
LAYER_NAME_COINS: {
"use_spatial_hash": True,
},
LAYER_NAME_DONT_TOUCH: {
```

Now in order to make our player appear behind the “Foreground” layer, we need to add a line in our `setup` function before we create the player Sprite. This will basically be telling our Scene where in the render order we want to place the player. Previously we haven’t defined this, and so it’s always just been added to the end of the render order.

Multiple Levels - Setup Function#
```            self.score = 0
self.reset_score = True

# Add Player Spritelist before "Foreground" layer. This will make the foreground
# be drawn after the player, making it appear to be in front of the Player.
# Setting before using scene.add_sprite allows us to define where the SpriteList
# will be in the draw order. If we just use add_sprite, it will be appended to the
# end of the order.

# Set up the player, specifically placing it at these coordinates.
```

Next in our `setup` function we need to check to see if we need to reset the score or keep it.

Multiple Levels - Setup Function#
```        # Load in TileMap

# Initiate New Scene with our TileMap, this will automatically add all layers
# from the map as SpriteLists in the scene in the proper order.

# Keep track of the score, make sure we keep the score if the player finishes a level
```

Lastly in our `setup` function we need to calculate the `end_of_map` value we added earlier in `init`.

Multiple Levels - Setup Function#
```
# --- Load in a map from the tiled editor ---
```

The `on_draw`, `on_key_press`, and `on_key_release` functions will be unchanged for this section, so the last thing to do is add a few things to the `on_update` function. First we check if the player has fallen off of the map, and if so, we move them back to the starting position. Then we check if they collided with something from the “Don’t Touch” layer, and if so reset them to the start. Lastly we check if they’ve reached the end of the map, and if they have we increment the level value, tell our `setup` function not to reset the score, and then re-run the `setup` function.

Multiple Levels - Update Function#
```            # Add one to the score
self.score += 1

# Did the player fall off the map?
if self.player_sprite.center_y < -100:
self.player_sprite.center_x = PLAYER_START_X
self.player_sprite.center_y = PLAYER_START_Y

# Did the player touch something they should not?
self.player_sprite, self.scene[LAYER_NAME_DONT_TOUCH]
):
self.player_sprite.change_x = 0
self.player_sprite.change_y = 0
self.player_sprite.center_x = PLAYER_START_X
self.player_sprite.center_y = PLAYER_START_Y

# See if the user got to the end of the level
if self.player_sprite.center_x >= self.end_of_map:
# Advance to the next level
self.level += 1

# Make sure to keep the score from this level when setting up the next level
self.reset_score = False
```

Note

What else might you want to do?

## Source Code#

Multiple Levels#
```  1"""
2Platformer Game
3
5"""
7
8# Constants
9SCREEN_WIDTH = 1000
10SCREEN_HEIGHT = 650
11SCREEN_TITLE = "Platformer"
12
13# Constants used to scale our sprites from their original size
14CHARACTER_SCALING = 1
15TILE_SCALING = 0.5
16COIN_SCALING = 0.5
17SPRITE_PIXEL_SIZE = 128
18GRID_PIXEL_SIZE = SPRITE_PIXEL_SIZE * TILE_SCALING
19
20# Movement speed of player, in pixels per frame
21PLAYER_MOVEMENT_SPEED = 10
22GRAVITY = 1
23PLAYER_JUMP_SPEED = 20
24
25# Player starting position
26PLAYER_START_X = 64
27PLAYER_START_Y = 225
28
29# Layer Names from our TileMap
30LAYER_NAME_PLATFORMS = "Platforms"
31LAYER_NAME_COINS = "Coins"
32LAYER_NAME_FOREGROUND = "Foreground"
33LAYER_NAME_BACKGROUND = "Background"
34LAYER_NAME_DONT_TOUCH = "Don't Touch"
35
36
38    """
39    Main application class.
40    """
41
42    def __init__(self):
43
44        # Call the parent class and set up the window
45        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
46
47        # Our TileMap Object
48        self.tile_map = None
49
50        # Our Scene Object
51        self.scene = None
52
53        # Separate variable that holds the player sprite
54        self.player_sprite = None
55
56        # Our physics engine
57        self.physics_engine = None
58
59        # A Camera that can be used for scrolling the screen
60        self.camera = None
61
62        # A Camera that can be used to draw GUI elements
63        self.gui_camera = None
64
65        # Keep track of the score
66        self.score = 0
67
68        # Do we need to reset the score?
69        self.reset_score = True
70
71        # Where is the right edge of the map?
72        self.end_of_map = 0
73
74        # Level
75        self.level = 1
76
81
82    def setup(self):
83        """Set up the game here. Call this function to restart the game."""
84
85        # Set up the Cameras
86        viewport = (0, 0, self.width, self.height)
89
90        # Map name
91        map_name = f":resources:tiled_maps/map2_level_{self.level}.json"
92
93        # Layer Specific Options for the Tilemap
94        layer_options = {
95            LAYER_NAME_PLATFORMS: {
96                "use_spatial_hash": True,
97            },
98            LAYER_NAME_COINS: {
99                "use_spatial_hash": True,
100            },
101            LAYER_NAME_DONT_TOUCH: {
102                "use_spatial_hash": True,
103            },
104        }
105
108
109        # Initiate New Scene with our TileMap, this will automatically add all layers
110        # from the map as SpriteLists in the scene in the proper order.
112
113        # Keep track of the score, make sure we keep the score if the player finishes a level
114        if self.reset_score:
115            self.score = 0
116        self.reset_score = True
117
118        # Add Player Spritelist before "Foreground" layer. This will make the foreground
119        # be drawn after the player, making it appear to be in front of the Player.
120        # Setting before using scene.add_sprite allows us to define where the SpriteList
121        # will be in the draw order. If we just use add_sprite, it will be appended to the
122        # end of the order.
124
125        # Set up the player, specifically placing it at these coordinates.
128        self.player_sprite.center_x = PLAYER_START_X
129        self.player_sprite.center_y = PLAYER_START_Y
131
132        # --- Load in a map from the tiled editor ---
133
134        # Calculate the right edge of the my_map in pixels
135        self.end_of_map = self.tile_map.width * GRID_PIXEL_SIZE
136
137        # --- Other stuff
138        # Set the background color
139        if self.tile_map.background_color:
140            self.background_color = self.tile_map.background_color
141
142        # Create the 'physics engine'
144            self.player_sprite,
145            gravity_constant=GRAVITY,
146            walls=self.scene[LAYER_NAME_PLATFORMS],
147        )
148
149    def on_draw(self):
150        """Render the screen."""
151
152        # Clear the screen to the background color
153        self.clear()
154
155        # Activate the game camera
156        self.camera.use()
157
158        # Draw our Scene
159        self.scene.draw()
160
161        # Activate the GUI camera before drawing GUI elements
162        self.gui_camera.use()
163
164        # Draw our score on the screen, scrolling it with the viewport
165        score_text = f"Score: {self.score}"
167            score_text,
168            10,
169            10,
171            18,
172        )
173
174    def on_key_press(self, key, modifiers):
175        """Called whenever a key is pressed."""
176
178            if self.physics_engine.can_jump():
179                self.player_sprite.change_y = PLAYER_JUMP_SPEED
182            self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
184            self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
185
186    def on_key_release(self, key, modifiers):
187        """Called when the user releases a key."""
188
190            self.player_sprite.change_x = 0
192            self.player_sprite.change_x = 0
193
194    def center_camera_to_player(self):
195        screen_center_x = self.player_sprite.center_x - (self.camera.viewport_width / 2)
196        screen_center_y = self.player_sprite.center_y - (
197            self.camera.viewport_height / 2
198        )
199        if screen_center_x < 0:
200            screen_center_x = 0
201        if screen_center_y < 0:
202            screen_center_y = 0
203        player_centered = screen_center_x, screen_center_y
204
205        self.camera.move_to(player_centered)
206
207    def on_update(self, delta_time):
208        """Movement and game logic"""
209
210        # Move the player with the physics engine
211        self.physics_engine.update()
212
213        # See if we hit any coins
215            self.player_sprite, self.scene[LAYER_NAME_COINS]
216        )
217
218        # Loop through each coin we hit (if any) and remove it
219        for coin in coin_hit_list:
220            # Remove the coin
221            coin.remove_from_sprite_lists()
222            # Play a sound
224            # Add one to the score
225            self.score += 1
226
227        # Did the player fall off the map?
228        if self.player_sprite.center_y < -100:
229            self.player_sprite.center_x = PLAYER_START_X
230            self.player_sprite.center_y = PLAYER_START_Y
231
233
234        # Did the player touch something they should not?
236            self.player_sprite, self.scene[LAYER_NAME_DONT_TOUCH]
237        ):
238            self.player_sprite.change_x = 0
239            self.player_sprite.change_y = 0
240            self.player_sprite.center_x = PLAYER_START_X
241            self.player_sprite.center_y = PLAYER_START_Y
242
244
245        # See if the user got to the end of the level
246        if self.player_sprite.center_x >= self.end_of_map:
247            # Advance to the next level
248            self.level += 1
249
250            # Make sure to keep the score from this level when setting up the next level
251            self.reset_score = False
252
253            # Load the next level
254            self.setup()
255
256        # Position the camera
257        self.center_camera_to_player()
258
259
260def main():
261    """Main function"""
262    window = MyGame()
263    window.setup()