Lights
This tutorial needs some documentation. Feel free to submit a PR to improve it!
1"""
2Show how to use lights.
3
4.. note:: This uses features from the upcoming version 2.4. The API for these
5 functions may still change. To use, you will need to install one of the
6 pre-release packages, or install via GitHub.
7
8Artwork from http://kenney.nl
9
10"""
11import arcade
12from arcade.future.light import Light, LightLayer
13
14SCREEN_WIDTH = 1024
15SCREEN_HEIGHT = 768
16SCREEN_TITLE = "Lighting Demo"
17VIEWPORT_MARGIN = 200
18MOVEMENT_SPEED = 5
19
20# This is the color used for 'ambient light'. If you don't want any
21# ambient light, set it to black.
22AMBIENT_COLOR = (10, 10, 10)
23
24class MyGame(arcade.Window):
25 """ Main Game Window """
26
27 def __init__(self, width, height, title):
28 """ Set up the class. """
29 super().__init__(width, height, title, resizable=True)
30
31 # Sprite lists
32 self.background_sprite_list = None
33 self.player_list = None
34 self.wall_list = None
35 self.player_sprite = None
36
37 # Physics engine
38 self.physics_engine = None
39
40 # camera for scrolling
41 self.camera = None
42
43 # --- Light related ---
44 # List of all the lights
45 self.light_layer = None
46 # Individual light we move with player, and turn on/off
47 self.player_light = None
48
49 def setup(self):
50 """ Create everything """
51
52 # Create sprite lists
53 self.background_sprite_list = arcade.SpriteList()
54 self.player_list = arcade.SpriteList()
55 self.wall_list = arcade.SpriteList()
56
57 # Create player sprite
58 self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png", 0.4)
59 self.player_sprite.center_x = 64
60 self.player_sprite.center_y = 270
61 self.player_list.append(self.player_sprite)
62
63 # --- Light related ---
64 # Lights must shine on something. If there is no background sprite or color,
65 # you will just see black. Therefore, we use a loop to create a whole bunch of brick tiles to go in the
66 # background.
67 for x in range(-128, 2000, 128):
68 for y in range(-128, 1000, 128):
69 sprite = arcade.Sprite(":resources:images/tiles/brickTextureWhite.png")
70 sprite.position = x, y
71 self.background_sprite_list.append(sprite)
72
73 # Create a light layer, used to render things to, then post-process and
74 # add lights. This must match the screen size.
75 self.light_layer = LightLayer(SCREEN_WIDTH, SCREEN_HEIGHT)
76 # We can also set the background color that will be lit by lights,
77 # but in this instance we just want a black background
78 self.light_layer.set_background_color(arcade.color.BLACK)
79
80 # Here we create a bunch of lights.
81
82 # Create a small white light
83 x = 100
84 y = 200
85 radius = 100
86 mode = 'soft'
87 color = arcade.csscolor.WHITE
88 light = Light(x, y, radius, color, mode)
89 self.light_layer.add(light)
90
91 # Create an overlapping, large white light
92 x = 300
93 y = 150
94 radius = 200
95 color = arcade.csscolor.WHITE
96 mode = 'soft'
97 light = Light(x, y, radius, color, mode)
98 self.light_layer.add(light)
99
100 # Create three, non-overlapping RGB lights
101 x = 50
102 y = 450
103 radius = 100
104 mode = 'soft'
105 color = arcade.csscolor.RED
106 light = Light(x, y, radius, color, mode)
107 self.light_layer.add(light)
108
109 x = 250
110 y = 450
111 radius = 100
112 mode = 'soft'
113 color = arcade.csscolor.GREEN
114 light = Light(x, y, radius, color, mode)
115 self.light_layer.add(light)
116
117 x = 450
118 y = 450
119 radius = 100
120 mode = 'soft'
121 color = arcade.csscolor.BLUE
122 light = Light(x, y, radius, color, mode)
123 self.light_layer.add(light)
124
125 # Create three, overlapping RGB lights
126 x = 650
127 y = 450
128 radius = 100
129 mode = 'soft'
130 color = arcade.csscolor.RED
131 light = Light(x, y, radius, color, mode)
132 self.light_layer.add(light)
133
134 x = 750
135 y = 450
136 radius = 100
137 mode = 'soft'
138 color = arcade.csscolor.GREEN
139 light = Light(x, y, radius, color, mode)
140 self.light_layer.add(light)
141
142 x = 850
143 y = 450
144 radius = 100
145 mode = 'soft'
146 color = arcade.csscolor.BLUE
147 light = Light(x, y, radius, color, mode)
148 self.light_layer.add(light)
149
150 # Create three, overlapping RGB lights
151 # But 'hard' lights that don't fade out.
152 x = 650
153 y = 150
154 radius = 100
155 mode = 'hard'
156 color = arcade.csscolor.RED
157 light = Light(x, y, radius, color, mode)
158 self.light_layer.add(light)
159
160 x = 750
161 y = 150
162 radius = 100
163 mode = 'hard'
164 color = arcade.csscolor.GREEN
165 light = Light(x, y, radius, color, mode)
166 self.light_layer.add(light)
167
168 x = 850
169 y = 150
170 radius = 100
171 mode = 'hard'
172 color = arcade.csscolor.BLUE
173 light = Light(x, y, radius, color, mode)
174 self.light_layer.add(light)
175
176 # Create a light to follow the player around.
177 # We'll position it later, when the player moves.
178 # We'll only add it to the light layer when the player turns the light
179 # on. We start with the light off.
180 radius = 150
181 mode = 'soft'
182 color = arcade.csscolor.WHITE
183 self.player_light = Light(0, 0, radius, color, mode)
184
185 # Create the physics engine
186 self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list)
187
188 # setup camera
189 self.camera = arcade.camera.Camera2D()
190
191 def on_draw(self):
192 """ Draw everything. """
193 self.clear()
194
195 # --- Light related ---
196 # Everything that should be affected by lights gets rendered inside this
197 # 'with' statement. Nothing is rendered to the screen yet, just the light
198 # layer.
199 with self.light_layer:
200 self.background_sprite_list.draw()
201 self.player_list.draw()
202
203 # Draw the light layer to the screen.
204 # This fills the entire screen with the lit version
205 # of what we drew into the light layer above.
206 self.light_layer.draw(ambient_color=AMBIENT_COLOR)
207
208 # Now draw anything that should NOT be affected by lighting.
209 left, bottom = self.camera.bottom_left
210 arcade.draw_text("Press SPACE to turn character light on/off.",
211 10 + left, 10 + bottom,
212 arcade.color.WHITE, 20)
213
214 def on_resize(self, width, height):
215 """ User resizes the screen. """
216
217 # --- Light related ---
218 # We need to resize the light layer to
219 self.light_layer.resize(width, height)
220
221 # Scroll the screen so the user is visible
222 self.scroll_screen()
223
224 def on_key_press(self, key, _):
225 """Called whenever a key is pressed. """
226
227 if key == arcade.key.UP:
228 self.player_sprite.change_y = MOVEMENT_SPEED
229 elif key == arcade.key.DOWN:
230 self.player_sprite.change_y = -MOVEMENT_SPEED
231 elif key == arcade.key.LEFT:
232 self.player_sprite.change_x = -MOVEMENT_SPEED
233 elif key == arcade.key.RIGHT:
234 self.player_sprite.change_x = MOVEMENT_SPEED
235 elif key == arcade.key.SPACE:
236 # --- Light related ---
237 # We can add/remove lights from the light layer. If they aren't
238 # in the light layer, the light is off.
239 if self.player_light in self.light_layer:
240 self.light_layer.remove(self.player_light)
241 else:
242 self.light_layer.add(self.player_light)
243
244 def on_key_release(self, key, _):
245 """Called when the user releases a key. """
246
247 if key == arcade.key.UP or key == arcade.key.DOWN:
248 self.player_sprite.change_y = 0
249 elif key == arcade.key.LEFT or key == arcade.key.RIGHT:
250 self.player_sprite.change_x = 0
251
252 def scroll_screen(self):
253 """ Manage Scrolling """
254 position = self.camera.position
255
256 top_left = self.camera.top_left
257 bottom_right = self.camera.bottom_right
258
259 # Scroll left
260 left_boundary = top_left[0] + VIEWPORT_MARGIN
261 if self.player_sprite.left < left_boundary:
262 position = position[0] + (self.player_sprite.left - left_boundary), position[1]
263
264 # Scroll right
265 right_boundary = bottom_right[0] - VIEWPORT_MARGIN
266 if self.player_sprite.right > right_boundary:
267 position = position[0] + (self.player_sprite.right - right_boundary), position[1]
268
269 # Scroll up
270 top_boundary = top_left[1] - VIEWPORT_MARGIN
271 if self.player_sprite.top > top_boundary:
272 position = position[0], position[1] + (self.player_sprite.top - top_boundary)
273
274 # Scroll down
275 bottom_boundary = bottom_right[1] + VIEWPORT_MARGIN
276 if self.player_sprite.bottom < bottom_boundary:
277 position = position[0], position[1] + (self.player_sprite.bottom - bottom_boundary)
278
279 self.camera.position = position
280
281 # Make sure our boundaries are integer values. While the viewport does
282 # support floating point numbers, for this application we want every pixel
283 # in the view port to map directly onto a pixel on the screen. We don't want
284 # any rounding errors.
285 bottom_left = self.camera.bottom_left
286 self.camera.bottom_left = int(bottom_left[0]), int(bottom_left[1])
287
288 self.camera.use()
289
290 def on_update(self, delta_time):
291 """ Movement and game logic """
292
293 # Call update on all sprites (The sprites don't do much in this
294 # example though.)
295 self.physics_engine.update()
296
297 # --- Light related ---
298 # We can easily move the light by setting the position,
299 # or by center_x, center_y.
300 self.player_light.position = self.player_sprite.position
301
302 # Scroll the screen so we can see the player
303 self.scroll_screen()
304
305
306if __name__ == "__main__":
307 window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
308 window.setup()
309 arcade.run()