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