Step 5 - Add Gravity#
The previous example great for top-down, but what if it is a side view with jumping like our platformer? We need to add gravity. First, let’s define a constant to represent the acceleration for gravity, and one for a jump speed.
# Movement speed of player, in pixels per frame
PLAYER_MOVEMENT_SPEED = 5
At the end of the setup
method, change the physics engine to
PhysicsEnginePlatformer
and include gravity as a parameter.
self.scene.add_sprite("Walls", wall)
# Create the 'physics engine'
self.physics_engine = arcade.PhysicsEnginePlatformer(
We are sending our SpriteList
for the things the player should collide with to the walls
parameter of the the physics engine. As we’ll see in later chapters, the platformer physics engine
has a platforms
and walls
parameter. The difference between these is very important. Static
non-moving spritelists should always be sent to the walls
parameter, and moving sprites should
be sent to the platforms
parameter. Ensuring you do this will have extreme benefits to performance.
Adding static sprites via the platforms
parameter is roughly an O(n) operation, meaning performance will
linearly get worse as you add more sprites. If you add your static sprites via the walls
parameter, then
it is nearly O(1) and there is essentially no difference between for example 100 and 50,000 non-moving sprites.
We also see here some new syntax relating to our Scene
object. You can access the scene like you would a
Python dictionary in order to get your SpriteLists from it. There are multiple ways to access the SpriteLists
within a Scene but this is the easiest and most straight forward. You could alternatively use scene.get_sprite_list("My Layer")
.
Then, modify the key down and key up event handlers. We’ll remove the up/down statements we had before, and make ‘UP’ jump when pressed.
1 self.scene.draw()
2
3 def on_key_press(self, key, modifiers):
4 """Called whenever a key is pressed."""
5
6 if key == arcade.key.UP or key == arcade.key.W:
7 if self.physics_engine.can_jump():
8 self.player_sprite.change_y = PLAYER_JUMP_SPEED
9 elif key == arcade.key.LEFT or key == arcade.key.A:
10 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
11 elif key == arcade.key.RIGHT or key == arcade.key.D:
12 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
13
14 def on_key_release(self, key, modifiers):
15 """Called when the user releases a key."""
16
17 if key == arcade.key.LEFT or key == arcade.key.A:
18 self.player_sprite.change_x = 0
Note
You can change how the user jumps by changing the gravity and jump constants. Lower values for both will make for a more “floaty” character. Higher values make for a faster-paced game.
Source Code#
1"""
2Platformer Game
3
4python -m arcade.examples.platform_tutorial.05_add_gravity
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
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 # Our Scene Object
34 self.scene = None
35
36 # Separate variable that holds the player sprite
37 self.player_sprite = None
38
39 # Our physics engine
40 self.physics_engine = None
41
42 self.background_color = arcade.csscolor.CORNFLOWER_BLUE
43
44 def setup(self):
45 """Set up the game here. Call this function to restart the game."""
46
47 # Initialize Scene
48 self.scene = arcade.Scene()
49
50 # Set up the player, specifically placing it at these coordinates.
51 image_source = ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
52 self.player_sprite = arcade.Sprite(image_source, CHARACTER_SCALING)
53 self.player_sprite.center_x = 64
54 self.player_sprite.center_y = 128
55 self.scene.add_sprite("Player", self.player_sprite)
56
57 # Create the ground
58 # This shows using a loop to place multiple sprites horizontally
59 for x in range(0, 1250, 64):
60 wall = arcade.Sprite(":resources:images/tiles/grassMid.png", TILE_SCALING)
61 wall.center_x = x
62 wall.center_y = 32
63 self.scene.add_sprite("Walls", wall)
64
65 # Put some crates on the ground
66 # This shows using a coordinate list to place sprites
67 coordinate_list = [[512, 96], [256, 96], [768, 96]]
68
69 for coordinate in coordinate_list:
70 # Add a crate on the ground
71 wall = arcade.Sprite(
72 ":resources:images/tiles/boxCrate_double.png", TILE_SCALING
73 )
74 wall.position = coordinate
75 self.scene.add_sprite("Walls", wall)
76
77 # Create the 'physics engine'
78 self.physics_engine = arcade.PhysicsEnginePlatformer(
79 self.player_sprite, gravity_constant=GRAVITY, walls=self.scene["Walls"]
80 )
81
82 def on_draw(self):
83 """Render the screen."""
84
85 # Clear the screen to the background color
86 self.clear()
87
88 # Draw our Scene
89 self.scene.draw()
90
91 def on_key_press(self, key, modifiers):
92 """Called whenever a key is pressed."""
93
94 if key == arcade.key.UP or key == arcade.key.W:
95 if self.physics_engine.can_jump():
96 self.player_sprite.change_y = PLAYER_JUMP_SPEED
97 elif key == arcade.key.LEFT or key == arcade.key.A:
98 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
99 elif key == arcade.key.RIGHT or key == arcade.key.D:
100 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
101
102 def on_key_release(self, key, modifiers):
103 """Called when the user releases a key."""
104
105 if key == arcade.key.LEFT or key == arcade.key.A:
106 self.player_sprite.change_x = 0
107 elif key == arcade.key.RIGHT or key == arcade.key.D:
108 self.player_sprite.change_x = 0
109
110 def on_update(self, delta_time):
111 """Movement and game logic"""
112
113 # Move the player with the physics engine
114 self.physics_engine.update()
115
116
117def main():
118 """Main function"""
119 window = MyGame()
120 window.setup()
121 arcade.run()
122
123
124if __name__ == "__main__":
125 main()