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#

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