Step 11 - Add Ladders, Properties, and a Moving Platform#

This example shows using:
Ladders
Properties to define point value of coins and flags
Properties and an object layer to define a moving platform.
To create a moving platform using TMX editor, there are a few steps:
Define an object layer instead of a tile layer.
Select Insert Tile
Select the tile you wish to insert.
Place the tile.
Add custom properties. You can add:
change_x
change_y
boundary_bottom
boundary_top
boundary_left
boundary_right

Ladders, Animated Tiles, and Moving Platforms#
1"""
2Platformer Game
3
4python -m arcade.examples.platform_tutorial.11_ladders_and_more
5"""
6import arcade
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 = 7
22GRAVITY = 1.5
23PLAYER_JUMP_SPEED = 30
24
25PLAYER_START_X = 64
26PLAYER_START_Y = 256
27
28# Layer Names from our TileMap
29LAYER_NAME_MOVING_PLATFORMS = "Moving Platforms"
30LAYER_NAME_PLATFORMS = "Platforms"
31LAYER_NAME_COINS = "Coins"
32LAYER_NAME_BACKGROUND = "Background"
33LAYER_NAME_LADDERS = "Ladders"
34
35
36class MyGame(arcade.Window):
37 """
38 Main application class.
39 """
40
41 def __init__(self):
42 """
43 Initializer for the game
44 """
45 # Call the parent class and set up the window
46 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
47
48 # Our TileMap Object
49 self.tile_map = None
50
51 # Our Scene Object
52 self.scene = None
53
54 # Separate variable that holds the player sprite
55 self.player_sprite = None
56
57 # Our 'physics' engine
58 self.physics_engine = None
59
60 # A Camera that can be used for scrolling the screen
61 self.camera = None
62
63 # A Camera that can be used to draw GUI elements
64 self.gui_camera = None
65
66 self.end_of_map = 0
67
68 # Keep track of the score
69 self.score = 0
70
71 # Load sounds
72 self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
73 self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")
74 self.game_over = arcade.load_sound(":resources:sounds/gameover1.wav")
75
76 def setup(self):
77 """Set up the game here. Call this function to restart the game."""
78
79 # Set up the Cameras
80 viewport = (0, 0, self.width, self.height)
81 self.camera = arcade.SimpleCamera(viewport=viewport)
82 self.gui_camera = arcade.SimpleCamera(viewport=viewport)
83
84 # Map name
85 map_name = ":resources:tiled_maps/map_with_ladders.json"
86
87 # Layer Specific Options for the Tilemap
88 layer_options = {
89 LAYER_NAME_PLATFORMS: {
90 "use_spatial_hash": True,
91 },
92 LAYER_NAME_MOVING_PLATFORMS: {
93 "use_spatial_hash": False,
94 },
95 LAYER_NAME_LADDERS: {
96 "use_spatial_hash": True,
97 },
98 LAYER_NAME_COINS: {
99 "use_spatial_hash": True,
100 },
101 }
102
103 # Load in TileMap
104 self.tile_map = arcade.load_tilemap(map_name, TILE_SCALING, layer_options)
105
106 # Initiate New Scene with our TileMap, this will automatically add all layers
107 # from the map as SpriteLists in the scene in the proper order.
108 self.scene = arcade.Scene.from_tilemap(self.tile_map)
109
110 # Keep track of the score
111 self.score = 0
112
113 # Set up the player, specifically placing it at these coordinates.
114 image_source = ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
115 self.player_sprite = arcade.Sprite(image_source, CHARACTER_SCALING)
116 self.player_sprite.center_x = PLAYER_START_X
117 self.player_sprite.center_y = PLAYER_START_Y
118 self.scene.add_sprite("Player", self.player_sprite)
119
120 # Calculate the right edge of the my_map in pixels
121 self.end_of_map = self.tile_map.width * GRID_PIXEL_SIZE
122
123 # --- Other stuff
124 # Set the background color
125 if self.tile_map.background_color:
126 self.background_color = self.tile_map.background_color
127
128 # Create the 'physics engine'
129 self.physics_engine = arcade.PhysicsEnginePlatformer(
130 self.player_sprite,
131 platforms=self.scene[LAYER_NAME_MOVING_PLATFORMS],
132 gravity_constant=GRAVITY,
133 ladders=self.scene[LAYER_NAME_LADDERS],
134 walls=self.scene[LAYER_NAME_PLATFORMS]
135 )
136
137 def on_draw(self):
138 """Render the screen."""
139 # Clear the screen to the background color
140 self.clear()
141
142 # Activate the game camera
143 self.camera.use()
144
145 # Draw our Scene
146 self.scene.draw()
147
148 # Activate the GUI camera before drawing GUI elements
149 self.gui_camera.use()
150
151 # Draw our score on the screen, scrolling it with the viewport
152 score_text = f"Score: {self.score}"
153 arcade.draw_text(
154 score_text,
155 10,
156 10,
157 arcade.csscolor.BLACK,
158 18,
159 )
160
161 def on_key_press(self, key, modifiers):
162 """Called whenever a key is pressed."""
163
164 if key == arcade.key.UP or key == arcade.key.W:
165 if self.physics_engine.is_on_ladder():
166 self.player_sprite.change_y = PLAYER_MOVEMENT_SPEED
167 elif self.physics_engine.can_jump():
168 self.player_sprite.change_y = PLAYER_JUMP_SPEED
169 arcade.play_sound(self.jump_sound)
170 elif key == arcade.key.DOWN or key == arcade.key.S:
171 if self.physics_engine.is_on_ladder():
172 self.player_sprite.change_y = -PLAYER_MOVEMENT_SPEED
173 elif key == arcade.key.LEFT or key == arcade.key.A:
174 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
175 elif key == arcade.key.RIGHT or key == arcade.key.D:
176 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
177
178 def on_key_release(self, key, modifiers):
179 """Called when the user releases a key."""
180
181 if key == arcade.key.UP or key == arcade.key.W:
182 if self.physics_engine.is_on_ladder():
183 self.player_sprite.change_y = 0
184 elif key == arcade.key.DOWN or key == arcade.key.S:
185 if self.physics_engine.is_on_ladder():
186 self.player_sprite.change_y = 0
187 elif key == arcade.key.LEFT or key == arcade.key.A:
188 self.player_sprite.change_x = 0
189 elif key == arcade.key.RIGHT or key == arcade.key.D:
190 self.player_sprite.change_x = 0
191
192 def center_camera_to_player(self):
193 screen_center_x = self.player_sprite.center_x - (self.camera.viewport_width / 2)
194 screen_center_y = self.player_sprite.center_y - (
195 self.camera.viewport_height / 2
196 )
197 if screen_center_x < 0:
198 screen_center_x = 0
199 if screen_center_y < 0:
200 screen_center_y = 0
201 player_centered = screen_center_x, screen_center_y
202
203 self.camera.move_to(player_centered, 0.2)
204
205 def on_update(self, delta_time):
206 """Movement and game logic"""
207 # Move the player with the physics engine
208 self.physics_engine.update()
209
210 # Update animations
211 self.scene.update_animation(
212 delta_time, [LAYER_NAME_COINS, LAYER_NAME_BACKGROUND]
213 )
214
215 # Update walls, used with moving platforms
216 self.scene.update([LAYER_NAME_MOVING_PLATFORMS])
217
218 # See if we hit any coins
219 coin_hit_list = arcade.check_for_collision_with_list(
220 self.player_sprite, self.scene[LAYER_NAME_COINS]
221 )
222
223 # Loop through each coin we hit (if any) and remove it
224 for coin in coin_hit_list:
225
226 # Figure out how many points this coin is worth
227 if "Points" not in coin.properties:
228 print("Warning, collected a coin without a Points property.")
229 else:
230 points = int(coin.properties["Points"])
231 self.score += points
232
233 # Remove the coin
234 coin.remove_from_sprite_lists()
235 arcade.play_sound(self.collect_coin_sound)
236
237 # Position the camera
238 self.center_camera_to_player()
239
240
241def main():
242 """Main function"""
243 window = MyGame()
244 window.setup()
245 arcade.run()
246
247
248if __name__ == "__main__":
249 main()
Source Code#
Ladders and More#
1"""
2Platformer Game
3
4python -m arcade.examples.platform_tutorial.11_ladders_and_more
5"""
6import arcade
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 = 7
22GRAVITY = 1.5
23PLAYER_JUMP_SPEED = 30
24
25PLAYER_START_X = 64
26PLAYER_START_Y = 256
27
28# Layer Names from our TileMap
29LAYER_NAME_MOVING_PLATFORMS = "Moving Platforms"
30LAYER_NAME_PLATFORMS = "Platforms"
31LAYER_NAME_COINS = "Coins"
32LAYER_NAME_BACKGROUND = "Background"
33LAYER_NAME_LADDERS = "Ladders"
34
35
36class MyGame(arcade.Window):
37 """
38 Main application class.
39 """
40
41 def __init__(self):
42 """
43 Initializer for the game
44 """
45 # Call the parent class and set up the window
46 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
47
48 # Our TileMap Object
49 self.tile_map = None
50
51 # Our Scene Object
52 self.scene = None
53
54 # Separate variable that holds the player sprite
55 self.player_sprite = None
56
57 # Our 'physics' engine
58 self.physics_engine = None
59
60 # A Camera that can be used for scrolling the screen
61 self.camera = None
62
63 # A Camera that can be used to draw GUI elements
64 self.gui_camera = None
65
66 self.end_of_map = 0
67
68 # Keep track of the score
69 self.score = 0
70
71 # Load sounds
72 self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
73 self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")
74 self.game_over = arcade.load_sound(":resources:sounds/gameover1.wav")
75
76 def setup(self):
77 """Set up the game here. Call this function to restart the game."""
78
79 # Set up the Cameras
80 viewport = (0, 0, self.width, self.height)
81 self.camera = arcade.SimpleCamera(viewport=viewport)
82 self.gui_camera = arcade.SimpleCamera(viewport=viewport)
83
84 # Map name
85 map_name = ":resources:tiled_maps/map_with_ladders.json"
86
87 # Layer Specific Options for the Tilemap
88 layer_options = {
89 LAYER_NAME_PLATFORMS: {
90 "use_spatial_hash": True,
91 },
92 LAYER_NAME_MOVING_PLATFORMS: {
93 "use_spatial_hash": False,
94 },
95 LAYER_NAME_LADDERS: {
96 "use_spatial_hash": True,
97 },
98 LAYER_NAME_COINS: {
99 "use_spatial_hash": True,
100 },
101 }
102
103 # Load in TileMap
104 self.tile_map = arcade.load_tilemap(map_name, TILE_SCALING, layer_options)
105
106 # Initiate New Scene with our TileMap, this will automatically add all layers
107 # from the map as SpriteLists in the scene in the proper order.
108 self.scene = arcade.Scene.from_tilemap(self.tile_map)
109
110 # Keep track of the score
111 self.score = 0
112
113 # Set up the player, specifically placing it at these coordinates.
114 image_source = ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
115 self.player_sprite = arcade.Sprite(image_source, CHARACTER_SCALING)
116 self.player_sprite.center_x = PLAYER_START_X
117 self.player_sprite.center_y = PLAYER_START_Y
118 self.scene.add_sprite("Player", self.player_sprite)
119
120 # Calculate the right edge of the my_map in pixels
121 self.end_of_map = self.tile_map.width * GRID_PIXEL_SIZE
122
123 # --- Other stuff
124 # Set the background color
125 if self.tile_map.background_color:
126 self.background_color = self.tile_map.background_color
127
128 # Create the 'physics engine'
129 self.physics_engine = arcade.PhysicsEnginePlatformer(
130 self.player_sprite,
131 platforms=self.scene[LAYER_NAME_MOVING_PLATFORMS],
132 gravity_constant=GRAVITY,
133 ladders=self.scene[LAYER_NAME_LADDERS],
134 walls=self.scene[LAYER_NAME_PLATFORMS]
135 )
136
137 def on_draw(self):
138 """Render the screen."""
139 # Clear the screen to the background color
140 self.clear()
141
142 # Activate the game camera
143 self.camera.use()
144
145 # Draw our Scene
146 self.scene.draw()
147
148 # Activate the GUI camera before drawing GUI elements
149 self.gui_camera.use()
150
151 # Draw our score on the screen, scrolling it with the viewport
152 score_text = f"Score: {self.score}"
153 arcade.draw_text(
154 score_text,
155 10,
156 10,
157 arcade.csscolor.BLACK,
158 18,
159 )
160
161 def on_key_press(self, key, modifiers):
162 """Called whenever a key is pressed."""
163
164 if key == arcade.key.UP or key == arcade.key.W:
165 if self.physics_engine.is_on_ladder():
166 self.player_sprite.change_y = PLAYER_MOVEMENT_SPEED
167 elif self.physics_engine.can_jump():
168 self.player_sprite.change_y = PLAYER_JUMP_SPEED
169 arcade.play_sound(self.jump_sound)
170 elif key == arcade.key.DOWN or key == arcade.key.S:
171 if self.physics_engine.is_on_ladder():
172 self.player_sprite.change_y = -PLAYER_MOVEMENT_SPEED
173 elif key == arcade.key.LEFT or key == arcade.key.A:
174 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
175 elif key == arcade.key.RIGHT or key == arcade.key.D:
176 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
177
178 def on_key_release(self, key, modifiers):
179 """Called when the user releases a key."""
180
181 if key == arcade.key.UP or key == arcade.key.W:
182 if self.physics_engine.is_on_ladder():
183 self.player_sprite.change_y = 0
184 elif key == arcade.key.DOWN or key == arcade.key.S:
185 if self.physics_engine.is_on_ladder():
186 self.player_sprite.change_y = 0
187 elif key == arcade.key.LEFT or key == arcade.key.A:
188 self.player_sprite.change_x = 0
189 elif key == arcade.key.RIGHT or key == arcade.key.D:
190 self.player_sprite.change_x = 0
191
192 def center_camera_to_player(self):
193 screen_center_x = self.player_sprite.center_x - (self.camera.viewport_width / 2)
194 screen_center_y = self.player_sprite.center_y - (
195 self.camera.viewport_height / 2
196 )
197 if screen_center_x < 0:
198 screen_center_x = 0
199 if screen_center_y < 0:
200 screen_center_y = 0
201 player_centered = screen_center_x, screen_center_y
202
203 self.camera.move_to(player_centered, 0.2)
204
205 def on_update(self, delta_time):
206 """Movement and game logic"""
207 # Move the player with the physics engine
208 self.physics_engine.update()
209
210 # Update animations
211 self.scene.update_animation(
212 delta_time, [LAYER_NAME_COINS, LAYER_NAME_BACKGROUND]
213 )
214
215 # Update walls, used with moving platforms
216 self.scene.update([LAYER_NAME_MOVING_PLATFORMS])
217
218 # See if we hit any coins
219 coin_hit_list = arcade.check_for_collision_with_list(
220 self.player_sprite, self.scene[LAYER_NAME_COINS]
221 )
222
223 # Loop through each coin we hit (if any) and remove it
224 for coin in coin_hit_list:
225
226 # Figure out how many points this coin is worth
227 if "Points" not in coin.properties:
228 print("Warning, collected a coin without a Points property.")
229 else:
230 points = int(coin.properties["Points"])
231 self.score += points
232
233 # Remove the coin
234 coin.remove_from_sprite_lists()
235 arcade.play_sound(self.collect_coin_sound)
236
237 # Position the camera
238 self.center_camera_to_player()
239
240
241def main():
242 """Main function"""
243 window = MyGame()
244 window.setup()
245 arcade.run()
246
247
248if __name__ == "__main__":
249 main()