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
  9WINDOW_WIDTH = 1280
 10WINDOW_HEIGHT = 720
 11WINDOW_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 GameView(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__(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
 31
 32        # Variable to hold our texture for our player
 33        self.player_texture = arcade.load_texture(
 34            ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
 35        )
 36
 37        # Separate variable that holds the player sprite
 38        self.player_sprite = arcade.Sprite(self.player_texture)
 39        self.player_sprite.center_x = 64
 40        self.player_sprite.center_y = 128
 41
 42        # SpriteList for our player
 43        self.player_list = arcade.SpriteList()
 44        self.player_list.append(self.player_sprite)
 45
 46        # SpriteList for our boxes and ground
 47        # Putting our ground and box Sprites in the same SpriteList
 48        # will make it easier to perform collision detection against
 49        # them later on. Setting the spatial hash to True will make
 50        # collision detection much faster if the objects in this
 51        # SpriteList do not move.
 52        self.wall_list = arcade.SpriteList(use_spatial_hash=True)
 53
 54        # Create the ground
 55        # This shows using a loop to place multiple sprites horizontally
 56        for x in range(0, 1250, 64):
 57            wall = arcade.Sprite(":resources:images/tiles/grassMid.png", scale=TILE_SCALING)
 58            wall.center_x = x
 59            wall.center_y = 32
 60            self.wall_list.append(wall)
 61
 62        # Put some crates on the ground
 63        # This shows using a coordinate list to place sprites
 64        coordinate_list = [[512, 96], [256, 96], [768, 96]]
 65
 66        for coordinate in coordinate_list:
 67            # Add a crate on the ground
 68            wall = arcade.Sprite(
 69                ":resources:images/tiles/boxCrate_double.png", scale=TILE_SCALING
 70            )
 71            wall.position = coordinate
 72            self.wall_list.append(wall)
 73
 74        # Create a Platformer Physics Engine.
 75        # This will handle moving our player as well as collisions between
 76        # the player sprite and whatever SpriteList we specify for the walls.
 77        # It is important to supply static platforms to the walls parameter. There is a
 78        # platforms parameter that is intended for moving platforms.
 79        # If a platform is supposed to move, and is added to the walls list,
 80        # it will not be moved.
 81        self.physics_engine = arcade.PhysicsEnginePlatformer(
 82            self.player_sprite, walls=self.wall_list, gravity_constant=GRAVITY
 83        )
 84
 85        self.background_color = arcade.csscolor.CORNFLOWER_BLUE
 86
 87    def setup(self):
 88        """Set up the game here. Call this function to restart the game."""
 89        pass
 90
 91    def on_draw(self):
 92        """Render the screen."""
 93
 94        # Clear the screen to the background color
 95        self.clear()
 96
 97        # Draw our sprites
 98        self.player_list.draw()
 99        self.wall_list.draw()
100
101    def on_update(self, delta_time):
102        """Movement and Game Logic"""
103
104        # Move the player using our physics engine
105        self.physics_engine.update()
106
107    def on_key_press(self, key, modifiers):
108        """Called whenever a key is pressed."""
109
110        if key == arcade.key.UP or key == arcade.key.W:
111            if self.physics_engine.can_jump():
112                self.player_sprite.change_y = PLAYER_JUMP_SPEED
113
114        if key == arcade.key.LEFT or key == arcade.key.A:
115            self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
116        elif key == arcade.key.RIGHT or key == arcade.key.D:
117            self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
118
119    def on_key_release(self, key, modifiers):
120        """Called whenever a key is released."""
121
122        if key == arcade.key.LEFT or key == arcade.key.A:
123            self.player_sprite.change_x = 0
124        elif key == arcade.key.RIGHT or key == arcade.key.D:
125            self.player_sprite.change_x = 0
126
127
128def main():
129    """Main function"""
130    window = GameView()
131    window.setup()
132    arcade.run()
133
134
135if __name__ == "__main__":
136    main()

Run This Chapter

python -m arcade.examples.platform_tutorial.05_add_gravity