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.
TILE_SCALING = 0.5
At the end of the setup
method, change the physics engine to
PhysicsEnginePlatformer
and include gravity as a parameter.
)
wall.position = coordinate
self.scene.add_sprite("Walls", wall)
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
2 # Draw our Scene
3 self.scene.draw()
4
5 def on_key_press(self, key, modifiers):
6 """Called whenever a key is pressed."""
7
8 if key == arcade.key.UP or key == arcade.key.W:
9 if self.physics_engine.can_jump():
10 self.player_sprite.change_y = PLAYER_JUMP_SPEED
11 elif key == arcade.key.LEFT or key == arcade.key.A:
12 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
13 elif key == arcade.key.RIGHT or key == arcade.key.D:
14 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
15
16 def on_key_release(self, key, modifiers):
17 """Called when the user releases a key."""
18
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"""
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
18
19# Movement speed of player, in pixels per frame
20PLAYER_MOVEMENT_SPEED = 5
21GRAVITY = 1
22PLAYER_JUMP_SPEED = 20
23
24
25class MyGame(arcade.Window):
26 """
27 Main application class.
28 """
29
30 def __init__(self):
31
32 # Call the parent class and set up the window
33 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
34
35 # Our Scene Object
36 self.scene = None
37
38 # Separate variable that holds the player sprite
39 self.player_sprite = None
40
41 # Our physics engine
42 self.physics_engine = None
43
44 self.background_color = arcade.csscolor.CORNFLOWER_BLUE
45
46 def setup(self):
47 """Set up the game here. Call this function to restart the game."""
48
49 # Initialize Scene
50 self.scene = arcade.Scene()
51
52 # Set up the player, specifically placing it at these coordinates.
53 image_source = ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
54 self.player_sprite = arcade.Sprite(image_source, CHARACTER_SCALING)
55 self.player_sprite.center_x = 64
56 self.player_sprite.center_y = 128
57 self.scene.add_sprite("Player", self.player_sprite)
58
59 # Create the ground
60 # This shows using a loop to place multiple sprites horizontally
61 for x in range(0, 1250, 64):
62 wall = arcade.Sprite(":resources:images/tiles/grassMid.png", TILE_SCALING)
63 wall.center_x = x
64 wall.center_y = 32
65 self.scene.add_sprite("Walls", wall)
66
67 # Put some crates on the ground
68 # This shows using a coordinate list to place sprites
69 coordinate_list = [[512, 96], [256, 96], [768, 96]]
70
71 for coordinate in coordinate_list:
72 # Add a crate on the ground
73 wall = arcade.Sprite(
74 ":resources:images/tiles/boxCrate_double.png", TILE_SCALING
75 )
76 wall.position = coordinate
77 self.scene.add_sprite("Walls", wall)
78
79 # Create the 'physics engine'
80 self.physics_engine = arcade.PhysicsEnginePlatformer(
81 self.player_sprite, gravity_constant=GRAVITY, walls=self.scene["Walls"]
82 )
83
84 def on_draw(self):
85 """Render the screen."""
86
87 # Clear the screen to the background color
88 self.clear()
89
90 # Draw our Scene
91 self.scene.draw()
92
93 def on_key_press(self, key, modifiers):
94 """Called whenever a key is pressed."""
95
96 if key == arcade.key.UP or key == arcade.key.W:
97 if self.physics_engine.can_jump():
98 self.player_sprite.change_y = PLAYER_JUMP_SPEED
99 elif key == arcade.key.LEFT or key == arcade.key.A:
100 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
101 elif key == arcade.key.RIGHT or key == arcade.key.D:
102 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
103
104 def on_key_release(self, key, modifiers):
105 """Called when the user releases a key."""
106
107 if key == arcade.key.LEFT or key == arcade.key.A:
108 self.player_sprite.change_x = 0
109 elif key == arcade.key.RIGHT or key == arcade.key.D:
110 self.player_sprite.change_x = 0
111
112 def on_update(self, delta_time):
113 """Movement and game logic"""
114
115 # Move the player with the physics engine
116 self.physics_engine.update()
117
118
119def main():
120 """Main function"""
121 window = MyGame()
122 window.setup()
123 arcade.run()
124
125
126if __name__ == "__main__":
127 main()