Move with a Scrolling Screen - Margins
Unlike the Move with a Scrolling Screen - Centered which centers the camera on the player, this example only moves the camera if the user gets within so many pixels of the edge. It allows for a ‘box’ in the middle where the user can move around and NOT move the camera.

sprite_move_scrolling_box.py
1"""
2Scroll around a large screen.
3
4Artwork from https://kenney.nl
5
6If Python and Arcade are installed, this example can be run from the command line with:
7python -m arcade.examples.sprite_move_scrolling_box
8"""
9
10import random
11import arcade
12
13SPRITE_SCALING = 0.5
14
15WINDOW_WIDTH = 1280
16WINDOW_HEIGHT = 720
17WINDOW_TITLE = "Sprite Move with Scrolling Screen Example"
18
19# How many pixels to keep as a minimum margin between the character
20# and the edge of the screen.
21VIEWPORT_MARGIN = 200
22HORIZONTAL_BOUNDARY = WINDOW_WIDTH / 2.0 - VIEWPORT_MARGIN
23VERTICAL_BOUNDARY = WINDOW_HEIGHT / 2.0 - VIEWPORT_MARGIN
24# If the player moves further than this boundary away from the camera we use a
25# constraint to move the camera
26CAMERA_BOUNDARY = arcade.LRBT(
27 -HORIZONTAL_BOUNDARY,
28 HORIZONTAL_BOUNDARY,
29 -VERTICAL_BOUNDARY,
30 VERTICAL_BOUNDARY,
31)
32
33# How fast the camera pans to the player. 1.0 is instant.
34CAMERA_SPEED = 0.1
35
36# How fast the character moves
37PLAYER_MOVEMENT_SPEED = 7
38
39
40class GameView(arcade.View):
41 """ Main application class. """
42
43 def __init__(self):
44 """
45 Initializer
46 """
47 super().__init__()
48
49 # Sprite lists
50 self.player_list = None
51 self.wall_list = None
52
53 # Set up the player
54 self.player_sprite = None
55
56 self.physics_engine = None
57
58 # Track the current state of what key is pressed
59 self.left_pressed = False
60 self.right_pressed = False
61 self.up_pressed = False
62 self.down_pressed = False
63
64 self.camera_sprites = arcade.camera.Camera2D()
65 self.camera_gui = arcade.camera.Camera2D()
66
67 def setup(self):
68 """ Set up the game and initialize the variables. """
69
70 # Sprite lists
71 self.player_list = arcade.SpriteList()
72 self.wall_list = arcade.SpriteList()
73
74 # Set up the player
75 self.player_sprite = arcade.Sprite(
76 ":resources:images/animated_characters/female_person/femalePerson_idle.png",
77 scale=0.4,
78 )
79 self.player_sprite.center_x = 256
80 self.player_sprite.center_y = 512
81 self.player_list.append(self.player_sprite)
82
83 # -- Set up several columns of walls
84 for x in range(200, 1650, 210):
85 for y in range(0, 1600, 64):
86 # Randomly skip a box so the player can find a way through
87 if random.randrange(5) > 0:
88 wall = arcade.Sprite(
89 ":resources:images/tiles/grassCenter.png",
90 scale=SPRITE_SCALING,
91 )
92 wall.center_x = x
93 wall.center_y = y
94 self.wall_list.append(wall)
95
96 self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list)
97
98 # Set the background color
99 self.background_color = arcade.color.AMAZON
100
101 def on_draw(self):
102 """
103 Render the screen.
104 """
105
106 # This command has to happen before we start drawing
107 self.clear()
108
109 # Select the camera we'll use to draw all our sprites
110 self.camera_sprites.use()
111
112 # Draw all the sprites.
113 self.wall_list.draw()
114 self.player_list.draw()
115
116 # Draw the box that we work to make sure the user stays inside of.
117 # This is just for illustration purposes. You'd want to remove this
118 # in your game.
119 camera_x, camera_y = self.camera_sprites.position
120 arcade.draw_rect_outline(
121 arcade.XYWH(camera_x, camera_y, CAMERA_BOUNDARY.width, CAMERA_BOUNDARY.height),
122 arcade.color.RED, 2
123 )
124
125 # Select the (unscrolled) camera for our GUI
126 self.camera_gui.use()
127
128 # Draw the GUI
129 arcade.draw_rect_filled(
130 arcade.rect.XYWH(self.width // 2, 20, self.width, 40),
131 color=arcade.color.ALMOND
132 )
133 text = (
134 f"Scroll value: ("
135 f"{self.camera_sprites.position[0]:5.1f}",
136 f"{self.camera_sprites.position[1]:5.1f})"
137 )
138 arcade.draw_text(text, 10, 10, arcade.color.BLACK_BEAN, 20)
139
140
141 def on_key_press(self, key, modifiers):
142 """Called whenever a key is pressed. """
143
144 if key == arcade.key.UP:
145 self.up_pressed = True
146 elif key == arcade.key.DOWN:
147 self.down_pressed = True
148 elif key == arcade.key.LEFT:
149 self.left_pressed = True
150 elif key == arcade.key.RIGHT:
151 self.right_pressed = True
152
153 def on_key_release(self, key, modifiers):
154 """Called when the user releases a key. """
155
156 if key == arcade.key.UP:
157 self.up_pressed = False
158 elif key == arcade.key.DOWN:
159 self.down_pressed = False
160 elif key == arcade.key.LEFT:
161 self.left_pressed = False
162 elif key == arcade.key.RIGHT:
163 self.right_pressed = False
164
165 def on_update(self, delta_time):
166 """ Movement and game logic """
167
168 # Calculate speed based on the keys pressed
169 self.player_sprite.change_x = 0
170 self.player_sprite.change_y = 0
171
172 if self.up_pressed and not self.down_pressed:
173 self.player_sprite.change_y = PLAYER_MOVEMENT_SPEED
174 elif self.down_pressed and not self.up_pressed:
175 self.player_sprite.change_y = -PLAYER_MOVEMENT_SPEED
176 if self.left_pressed and not self.right_pressed:
177 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
178 elif self.right_pressed and not self.left_pressed:
179 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
180
181 # Call update on all sprites (The sprites don't do much in this
182 # example though.)
183 self.physics_engine.update()
184
185 # Scroll the screen to the player
186 self.scroll_to_player()
187
188 def scroll_to_player(self):
189 """
190 Scroll the window to the player.
191 This method will attempt to keep the player at least VIEWPORT_MARGIN
192 pixels away from the edge.
193
194 if CAMERA_SPEED is 1, the camera will immediately move to the desired position.
195 Anything between 0 and 1 will have the camera move to the location with a smoother
196 pan.
197 """
198
199 # --- Manage Scrolling ---
200 new_position = arcade.camera.grips.constrain_boundary_xy(
201 self.camera_sprites.view_data, CAMERA_BOUNDARY, self.player_sprite.position
202 )
203
204 self.camera_sprites.position = arcade.math.lerp_2d(
205 self.camera_sprites.position, (new_position[0], new_position[1]), CAMERA_SPEED
206 )
207
208 def on_resize(self, width: int, height: int):
209 """
210 Resize window
211 Handle the user grabbing the edge and resizing the window.
212 """
213 super().on_resize(width, height)
214 self.camera_sprites.match_window()
215 self.camera_gui.match_window(position=True)
216
217
218def main():
219 """ Main function """
220 # Create a window class. This is what actually shows up on screen
221 window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
222
223 # Create and setup the GameView
224 game = GameView()
225 game.setup()
226
227 # Show GameView on screen
228 window.show_view(game)
229
230 # Start the arcade game loop
231 arcade.run()
232
233
234if __name__ == "__main__":
235 main()