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