Parallax
Warning
This example is experimental!
The features it uses may change rapidly or be replaced in future Arcade releases!

background_parallax.py
1"""
2Parallax scrolling layers move slower the "farther" away they are.
3
4Use the right and left arrow keys to move the car.
5
6Arcade's ParallaxGroup allows you to implement this technique quickly
7to create more satisfying backgrounds to your games. The example below
8demonstrates how to fake an endless world by adjusting ParallaxGroup's
9position & offset values. For limited worlds or backgrounds, limit the
10repositioning to only occur within certain bounds, or delete it.
11
12If Python and Arcade are installed, this example can be run from the command line with:
13python -m arcade.examples.background_parallax
14"""
15
16import arcade
17import arcade.future.background as background
18
19# How much we'll scale up our pixel art
20PIXEL_SCALE = 3
21
22# The original & scaled heights of our background layer image data in pixels.
23ORIGINAL_BG_LAYER_HEIGHT_PX = 240
24SCALED_BG_LAYER_HEIGHT_PX = ORIGINAL_BG_LAYER_HEIGHT_PX * PIXEL_SCALE
25
26
27WINDOW_TITLE = "Background Group Example"
28WINDOW_WIDTH = 1280
29WINDOW_HEIGHT = SCALED_BG_LAYER_HEIGHT_PX
30
31
32PLAYER_SPEED = 300 # The player's speed in pixels / second
33CAMERA_SPEED = 0.1
34
35
36class GameView(arcade.View):
37 def __init__(self):
38 super().__init__()
39
40 # Set the background color to match the sky in the background images
41 self.background_color = (162, 84, 162, 255)
42
43 self.camera = arcade.camera.Camera2D()
44
45 # Create a background group to hold all the landscape's layers
46 self.backgrounds = background.ParallaxGroup()
47
48 # Calculate the current size of each background fill layer in pixels
49 bg_layer_size_px = (WINDOW_WIDTH, SCALED_BG_LAYER_HEIGHT_PX)
50
51 # Import the image data for each background layer.
52 # Unlike sprites, the scale argument doesn't resize the layer
53 # itself. Instead, it changes the zoom level, while depth
54 # controls how fast each layer scrolls. This means you have to
55 # pass a correct size value when adding a layer. We calculated
56 # this above.
57 self.backgrounds.add_from_file(
58 ":resources:/images/miami_synth_parallax/layers/back.png",
59 size=bg_layer_size_px,
60 depth=10.0,
61 scale=PIXEL_SCALE
62 )
63 self.backgrounds.add_from_file(
64 ":resources:/images/miami_synth_parallax/layers/buildings.png",
65 size=bg_layer_size_px,
66 depth=5.0,
67 scale=PIXEL_SCALE
68 )
69 self.backgrounds.add_from_file(
70 ":resources:/images/miami_synth_parallax/layers/palms.png",
71 size=bg_layer_size_px,
72 depth=3.0,
73 scale=PIXEL_SCALE
74 )
75 self.backgrounds.add_from_file(
76 ":resources:/images/miami_synth_parallax/layers/highway.png",
77 size=bg_layer_size_px,
78 depth=1.0,
79 scale=PIXEL_SCALE
80 )
81
82 # Load the car texture and create a flipped version
83 self.car_texture_right = arcade.texture.load_texture(
84 ":resources:/images/miami_synth_parallax/car/car-idle.png")
85 self.car_texture_left = self.car_texture_right.flip_left_right()
86
87 # Create & position the player sprite in the center of the camera's view
88 self.player_sprite = arcade.Sprite(
89 self.car_texture_right,
90 center_x=self.camera.viewport_width // 2, center_y=-200.0, scale=PIXEL_SCALE
91 )
92
93 self.player_sprite.bottom = 0
94
95 # Track the player's x velocity
96 self.x_velocity = 0
97
98 def pan_camera_to_player(self):
99 # Move the camera toward the center of the player's sprite
100 target_x = self.player_sprite.center_x
101 self.camera.position = arcade.math.lerp_2d(
102 self.camera.position,
103 (target_x, self.height//2),
104 CAMERA_SPEED
105 )
106
107 def on_update(self, delta_time: float):
108 # Move the player in our infinite world
109 self.player_sprite.center_x += self.x_velocity * delta_time
110 self.pan_camera_to_player()
111
112 def on_draw(self):
113
114 # Set up our drawing
115 self.clear()
116 with self.camera.activate():
117 # Store a reference to the background layers as shorthand
118 bg = self.backgrounds
119
120 # Fake an endless world with scrolling terrain
121 # Try experimenting with commenting out 1 or both of the 2 lines
122 # below to get an intuitive understanding of what each does!
123 bg.offset = self.camera.bottom_left # Fake depth by moving layers
124 bg.pos = self.camera.bottom_left # Follow the car to fake infinity
125
126 # Draw the background & the player's car
127 with self.window.ctx.enabled(self.window.ctx.BLEND):
128 bg.draw()
129 arcade.draw_sprite(self.player_sprite, pixelated=True)
130
131 def update_car_direction(self):
132 if self.x_velocity < 0:
133 self.player_sprite.texture = self.car_texture_left
134 elif self.x_velocity > 0:
135 self.player_sprite.texture = self.car_texture_right
136
137 def on_key_press(self, symbol: int, modifiers: int):
138 if symbol == arcade.key.LEFT:
139 self.x_velocity -= PLAYER_SPEED
140 self.update_car_direction()
141 elif symbol == arcade.key.RIGHT:
142 self.x_velocity += PLAYER_SPEED
143 self.update_car_direction()
144
145 def on_key_release(self, symbol: int, modifiers: int):
146 if symbol == arcade.key.LEFT:
147 self.x_velocity += PLAYER_SPEED
148 self.update_car_direction()
149 elif symbol == arcade.key.RIGHT:
150 self.x_velocity -= PLAYER_SPEED
151 self.update_car_direction()
152
153 def on_resize(self, width: int, height: int):
154 super().on_resize(width, height)
155 self.camera.match_window()
156 full_width_size = (width, SCALED_BG_LAYER_HEIGHT_PX)
157
158 # We can iterate through a background group,
159 # but in the case of a parallax group the iter returns
160 # both the Backgrounds and the depths. (tuple[Background, float])
161 for layer, depth in self.backgrounds:
162 layer.size = full_width_size
163
164
165def main():
166 """ Main function """
167 # Create a window class. This is what actually shows up on screen
168 window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, resizable=True)
169
170 # Create the GameView
171 game = GameView()
172
173 # Show GameView on screen
174 window.show_view(game)
175
176 # Start the arcade game loop
177 arcade.run()
178
179
180if __name__ == "__main__":
181 main()