第八步 Python#

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.camera.Camera2D()
43 self.camera_gui = arcade.camera.Camera2D()
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
107 def on_draw(self):
108 # Use our scrolled camera
109 self.camera_sprites.use()
110
111 # Select the channel 0 frame buffer to draw on
112 self.channel0.use()
113 self.channel0.clear()
114 # Draw the walls
115 self.wall_list.draw()
116
117 self.channel1.use()
118 self.channel1.clear()
119 # Draw the bombs
120 self.bomb_list.draw()
121
122 # Select this window to draw on
123 self.use()
124 # Clear to background color
125 self.clear()
126
127 # Calculate the light position. We have to subtract the camera position
128 # from the player position to get screen-relative coordinates.
129 p = (self.player_sprite.position[0] - self.camera_sprites.left,
130 self.player_sprite.position[1] - self.camera_sprites.bottom)
131
132 # Set the uniform data
133 self.shadertoy.program['lightPosition'] = p
134 self.shadertoy.program['lightSize'] = 300
135
136 # Run the shader and render to the window
137 self.shadertoy.render()
138
139 # Draw the walls
140 self.wall_list.draw()
141
142 # Draw the player
143 self.player_list.draw()
144
145 # Switch to the un-scrolled camera to draw the GUI with
146 self.camera_gui.use()
147 # Draw our sample GUI text
148 self.score_text.draw()
149
150 def on_key_press(self, key, modifiers):
151 """Called whenever a key is pressed. """
152
153 if key == arcade.key.UP:
154 self.player_sprite.change_y = PLAYER_MOVEMENT_SPEED
155 elif key == arcade.key.DOWN:
156 self.player_sprite.change_y = -PLAYER_MOVEMENT_SPEED
157 elif key == arcade.key.LEFT:
158 self.player_sprite.change_x = -PLAYER_MOVEMENT_SPEED
159 elif key == arcade.key.RIGHT:
160 self.player_sprite.change_x = PLAYER_MOVEMENT_SPEED
161
162 def on_key_release(self, key, modifiers):
163 """Called when the user releases a key. """
164
165 if key == arcade.key.UP or key == arcade.key.DOWN:
166 self.player_sprite.change_y = 0
167 elif key == arcade.key.LEFT or key == arcade.key.RIGHT:
168 self.player_sprite.change_x = 0
169
170 def on_update(self, delta_time):
171 """ Movement and game logic """
172
173 # Call update on all sprites (The sprites don't do much in this
174 # example though.)
175 self.physics_engine.update()
176 # Scroll the screen to the player
177 self.scroll_to_player()
178
179 def scroll_to_player(self, speed=CAMERA_SPEED):
180 """
181 Scroll the window to the player.
182
183 if CAMERA_SPEED is 1, the camera will immediately move to the desired position.
184 Anything between 0 and 1 will have the camera move to the location with a smoother
185 pan.
186 """
187
188 position = (self.player_sprite.center_x, self.player_sprite.center_y)
189 self.camera_sprites.position = arcade.math.lerp_2d(self.camera_sprites.position, position, CAMERA_SPEED)
190
191 def on_resize(self, width: int, height: int):
192 super().on_resize(width, height)
193 self.camera_sprites.match_screen(and_projection=True)
194 self.camera_gui.match_screen(and_projection=True)
195 self.shadertoy.resize((width, height))
196
197
198if __name__ == "__main__":
199 MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
200 arcade.run()