Step 5 - Add Scrolling

We can have our window be a small viewport into a much larger world by adding scrolling.

The viewport margins control how close you can get to the edge of the screen before the camera starts scrolling.

Add Scrolling
  1"""
  2Platformer Game
  3"""
  4import arcade
  5
  6# Constants
  7SCREEN_WIDTH = 1000
  8SCREEN_HEIGHT = 650
  9SCREEN_TITLE = "Platformer"
 10
 11# Constants used to scale our sprites from their original size
 12CHARACTER_SCALING = 1
 13TILE_SCALING = 0.5
 14COIN_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# How many pixels to keep as a minimum margin between the character
 22# and the edge of the screen.
 23LEFT_VIEWPORT_MARGIN = 250
 24RIGHT_VIEWPORT_MARGIN = 250
 25BOTTOM_VIEWPORT_MARGIN = 50
 26TOP_VIEWPORT_MARGIN = 100
 27
 28
 29class MyGame(arcade.Window):
 30    """
 31    Main application class.
 32    """
 33
 34    def __init__(self):
 35
 36        # Call the parent class and set up the window
 37        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
 38
 39        # These are 'lists' that keep track of our sprites. Each sprite should
 40        # go into a list.
 41        self.coin_list = None
 42        self.wall_list = None
 43        self.player_list = None
 44
 45        # Separate variable that holds the player sprite
 46        self.player_sprite = None
 47
 48        # Our physics engine
 49        self.physics_engine = None
 50
 51        # Used to keep track of our scrolling
 52        self.view_bottom = 0
 53        self.view_left = 0
 54
 55        arcade.set_background_color(arcade.csscolor.CORNFLOWER_BLUE)
 56
 57    def setup(self):
 58        """ Set up the game here. Call this function to restart the game. """
 59
 60        # Used to keep track of our scrolling
 61        self.view_bottom = 0
 62        self.view_left = 0
 63
 64        # Create the Sprite lists
 65        self.player_list = arcade.SpriteList()
 66        self.wall_list = arcade.SpriteList()
 67        self.coin_list = arcade.SpriteList()
 68
 69        # Set up the player, specifically placing it at these coordinates.
 70        image_source = ":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"
 71        self.player_sprite = arcade.Sprite(image_source, CHARACTER_SCALING)
 72        self.player_sprite.center_x = 64
 73        self.player_sprite.center_y = 96
 74        self.player_list.append(self.player_sprite)
 75
 76        # Create the ground
 77        # This shows using a loop to place multiple sprites horizontally
 78        for x in range(0, 1250, 64):
 79            wall = arcade.Sprite(":resources:images/tiles/grassMid.png", TILE_SCALING)
 80            wall.center_x = x
 81            wall.center_y = 32
 82            self.wall_list.append(wall)
 83
 84        # Put some crates on the ground
 85        # This shows using a coordinate list to place sprites
 86        coordinate_list = [[512, 96],
 87                           [256, 96],
 88                           [768, 96]]
 89
 90        for coordinate in coordinate_list:
 91            # Add a crate on the ground
 92            wall = arcade.Sprite(":resources:images/tiles/boxCrate_double.png", TILE_SCALING)
 93            wall.position = coordinate
 94            self.wall_list.append(wall)
 95
 96        # Create the 'physics engine'
 97        self.physics_engine = arcade.PhysicsEnginePlatformer(self.player_sprite,
 98                                                             self.wall_list,
 99                                                             GRAVITY)
100
101    def on_draw(self):
102        """ Render the screen. """
103
104        # Clear the screen to the background color
105        arcade.start_render()
106
107        # Draw our sprites
108        self.wall_list.draw()
109        self.coin_list.draw()
110        self.player_list.draw()
111
112    def on_key_press(self, key, modifiers):
113        """Called whenever a key is pressed. """
114
115        if key == arcade.key.UP or key == arcade.key.W:
116            if self.physics_engine.can_jump():
117                self.player_sprite.change_y = PLAYER_JUMP_SPEED
118        elif key == arcade.key.LEFT or key == arcade.key.A:
119            self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
120        elif key == arcade.key.RIGHT or key == arcade.key.D:
121            self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
122
123    def on_key_release(self, key, modifiers):
124        """Called when the user releases a key. """
125
126        if key == arcade.key.LEFT or key == arcade.key.A:
127            self.player_sprite.change_x = 0
128        elif key == arcade.key.RIGHT or key == arcade.key.D:
129            self.player_sprite.change_x = 0
130
131    def on_update(self, delta_time):
132        """ Movement and game logic """
133
134        # Move the player with the physics engine
135        self.physics_engine.update()
136
137        # --- Manage Scrolling ---
138
139        # Track if we need to change the viewport
140
141        changed = False
142
143        # Scroll left
144        left_boundary = self.view_left + LEFT_VIEWPORT_MARGIN
145        if self.player_sprite.left < left_boundary:
146            self.view_left -= left_boundary - self.player_sprite.left
147            changed = True
148
149        # Scroll right
150        right_boundary = self.view_left + SCREEN_WIDTH - RIGHT_VIEWPORT_MARGIN
151        if self.player_sprite.right > right_boundary:
152            self.view_left += self.player_sprite.right - right_boundary
153            changed = True
154
155        # Scroll up
156        top_boundary = self.view_bottom + SCREEN_HEIGHT - TOP_VIEWPORT_MARGIN
157        if self.player_sprite.top > top_boundary:
158            self.view_bottom += self.player_sprite.top - top_boundary
159            changed = True
160
161        # Scroll down
162        bottom_boundary = self.view_bottom + BOTTOM_VIEWPORT_MARGIN
163        if self.player_sprite.bottom < bottom_boundary:
164            self.view_bottom -= bottom_boundary - self.player_sprite.bottom
165            changed = True
166
167        if changed:
168            # Only scroll to integers. Otherwise we end up with pixels that
169            # don't line up on the screen
170            self.view_bottom = int(self.view_bottom)
171            self.view_left = int(self.view_left)
172
173            # Do the scrolling
174            arcade.set_viewport(self.view_left,
175                                SCREEN_WIDTH + self.view_left,
176                                self.view_bottom,
177                                SCREEN_HEIGHT + self.view_bottom)
178
179
180def main():
181    """ Main method """
182    window = MyGame()
183    window.setup()
184    arcade.run()
185
186
187if __name__ == "__main__":
188    main()

Note

Work at changing the viewport margins to something that you like.