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