Step 8 Python#

../../_images/step_06.png
step_08.py#
  1import random
  2from pyglet.math import Vec2
  3
  4import arcade
  5from arcade.experimental import Shadertoy
  6
  7# Do the math to figure out our screen dimensions
  8SCREEN_WIDTH = 800
  9SCREEN_HEIGHT = 600
 10SCREEN_TITLE = "Ray-casting Demo"
 11
 12SPRITE_SCALING = 0.25
 13
 14# How fast the camera pans to the player. 1.0 is instant.
 15CAMERA_SPEED = 0.1
 16
 17PLAYER_MOVEMENT_SPEED = 7
 18BOMB_COUNT = 70
 19PLAYING_FIELD_WIDTH = 1600
 20PLAYING_FIELD_HEIGHT = 1600
 21
 22
 23class MyGame(arcade.Window):
 24
 25    def __init__(self, width, height, title):
 26        super().__init__(width, height, title, resizable=True)
 27
 28        # The shader toy and 'channels' we'll be using
 29        self.shadertoy = None
 30        self.channel0 = None
 31        self.channel1 = None
 32        self.load_shader()
 33
 34        # Sprites and sprite lists
 35        self.player_sprite = None
 36        self.wall_list = arcade.SpriteList()
 37        self.player_list = arcade.SpriteList()
 38        self.bomb_list = arcade.SpriteList()
 39        self.physics_engine = None
 40
 41        # Create cameras used for scrolling
 42        self.camera_sprites = arcade.SimpleCamera()
 43        self.camera_gui = arcade.SimpleCamera()
 44
 45        self.generate_sprites()
 46
 47        # Our sample GUI text
 48        self.score_text = arcade.Text("Score: 0", 10, 10, arcade.color.WHITE, 24)
 49
 50        self.background_color = arcade.color.ARMY_GREEN
 51
 52    def load_shader(self):
 53        # Size of the window
 54        window_size = self.get_size()
 55
 56        # Create the shader toy, passing in a path for the shader source
 57        self.shadertoy = Shadertoy.create_from_file(window_size, "step_06.glsl")
 58
 59        # Create the channels 0 and 1 frame buffers.
 60        # Make the buffer the size of the window, with 4 channels (RGBA)
 61        self.channel0 = self.shadertoy.ctx.framebuffer(
 62            color_attachments=[self.shadertoy.ctx.texture(window_size, components=4)]
 63        )
 64        self.channel1 = self.shadertoy.ctx.framebuffer(
 65            color_attachments=[self.shadertoy.ctx.texture(window_size, components=4)]
 66        )
 67
 68        # Assign the frame buffers to the channels
 69        self.shadertoy.channel_0 = self.channel0.color_attachments[0]
 70        self.shadertoy.channel_1 = self.channel1.color_attachments[0]
 71
 72    def generate_sprites(self):
 73        # -- Set up several columns of walls
 74        for x in range(0, PLAYING_FIELD_WIDTH, 128):
 75            for y in range(0, PLAYING_FIELD_HEIGHT, int(128 * SPRITE_SCALING)):
 76                # Randomly skip a box so the player can find a way through
 77                if random.randrange(2) > 0:
 78                    wall = arcade.Sprite(":resources:images/tiles/boxCrate_double.png", SPRITE_SCALING)
 79                    wall.center_x = x
 80                    wall.center_y = y
 81                    self.wall_list.append(wall)
 82
 83        # -- Set some hidden bombs in the area
 84        for i in range(BOMB_COUNT):
 85            bomb = arcade.Sprite(":resources:images/tiles/bomb.png", 0.25)
 86            placed = False
 87            while not placed:
 88                bomb.center_x = random.randrange(PLAYING_FIELD_WIDTH)
 89                bomb.center_y = random.randrange(PLAYING_FIELD_HEIGHT)
 90                if not arcade.check_for_collision_with_list(bomb, self.wall_list):
 91                    placed = True
 92            self.bomb_list.append(bomb)
 93
 94        # Create the player
 95        self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png",
 96                                           scale=SPRITE_SCALING)
 97        self.player_sprite.center_x = 256
 98        self.player_sprite.center_y = 512
 99        self.player_list.append(self.player_sprite)
100
101        # Physics engine, so we don't run into walls
102        self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list)
103
104        # Start centered on the player
105        self.scroll_to_player(1.0)
106        self.camera_sprites.update()
107
108
109    def on_draw(self):
110        # Use our scrolled camera
111        self.camera_sprites.use()
112
113        # Select the channel 0 frame buffer to draw on
114        self.channel0.use()
115        self.channel0.clear()
116        # Draw the walls
117        self.wall_list.draw()
118
119        self.channel1.use()
120        self.channel1.clear()
121        # Draw the bombs
122        self.bomb_list.draw()
123
124        # Select this window to draw on
125        self.use()
126        # Clear to background color
127        self.clear()
128
129        # Calculate the light position. We have to subtract the camera position
130        # from the player position to get screen-relative coordinates.
131        p = (self.player_sprite.position[0] - self.camera_sprites.position[0],
132             self.player_sprite.position[1] - self.camera_sprites.position[1])
133
134        # Set the uniform data
135        self.shadertoy.program['lightPosition'] = p
136        self.shadertoy.program['lightSize'] = 300
137
138        # Run the shader and render to the window
139        self.shadertoy.render()
140
141        # Draw the walls
142        self.wall_list.draw()
143
144        # Draw the player
145        self.player_list.draw()
146
147        # Switch to the un-scrolled camera to draw the GUI with
148        self.camera_gui.use()
149        # Draw our sample GUI text
150        self.score_text.draw()
151
152    def on_key_press(self, key, modifiers):
153        """Called whenever a key is pressed. """
154
155        if key == arcade.key.UP:
156            self.player_sprite.change_y = PLAYER_MOVEMENT_SPEED
157        elif key == arcade.key.DOWN:
158            self.player_sprite.change_y = -PLAYER_MOVEMENT_SPEED
159        elif key == arcade.key.LEFT:
160            self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
161        elif key == arcade.key.RIGHT:
162            self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
163
164    def on_key_release(self, key, modifiers):
165        """Called when the user releases a key. """
166
167        if key == arcade.key.UP or key == arcade.key.DOWN:
168            self.player_sprite.change_y = 0
169        elif key == arcade.key.LEFT or key == arcade.key.RIGHT:
170            self.player_sprite.change_x = 0
171
172    def on_update(self, delta_time):
173        """ Movement and game logic """
174
175        # Call update on all sprites (The sprites don't do much in this
176        # example though.)
177        self.physics_engine.update()
178        # Scroll the screen to the player
179        self.scroll_to_player()
180
181    def scroll_to_player(self, speed=CAMERA_SPEED):
182        """
183        Scroll the window to the player.
184
185        if CAMERA_SPEED is 1, the camera will immediately move to the desired position.
186        Anything between 0 and 1 will have the camera move to the location with a smoother
187        pan.
188        """
189
190        position = Vec2(self.player_sprite.center_x - self.width / 2,
191                        self.player_sprite.center_y - self.height / 2)
192        self.camera_sprites.move_to(position, speed)
193
194    def on_resize(self, width: int, height: int):
195        super().on_resize(width, height)
196        self.camera_sprites.resize(width, height)
197        self.camera_gui.resize(width, height)
198        self.shadertoy.resize((width, height))
199
200
201if __name__ == "__main__":
202    MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
203    arcade.run()