Lighting Demo

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