Lights

../../_images/lights.png

This tutorial needs some documentation. Feel free to submit a PR to improve it!

light_demo.py
  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()