Step 12 - Add Character Animations, and Better Keyboard Control#
Add character animations!
Animate Characters#
1"""
2Platformer Game
3
4python -m arcade.examples.platform_tutorial.12_animate_character
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
14TILE_SCALING = 0.5
15CHARACTER_SCALING = TILE_SCALING * 2
16COIN_SCALING = TILE_SCALING
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 = SPRITE_PIXEL_SIZE * TILE_SCALING * 2
26PLAYER_START_Y = SPRITE_PIXEL_SIZE * TILE_SCALING * 1
27
28# Constants used to track if the player is facing left or right
29RIGHT_FACING = 0
30LEFT_FACING = 1
31
32LAYER_NAME_MOVING_PLATFORMS = "Moving Platforms"
33LAYER_NAME_PLATFORMS = "Platforms"
34LAYER_NAME_COINS = "Coins"
35LAYER_NAME_BACKGROUND = "Background"
36LAYER_NAME_LADDERS = "Ladders"
37LAYER_NAME_PLAYER = "Player"
38
39
40def load_texture_pair(filename):
41 """
42 Load a texture pair, with the second being a mirror image.
43 """
44 return [
45 arcade.load_texture(filename),
46 arcade.load_texture(filename, flipped_horizontally=True),
47 ]
48
49
50class PlayerCharacter(arcade.Sprite):
51 """Player Sprite"""
52
53 def __init__(self):
54
55 # Set up parent class
56 super().__init__()
57
58 # Default to face-right
59 self.character_face_direction = RIGHT_FACING
60
61 # Used for flipping between image sequences
62 self.cur_texture = 0
63 self.scale = CHARACTER_SCALING
64
65 # Track our state
66 self.jumping = False
67 self.climbing = False
68 self.is_on_ladder = False
69
70 # --- Load Textures ---
71
72 # Images from Kenney.nl's Asset Pack 3
73 main_path = ":resources:images/animated_characters/male_person/malePerson"
74
75 # Load textures for idle standing
76 self.idle_texture_pair = load_texture_pair(f"{main_path}_idle.png")
77 self.jump_texture_pair = load_texture_pair(f"{main_path}_jump.png")
78 self.fall_texture_pair = load_texture_pair(f"{main_path}_fall.png")
79
80 # Load textures for walking
81 self.walk_textures = []
82 for i in range(8):
83 texture = load_texture_pair(f"{main_path}_walk{i}.png")
84 self.walk_textures.append(texture)
85
86 # Load textures for climbing
87 self.climbing_textures = []
88 texture = arcade.load_texture(f"{main_path}_climb0.png")
89 self.climbing_textures.append(texture)
90 texture = arcade.load_texture(f"{main_path}_climb1.png")
91 self.climbing_textures.append(texture)
92
93 # Set the initial texture
94 self.texture = self.idle_texture_pair[0]
95
96 # Hit box will be set based on the first image used. If you want to specify
97 # a different hit box, you can do it like the code below.
98 # set_hit_box = [[-22, -64], [22, -64], [22, 28], [-22, 28]]
99 self.hit_box = self.texture.hit_box_points
100
101 def update_animation(self, delta_time: float = 1 / 60):
102
103 # Figure out if we need to flip face left or right
104 if self.change_x < 0 and self.character_face_direction == RIGHT_FACING:
105 self.character_face_direction = LEFT_FACING
106 elif self.change_x > 0 and self.character_face_direction == LEFT_FACING:
107 self.character_face_direction = RIGHT_FACING
108
109 # Climbing animation
110 if self.is_on_ladder:
111 self.climbing = True
112 if not self.is_on_ladder and self.climbing:
113 self.climbing = False
114 if self.climbing and abs(self.change_y) > 1:
115 self.cur_texture += 1
116 if self.cur_texture > 7:
117 self.cur_texture = 0
118 if self.climbing:
119 self.texture = self.climbing_textures[self.cur_texture // 4]
120 return
121
122 # Jumping animation
123 if self.change_y > 0 and not self.is_on_ladder:
124 self.texture = self.jump_texture_pair[self.character_face_direction]
125 return
126 elif self.change_y < 0 and not self.is_on_ladder:
127 self.texture = self.fall_texture_pair[self.character_face_direction]
128 return
129
130 # Idle animation
131 if self.change_x == 0:
132 self.texture = self.idle_texture_pair[self.character_face_direction]
133 return
134
135 # Walking animation
136 self.cur_texture += 1
137 if self.cur_texture > 7:
138 self.cur_texture = 0
139 self.texture = self.walk_textures[self.cur_texture][
140 self.character_face_direction
141 ]
142
143
144class MyGame(arcade.Window):
145 """
146 Main application class.
147 """
148
149 def __init__(self):
150 """
151 Initializer for the game
152 """
153 # Call the parent class and set up the window
154 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
155
156 # Track the current state of what key is pressed
157 self.left_pressed = False
158 self.right_pressed = False
159 self.up_pressed = False
160 self.down_pressed = False
161 self.jump_needs_reset = False
162
163 # Our TileMap Object
164 self.tile_map = None
165
166 # Our Scene Object
167 self.scene = None
168
169 # Separate variable that holds the player sprite
170 self.player_sprite = None
171
172 # Our 'physics' engine
173 self.physics_engine = None
174
175 # A Camera that can be used for scrolling the screen
176 self.camera = None
177
178 # A Camera that can be used to draw GUI elements
179 self.gui_camera = None
180
181 self.end_of_map = 0
182
183 # Keep track of the score
184 self.score = 0
185
186 # Load sounds
187 self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
188 self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")
189 self.game_over = arcade.load_sound(":resources:sounds/gameover1.wav")
190
191 def setup(self):
192 """Set up the game here. Call this function to restart the game."""
193
194 # Set up the Cameras
195 viewport = (0, 0, self.width, self.height)
196 self.camera = arcade.SimpleCamera(viewport=viewport)
197 self.gui_camera = arcade.SimpleCamera(viewport=viewport)
198
199 # Map name
200 map_name = ":resources:tiled_maps/map_with_ladders.json"
201
202 # Layer Specific Options for the Tilemap
203 layer_options = {
204 LAYER_NAME_PLATFORMS: {
205 "use_spatial_hash": True,
206 },
207 LAYER_NAME_MOVING_PLATFORMS: {
208 "use_spatial_hash": False,
209 },
210 LAYER_NAME_LADDERS: {
211 "use_spatial_hash": True,
212 },
213 LAYER_NAME_COINS: {
214 "use_spatial_hash": True,
215 },
216 }
217
218 # Load in TileMap
219 self.tile_map = arcade.load_tilemap(map_name, TILE_SCALING, layer_options)
220
221 # Initiate New Scene with our TileMap, this will automatically add all layers
222 # from the map as SpriteLists in the scene in the proper order.
223 self.scene = arcade.Scene.from_tilemap(self.tile_map)
224
225 # Keep track of the score
226 self.score = 0
227
228 # Set up the player, specifically placing it at these coordinates.
229 self.player_sprite = PlayerCharacter()
230 self.player_sprite.center_x = PLAYER_START_X
231 self.player_sprite.center_y = PLAYER_START_Y
232 self.scene.add_sprite(LAYER_NAME_PLAYER, self.player_sprite)
233
234 # Calculate the right edge of the my_map in pixels
235 self.end_of_map = self.tile_map.width * GRID_PIXEL_SIZE
236
237 # --- Other stuff
238 # Set the background color
239 if self.tile_map.background_color:
240 arcade.set_background_color(self.tile_map.background_color)
241
242 # Create the 'physics engine'
243 self.physics_engine = arcade.PhysicsEnginePlatformer(
244 self.player_sprite,
245 platforms=self.scene[LAYER_NAME_MOVING_PLATFORMS],
246 gravity_constant=GRAVITY,
247 ladders=self.scene[LAYER_NAME_LADDERS],
248 walls=self.scene[LAYER_NAME_PLATFORMS]
249 )
250
251 def on_draw(self):
252 """Render the screen."""
253
254 # Clear the screen to the background color
255 self.clear()
256
257 # Activate the game camera
258 self.camera.use()
259
260 # Draw our Scene
261 self.scene.draw()
262
263 # Activate the GUI camera before drawing GUI elements
264 self.gui_camera.use()
265
266 # Draw our score on the screen, scrolling it with the viewport
267 score_text = f"Score: {self.score}"
268 arcade.draw_text(
269 score_text,
270 10,
271 10,
272 arcade.csscolor.BLACK,
273 18,
274 )
275
276 # Draw hit boxes.
277 # for wall in self.wall_list:
278 # wall.draw_hit_box(arcade.color.BLACK, 3)
279 #
280 # self.player_sprite.draw_hit_box(arcade.color.RED, 3)
281
282 def process_keychange(self):
283 """
284 Called when we change a key up/down or we move on/off a ladder.
285 """
286 # Process up/down
287 if self.up_pressed and not self.down_pressed:
288 if self.physics_engine.is_on_ladder():
289 self.player_sprite.change_y = PLAYER_MOVEMENT_SPEED
290 elif (
291 self.physics_engine.can_jump(y_distance=10)
292 and not self.jump_needs_reset
293 ):
294 self.player_sprite.change_y = PLAYER_JUMP_SPEED
295 self.jump_needs_reset = True
296 arcade.play_sound(self.jump_sound)
297 elif self.down_pressed and not self.up_pressed:
298 if self.physics_engine.is_on_ladder():
299 self.player_sprite.change_y = -PLAYER_MOVEMENT_SPEED
300
301 # Process up/down when on a ladder and no movement
302 if self.physics_engine.is_on_ladder():
303 if not self.up_pressed and not self.down_pressed:
304 self.player_sprite.change_y = 0
305 elif self.up_pressed and self.down_pressed:
306 self.player_sprite.change_y = 0
307
308 # Process left/right
309 if self.right_pressed and not self.left_pressed:
310 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
311 elif self.left_pressed and not self.right_pressed:
312 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
313 else:
314 self.player_sprite.change_x = 0
315
316 def on_key_press(self, key, modifiers):
317 """Called whenever a key is pressed."""
318
319 if key == arcade.key.UP or key == arcade.key.W:
320 self.up_pressed = True
321 elif key == arcade.key.DOWN or key == arcade.key.S:
322 self.down_pressed = True
323 elif key == arcade.key.LEFT or key == arcade.key.A:
324 self.left_pressed = True
325 elif key == arcade.key.RIGHT or key == arcade.key.D:
326 self.right_pressed = True
327
328 self.process_keychange()
329
330 def on_key_release(self, key, modifiers):
331 """Called when the user releases a key."""
332
333 if key == arcade.key.UP or key == arcade.key.W:
334 self.up_pressed = False
335 self.jump_needs_reset = False
336 elif key == arcade.key.DOWN or key == arcade.key.S:
337 self.down_pressed = False
338 elif key == arcade.key.LEFT or key == arcade.key.A:
339 self.left_pressed = False
340 elif key == arcade.key.RIGHT or key == arcade.key.D:
341 self.right_pressed = False
342
343 self.process_keychange()
344
345 def center_camera_to_player(self):
346 screen_center_x = self.player_sprite.center_x - (self.camera.viewport_width / 2)
347 screen_center_y = self.player_sprite.center_y - (
348 self.camera.viewport_height / 2
349 )
350 if screen_center_x < 0:
351 screen_center_x = 0
352 if screen_center_y < 0:
353 screen_center_y = 0
354 player_centered = screen_center_x, screen_center_y
355
356 self.camera.move_to(player_centered, 0.2)
357
358 def on_update(self, delta_time):
359 """Movement and game logic"""
360
361 # Move the player with the physics engine
362 self.physics_engine.update()
363
364 # Update animations
365 if self.physics_engine.can_jump():
366 self.player_sprite.can_jump = False
367 else:
368 self.player_sprite.can_jump = True
369
370 if self.physics_engine.is_on_ladder() and not self.physics_engine.can_jump():
371 self.player_sprite.is_on_ladder = True
372 self.process_keychange()
373 else:
374 self.player_sprite.is_on_ladder = False
375 self.process_keychange()
376
377 # Update Animations
378 self.scene.update_animation(
379 delta_time, [LAYER_NAME_COINS, LAYER_NAME_BACKGROUND, LAYER_NAME_PLAYER]
380 )
381
382 # Update walls, used with moving platforms
383 self.scene.update([LAYER_NAME_MOVING_PLATFORMS])
384
385 # See if we hit any coins
386 coin_hit_list = arcade.check_for_collision_with_list(
387 self.player_sprite, self.scene[LAYER_NAME_COINS]
388 )
389
390 # Loop through each coin we hit (if any) and remove it
391 for coin in coin_hit_list:
392
393 # Figure out how many points this coin is worth
394 if "Points" not in coin.properties:
395 print("Warning, collected a coin without a Points property.")
396 else:
397 points = int(coin.properties["Points"])
398 self.score += points
399
400 # Remove the coin
401 coin.remove_from_sprite_lists()
402 arcade.play_sound(self.collect_coin_sound)
403
404 # Position the camera
405 self.center_camera_to_player()
406
407
408def main():
409 """Main function"""
410 window = MyGame()
411 window.setup()
412 arcade.run()
413
414
415if __name__ == "__main__":
416 main()
Source Code#
Animate the player character#
1"""
2Platformer Game
3
4python -m arcade.examples.platform_tutorial.12_animate_character
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
14TILE_SCALING = 0.5
15CHARACTER_SCALING = TILE_SCALING * 2
16COIN_SCALING = TILE_SCALING
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 = SPRITE_PIXEL_SIZE * TILE_SCALING * 2
26PLAYER_START_Y = SPRITE_PIXEL_SIZE * TILE_SCALING * 1
27
28# Constants used to track if the player is facing left or right
29RIGHT_FACING = 0
30LEFT_FACING = 1
31
32LAYER_NAME_MOVING_PLATFORMS = "Moving Platforms"
33LAYER_NAME_PLATFORMS = "Platforms"
34LAYER_NAME_COINS = "Coins"
35LAYER_NAME_BACKGROUND = "Background"
36LAYER_NAME_LADDERS = "Ladders"
37LAYER_NAME_PLAYER = "Player"
38
39
40def load_texture_pair(filename):
41 """
42 Load a texture pair, with the second being a mirror image.
43 """
44 return [
45 arcade.load_texture(filename),
46 arcade.load_texture(filename, flipped_horizontally=True),
47 ]
48
49
50class PlayerCharacter(arcade.Sprite):
51 """Player Sprite"""
52
53 def __init__(self):
54
55 # Set up parent class
56 super().__init__()
57
58 # Default to face-right
59 self.character_face_direction = RIGHT_FACING
60
61 # Used for flipping between image sequences
62 self.cur_texture = 0
63 self.scale = CHARACTER_SCALING
64
65 # Track our state
66 self.jumping = False
67 self.climbing = False
68 self.is_on_ladder = False
69
70 # --- Load Textures ---
71
72 # Images from Kenney.nl's Asset Pack 3
73 main_path = ":resources:images/animated_characters/male_person/malePerson"
74
75 # Load textures for idle standing
76 self.idle_texture_pair = load_texture_pair(f"{main_path}_idle.png")
77 self.jump_texture_pair = load_texture_pair(f"{main_path}_jump.png")
78 self.fall_texture_pair = load_texture_pair(f"{main_path}_fall.png")
79
80 # Load textures for walking
81 self.walk_textures = []
82 for i in range(8):
83 texture = load_texture_pair(f"{main_path}_walk{i}.png")
84 self.walk_textures.append(texture)
85
86 # Load textures for climbing
87 self.climbing_textures = []
88 texture = arcade.load_texture(f"{main_path}_climb0.png")
89 self.climbing_textures.append(texture)
90 texture = arcade.load_texture(f"{main_path}_climb1.png")
91 self.climbing_textures.append(texture)
92
93 # Set the initial texture
94 self.texture = self.idle_texture_pair[0]
95
96 # Hit box will be set based on the first image used. If you want to specify
97 # a different hit box, you can do it like the code below.
98 # set_hit_box = [[-22, -64], [22, -64], [22, 28], [-22, 28]]
99 self.hit_box = self.texture.hit_box_points
100
101 def update_animation(self, delta_time: float = 1 / 60):
102
103 # Figure out if we need to flip face left or right
104 if self.change_x < 0 and self.character_face_direction == RIGHT_FACING:
105 self.character_face_direction = LEFT_FACING
106 elif self.change_x > 0 and self.character_face_direction == LEFT_FACING:
107 self.character_face_direction = RIGHT_FACING
108
109 # Climbing animation
110 if self.is_on_ladder:
111 self.climbing = True
112 if not self.is_on_ladder and self.climbing:
113 self.climbing = False
114 if self.climbing and abs(self.change_y) > 1:
115 self.cur_texture += 1
116 if self.cur_texture > 7:
117 self.cur_texture = 0
118 if self.climbing:
119 self.texture = self.climbing_textures[self.cur_texture // 4]
120 return
121
122 # Jumping animation
123 if self.change_y > 0 and not self.is_on_ladder:
124 self.texture = self.jump_texture_pair[self.character_face_direction]
125 return
126 elif self.change_y < 0 and not self.is_on_ladder:
127 self.texture = self.fall_texture_pair[self.character_face_direction]
128 return
129
130 # Idle animation
131 if self.change_x == 0:
132 self.texture = self.idle_texture_pair[self.character_face_direction]
133 return
134
135 # Walking animation
136 self.cur_texture += 1
137 if self.cur_texture > 7:
138 self.cur_texture = 0
139 self.texture = self.walk_textures[self.cur_texture][
140 self.character_face_direction
141 ]
142
143
144class MyGame(arcade.Window):
145 """
146 Main application class.
147 """
148
149 def __init__(self):
150 """
151 Initializer for the game
152 """
153 # Call the parent class and set up the window
154 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
155
156 # Track the current state of what key is pressed
157 self.left_pressed = False
158 self.right_pressed = False
159 self.up_pressed = False
160 self.down_pressed = False
161 self.jump_needs_reset = False
162
163 # Our TileMap Object
164 self.tile_map = None
165
166 # Our Scene Object
167 self.scene = None
168
169 # Separate variable that holds the player sprite
170 self.player_sprite = None
171
172 # Our 'physics' engine
173 self.physics_engine = None
174
175 # A Camera that can be used for scrolling the screen
176 self.camera = None
177
178 # A Camera that can be used to draw GUI elements
179 self.gui_camera = None
180
181 self.end_of_map = 0
182
183 # Keep track of the score
184 self.score = 0
185
186 # Load sounds
187 self.collect_coin_sound = arcade.load_sound(":resources:sounds/coin1.wav")
188 self.jump_sound = arcade.load_sound(":resources:sounds/jump1.wav")
189 self.game_over = arcade.load_sound(":resources:sounds/gameover1.wav")
190
191 def setup(self):
192 """Set up the game here. Call this function to restart the game."""
193
194 # Set up the Cameras
195 viewport = (0, 0, self.width, self.height)
196 self.camera = arcade.SimpleCamera(viewport=viewport)
197 self.gui_camera = arcade.SimpleCamera(viewport=viewport)
198
199 # Map name
200 map_name = ":resources:tiled_maps/map_with_ladders.json"
201
202 # Layer Specific Options for the Tilemap
203 layer_options = {
204 LAYER_NAME_PLATFORMS: {
205 "use_spatial_hash": True,
206 },
207 LAYER_NAME_MOVING_PLATFORMS: {
208 "use_spatial_hash": False,
209 },
210 LAYER_NAME_LADDERS: {
211 "use_spatial_hash": True,
212 },
213 LAYER_NAME_COINS: {
214 "use_spatial_hash": True,
215 },
216 }
217
218 # Load in TileMap
219 self.tile_map = arcade.load_tilemap(map_name, TILE_SCALING, layer_options)
220
221 # Initiate New Scene with our TileMap, this will automatically add all layers
222 # from the map as SpriteLists in the scene in the proper order.
223 self.scene = arcade.Scene.from_tilemap(self.tile_map)
224
225 # Keep track of the score
226 self.score = 0
227
228 # Set up the player, specifically placing it at these coordinates.
229 self.player_sprite = PlayerCharacter()
230 self.player_sprite.center_x = PLAYER_START_X
231 self.player_sprite.center_y = PLAYER_START_Y
232 self.scene.add_sprite(LAYER_NAME_PLAYER, self.player_sprite)
233
234 # Calculate the right edge of the my_map in pixels
235 self.end_of_map = self.tile_map.width * GRID_PIXEL_SIZE
236
237 # --- Other stuff
238 # Set the background color
239 if self.tile_map.background_color:
240 arcade.set_background_color(self.tile_map.background_color)
241
242 # Create the 'physics engine'
243 self.physics_engine = arcade.PhysicsEnginePlatformer(
244 self.player_sprite,
245 platforms=self.scene[LAYER_NAME_MOVING_PLATFORMS],
246 gravity_constant=GRAVITY,
247 ladders=self.scene[LAYER_NAME_LADDERS],
248 walls=self.scene[LAYER_NAME_PLATFORMS]
249 )
250
251 def on_draw(self):
252 """Render the screen."""
253
254 # Clear the screen to the background color
255 self.clear()
256
257 # Activate the game camera
258 self.camera.use()
259
260 # Draw our Scene
261 self.scene.draw()
262
263 # Activate the GUI camera before drawing GUI elements
264 self.gui_camera.use()
265
266 # Draw our score on the screen, scrolling it with the viewport
267 score_text = f"Score: {self.score}"
268 arcade.draw_text(
269 score_text,
270 10,
271 10,
272 arcade.csscolor.BLACK,
273 18,
274 )
275
276 # Draw hit boxes.
277 # for wall in self.wall_list:
278 # wall.draw_hit_box(arcade.color.BLACK, 3)
279 #
280 # self.player_sprite.draw_hit_box(arcade.color.RED, 3)
281
282 def process_keychange(self):
283 """
284 Called when we change a key up/down or we move on/off a ladder.
285 """
286 # Process up/down
287 if self.up_pressed and not self.down_pressed:
288 if self.physics_engine.is_on_ladder():
289 self.player_sprite.change_y = PLAYER_MOVEMENT_SPEED
290 elif (
291 self.physics_engine.can_jump(y_distance=10)
292 and not self.jump_needs_reset
293 ):
294 self.player_sprite.change_y = PLAYER_JUMP_SPEED
295 self.jump_needs_reset = True
296 arcade.play_sound(self.jump_sound)
297 elif self.down_pressed and not self.up_pressed:
298 if self.physics_engine.is_on_ladder():
299 self.player_sprite.change_y = -PLAYER_MOVEMENT_SPEED
300
301 # Process up/down when on a ladder and no movement
302 if self.physics_engine.is_on_ladder():
303 if not self.up_pressed and not self.down_pressed:
304 self.player_sprite.change_y = 0
305 elif self.up_pressed and self.down_pressed:
306 self.player_sprite.change_y = 0
307
308 # Process left/right
309 if self.right_pressed and not self.left_pressed:
310 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
311 elif self.left_pressed and not self.right_pressed:
312 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
313 else:
314 self.player_sprite.change_x = 0
315
316 def on_key_press(self, key, modifiers):
317 """Called whenever a key is pressed."""
318
319 if key == arcade.key.UP or key == arcade.key.W:
320 self.up_pressed = True
321 elif key == arcade.key.DOWN or key == arcade.key.S:
322 self.down_pressed = True
323 elif key == arcade.key.LEFT or key == arcade.key.A:
324 self.left_pressed = True
325 elif key == arcade.key.RIGHT or key == arcade.key.D:
326 self.right_pressed = True
327
328 self.process_keychange()
329
330 def on_key_release(self, key, modifiers):
331 """Called when the user releases a key."""
332
333 if key == arcade.key.UP or key == arcade.key.W:
334 self.up_pressed = False
335 self.jump_needs_reset = False
336 elif key == arcade.key.DOWN or key == arcade.key.S:
337 self.down_pressed = False
338 elif key == arcade.key.LEFT or key == arcade.key.A:
339 self.left_pressed = False
340 elif key == arcade.key.RIGHT or key == arcade.key.D:
341 self.right_pressed = False
342
343 self.process_keychange()
344
345 def center_camera_to_player(self):
346 screen_center_x = self.player_sprite.center_x - (self.camera.viewport_width / 2)
347 screen_center_y = self.player_sprite.center_y - (
348 self.camera.viewport_height / 2
349 )
350 if screen_center_x < 0:
351 screen_center_x = 0
352 if screen_center_y < 0:
353 screen_center_y = 0
354 player_centered = screen_center_x, screen_center_y
355
356 self.camera.move_to(player_centered, 0.2)
357
358 def on_update(self, delta_time):
359 """Movement and game logic"""
360
361 # Move the player with the physics engine
362 self.physics_engine.update()
363
364 # Update animations
365 if self.physics_engine.can_jump():
366 self.player_sprite.can_jump = False
367 else:
368 self.player_sprite.can_jump = True
369
370 if self.physics_engine.is_on_ladder() and not self.physics_engine.can_jump():
371 self.player_sprite.is_on_ladder = True
372 self.process_keychange()
373 else:
374 self.player_sprite.is_on_ladder = False
375 self.process_keychange()
376
377 # Update Animations
378 self.scene.update_animation(
379 delta_time, [LAYER_NAME_COINS, LAYER_NAME_BACKGROUND, LAYER_NAME_PLAYER]
380 )
381
382 # Update walls, used with moving platforms
383 self.scene.update([LAYER_NAME_MOVING_PLATFORMS])
384
385 # See if we hit any coins
386 coin_hit_list = arcade.check_for_collision_with_list(
387 self.player_sprite, self.scene[LAYER_NAME_COINS]
388 )
389
390 # Loop through each coin we hit (if any) and remove it
391 for coin in coin_hit_list:
392
393 # Figure out how many points this coin is worth
394 if "Points" not in coin.properties:
395 print("Warning, collected a coin without a Points property.")
396 else:
397 points = int(coin.properties["Points"])
398 self.score += points
399
400 # Remove the coin
401 coin.remove_from_sprite_lists()
402 arcade.play_sound(self.collect_coin_sound)
403
404 # Position the camera
405 self.center_camera_to_player()
406
407
408def main():
409 """Main function"""
410 window = MyGame()
411 window.setup()
412 arcade.run()
413
414
415if __name__ == "__main__":
416 main()