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.
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.