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