Step 5 - Add Gravity#
The previous example is great for top-down games, 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.
GRAVITY = 1
PLAYER_JUMP_SPEED = 20
Now, let’s change the Physics Engine we created in the __init__
function to a
arcade.PhysicsEnginePlatformer
instead of a arcade.PhysicsEngineSimple
.
This new physics engine will handle jumping and gravity for us, and will do even more
in later chapters.
self.physics_engine = arcade.PhysicsEnginePlatformer(
self.player_sprite, walls=self.wall_list, gravity_constant=GRAVITY
)
This is very similar to how we created the original simple physics engine, with two exceptions.
The first being that we have sent it our gravity constant. The second being that we have explicitly
sent our wall SpriteList to the walls
parameter. This is a very important step. The platformer
physics engine has two parameters for collidable objects, one named platforms
and one named walls
.
The difference is that objects sent to platforms
are intended to be moved. They are moved in the same
way the player is, by modifying their change_x
and change_y
values. Objects sent to the
walls
parameter will not be moved. The reason this is so important is that non-moving walls have much
faster performance than movable platforms.
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.
Lastly we will give our player the ability to jump. Modify the on_key_press
and on_key_release
functions.
We’ll remove the up/down statements we had before, and make UP
jump when pressed.
if key == arcade.key.UP or key == arcade.key.W:
if self.physics_engine.can_jump():
self.player_sprite.change_y = PLAYER_JUMP_SPEED
The can_jump()
check from our physics engine will make it so that we can only jump if we are touching the
ground. You can remove this function to allow jumping in mid-air for some interesting results. Think about how
you might implement a double-jump system using this.
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 = 800
10SCREEN_HEIGHT = 600
11SCREEN_TITLE = "Platformer"
12
13# Constants used to scale our sprites from their original size
14TILE_SCALING = 0.5
15
16# Movement speed of player, in pixels per frame
17PLAYER_MOVEMENT_SPEED = 5
18GRAVITY = 1
19PLAYER_JUMP_SPEED = 20
20
21
22class MyGame(arcade.Window):
23 """
24 Main application class.
25 """
26
27 def __init__(self):
28
29 # Call the parent class and set up the window
30 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
31
32 # Variable to hold our texture for our player
33 self.player_texture = arcade.load_texture(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png")
34
35 # Separate variable that holds the player sprite
36 self.player_sprite = arcade.Sprite(self.player_texture)
37 self.player_sprite.center_x = 64
38 self.player_sprite.center_y = 128
39
40 # SpriteList for our player
41 self.player_list = arcade.SpriteList()
42 self.player_list.append(self.player_sprite)
43
44 # SpriteList for our boxes and ground
45 # Putting our ground and box Sprites in the same SpriteList
46 # will make it easier to perform collision detection against
47 # them later on. Setting the spatial hash to True will make
48 # collision detection much faster if the objects in this
49 # SpriteList do not move.
50 self.wall_list = arcade.SpriteList(use_spatial_hash=True)
51
52 # Create the ground
53 # This shows using a loop to place multiple sprites horizontally
54 for x in range(0, 1250, 64):
55 wall = arcade.Sprite(":resources:images/tiles/grassMid.png", scale=TILE_SCALING)
56 wall.center_x = x
57 wall.center_y = 32
58 self.wall_list.append(wall)
59
60 # Put some crates on the ground
61 # This shows using a coordinate list to place sprites
62 coordinate_list = [[512, 96], [256, 96], [768, 96]]
63
64 for coordinate in coordinate_list:
65 # Add a crate on the ground
66 wall = arcade.Sprite(
67 ":resources:images/tiles/boxCrate_double.png", scale=TILE_SCALING
68 )
69 wall.position = coordinate
70 self.wall_list.append(wall)
71
72 # Create a Platformer Physics Engine.
73 # This will handle moving our player as well as collisions between
74 # the player sprite and whatever SpriteList we specify for the walls.
75 # It is important to supply static platforms to the walls parameter. There is a
76 # platforms parameter that is intended for moving platforms.
77 # If a platform is supposed to move, and is added to the walls list,
78 # it will not be moved.
79 self.physics_engine = arcade.PhysicsEnginePlatformer(
80 self.player_sprite, walls=self.wall_list, gravity_constant=GRAVITY
81 )
82
83 self.background_color = arcade.csscolor.CORNFLOWER_BLUE
84
85 def setup(self):
86 """Set up the game here. Call this function to restart the game."""
87 pass
88
89 def on_draw(self):
90 """Render the screen."""
91
92 # Clear the screen to the background color
93 self.clear()
94
95 # Draw our sprites
96 self.player_list.draw()
97 self.wall_list.draw()
98
99 def on_update(self, delta_time):
100 """Movement and Game Logic"""
101
102 # Move the player using our physics engine
103 self.physics_engine.update()
104
105 def on_key_press(self, key, modifiers):
106 """Called whenever a key is pressed."""
107
108 if key == arcade.key.UP or key == arcade.key.W:
109 if self.physics_engine.can_jump():
110 self.player_sprite.change_y = PLAYER_JUMP_SPEED
111
112 if key == arcade.key.LEFT or key == arcade.key.A:
113 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
114 elif key == arcade.key.RIGHT or key == arcade.key.D:
115 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
116
117 def on_key_release(self, key, modifiers):
118 """Called whenever a key is released."""
119
120 if key == arcade.key.LEFT or key == arcade.key.A:
121 self.player_sprite.change_x = 0
122 elif key == arcade.key.RIGHT or key == arcade.key.D:
123 self.player_sprite.change_x = 0
124
125
126def main():
127 """Main function"""
128 window = MyGame()
129 window.setup()
130 arcade.run()
131
132
133if __name__ == "__main__":
134 main()
Run This Chapter#
python -m arcade.examples.platform_tutorial.05_add_gravity