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.

05_add_gravity.py - Add Gravity#
TILE_SCALING = 0.5

At the end of the setup method, change the physics engine to PhysicsEnginePlatformer and include gravity as a parameter.

05_add_gravity.py - Add Gravity#
            )
            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.

05_add_gravity.py - Add Gravity#
 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#

05_add_gravity.py - Add Gravity#
  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()