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:
Source Code#
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()