Lighting Demo#

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