布卢姆效应捍卫者#

创造一种“发光”的效果可以增强2D游戏。这个例子展示了如何做到这一点。
创建帧缓冲区和后处理器#
第176-202行
这里我们创建了帧缓冲区,并添加了一个颜色附件来存储像素数据。
它还创建了一个后处理器,将对渲染的内容执行高斯模糊。模糊有很多参数,这取决于您希望它看起来是什么样子。
渲染到帧缓冲区#
239-252号线
绘制时,我们将希望模糊的对象渲染到帧缓冲区,然后运行后处理器进行模糊处理。
注意:此缓冲区不是透明的!它背后的任何东西都会被隐藏起来。所以在这个时候多层发光是不可能的,你也不能把任何东西放在发光后面。
将帧缓冲区渲染到屏幕#
第264-265行
最后,我们将该缓冲区呈现到屏幕上。
mini_map_defender.py#
1"""
2Defender Clone.
3
4This example shows how to create a 'bloom' or 'glow' effect.
5
6If Python and Arcade are installed, this example can be run from the command line with:
7python -m arcade.examples.bloom_defender
8"""
9
10import arcade
11import random
12
13# --- Bloom related ---
14from arcade.experimental import postprocessing
15
16# Size/title of the window
17SCREEN_WIDTH = 1280
18SCREEN_HEIGHT = 720
19SCREEN_TITLE = "Defender Clone"
20
21# Size of the playing field
22PLAYING_FIELD_WIDTH = 5000
23PLAYING_FIELD_HEIGHT = 1000
24
25# Size of the playing field.
26MAIN_SCREEN_HEIGHT = SCREEN_HEIGHT
27
28# How far away from the edges do we get before scrolling?
29VIEWPORT_MARGIN = SCREEN_WIDTH / 2 - 50
30TOP_VIEWPORT_MARGIN = 30
31DEFAULT_BOTTOM_VIEWPORT = -10
32
33# Control the physics of how the player moves
34MAX_HORIZONTAL_MOVEMENT_SPEED = 10
35MAX_VERTICAL_MOVEMENT_SPEED = 5
36HORIZONTAL_ACCELERATION = 0.5
37VERTICAL_ACCELERATION = 0.2
38MOVEMENT_DRAG = 0.08
39
40# How far the bullet travels before disappearing
41BULLET_MAX_DISTANCE = SCREEN_WIDTH * 0.75
42
43
44class Player(arcade.SpriteSolidColor):
45 """ Player ship """
46 def __init__(self):
47 """ Set up player """
48 super().__init__(40, 10, color=arcade.color.SLATE_GRAY)
49 self.face_right = True
50
51 def accelerate_up(self):
52 """ Accelerate player up """
53 self.change_y += VERTICAL_ACCELERATION
54 if self.change_y > MAX_VERTICAL_MOVEMENT_SPEED:
55 self.change_y = MAX_VERTICAL_MOVEMENT_SPEED
56
57 def accelerate_down(self):
58 """ Accelerate player down """
59 self.change_y -= VERTICAL_ACCELERATION
60 if self.change_y < -MAX_VERTICAL_MOVEMENT_SPEED:
61 self.change_y = -MAX_VERTICAL_MOVEMENT_SPEED
62
63 def accelerate_right(self):
64 """ Accelerate player right """
65 self.face_right = True
66 self.change_x += HORIZONTAL_ACCELERATION
67 if self.change_x > MAX_HORIZONTAL_MOVEMENT_SPEED:
68 self.change_x = MAX_HORIZONTAL_MOVEMENT_SPEED
69
70 def accelerate_left(self):
71 """ Accelerate player left """
72 self.face_right = False
73 self.change_x -= HORIZONTAL_ACCELERATION
74 if self.change_x < -MAX_HORIZONTAL_MOVEMENT_SPEED:
75 self.change_x = -MAX_HORIZONTAL_MOVEMENT_SPEED
76
77 def update(self):
78 """ Move the player """
79 # Move
80 self.center_x += self.change_x
81 self.center_y += self.change_y
82
83 # Drag
84 if self.change_x > 0:
85 self.change_x -= MOVEMENT_DRAG
86 if self.change_x < 0:
87 self.change_x += MOVEMENT_DRAG
88 if abs(self.change_x) < MOVEMENT_DRAG:
89 self.change_x = 0
90
91 if self.change_y > 0:
92 self.change_y -= MOVEMENT_DRAG
93 if self.change_y < 0:
94 self.change_y += MOVEMENT_DRAG
95 if abs(self.change_y) < MOVEMENT_DRAG:
96 self.change_y = 0
97
98 # Check bounds
99 if self.left < 0:
100 self.left = 0
101 elif self.right > PLAYING_FIELD_WIDTH - 1:
102 self.right = PLAYING_FIELD_WIDTH - 1
103
104 if self.bottom < 0:
105 self.bottom = 0
106 elif self.top > SCREEN_HEIGHT - 1:
107 self.top = SCREEN_HEIGHT - 1
108
109
110class Bullet(arcade.SpriteSolidColor):
111 """ Bullet """
112
113 def __init__(self, width, height, color):
114 super().__init__(width, height, color)
115 self.distance = 0
116
117 def update(self):
118 """ Move the particle, and fade out """
119 # Move
120 self.center_x += self.change_x
121 self.center_y += self.change_y
122 self.distance += self.change_x
123 if self.distance > BULLET_MAX_DISTANCE:
124 self.remove_from_sprite_lists()
125
126
127class Particle(arcade.SpriteSolidColor):
128 """ Particle from explosion """
129 def update(self):
130 """ Move the particle, and fade out """
131 # Move
132 self.center_x += self.change_x
133 self.center_y += self.change_y
134 # Fade
135 self.alpha -= 5
136 if self.alpha <= 0:
137 self.remove_from_sprite_lists()
138
139
140class MyGame(arcade.Window):
141 """ Main application class. """
142
143 def __init__(self, width, height, title):
144 """ Initializer """
145
146 # Call the parent class initializer
147 super().__init__(width, height, title)
148
149 # Variables that will hold sprite lists
150 self.player_list = None
151 self.star_sprite_list = None
152 self.enemy_sprite_list = None
153 self.bullet_sprite_list = None
154
155 # Set up the player info
156 self.player_sprite = None
157
158 # Track the current state of what key is pressed
159 self.left_pressed = False
160 self.right_pressed = False
161 self.up_pressed = False
162 self.down_pressed = False
163
164 self.view_bottom = 0
165 self.view_left = 0
166
167 # Set the background color of the window
168 self.background_color = arcade.color.BLACK
169
170 # --- Bloom related ---
171
172 # Frame to receive the glow, and color attachment to store each pixel's
173 # color data
174 self.bloom_color_attachment = self.ctx.texture((SCREEN_WIDTH, SCREEN_HEIGHT))
175 self.bloom_screen = self.ctx.framebuffer(
176 color_attachments=[self.bloom_color_attachment]
177 )
178
179 # Down-sampling helps improve the blur.
180 # Note: Any item with a size less than the down-sampling size may get missed in
181 # the blur process. Down-sampling by 8 and having an item of 4x4 size, the item
182 # will get missed 50% of the time in the x direction, and 50% of the time in the
183 # y direction for a total of being missed 75% of the time.
184 down_sampling = 4
185 # Size of the screen we are glowing onto
186 size = (SCREEN_WIDTH // down_sampling, SCREEN_HEIGHT // down_sampling)
187 # Gaussian blur parameters.
188 # To preview different values, see:
189 # https://observablehq.com/@jobleonard/gaussian-kernel-calculater
190 kernel_size = 21
191 sigma = 4
192 mu = 0
193 step = 1
194 # Control the intensity
195 multiplier = 2
196
197 # Create a post-processor to create a bloom
198 self.bloom_postprocessing = postprocessing.BloomEffect(size,
199 kernel_size,
200 sigma,
201 mu,
202 multiplier,
203 step)
204
205 def setup(self):
206 """ Set up the game and initialize the variables. """
207
208 # Sprite lists
209 self.player_list = arcade.SpriteList()
210 self.star_sprite_list = arcade.SpriteList()
211 self.enemy_sprite_list = arcade.SpriteList()
212 self.bullet_sprite_list = arcade.SpriteList()
213
214 # Set up the player
215 self.player_sprite = Player()
216 self.player_sprite.center_x = 50
217 self.player_sprite.center_y = 50
218 self.player_list.append(self.player_sprite)
219
220 # Add stars
221 for i in range(80):
222 sprite = arcade.SpriteSolidColor(4, 4, color=arcade.color.WHITE)
223 sprite.center_x = random.randrange(PLAYING_FIELD_WIDTH)
224 sprite.center_y = random.randrange(PLAYING_FIELD_HEIGHT)
225 self.star_sprite_list.append(sprite)
226
227 # Add enemies
228 for i in range(20):
229 sprite = arcade.SpriteSolidColor(20, 20, color=arcade.csscolor.LIGHT_SALMON)
230 sprite.center_x = random.randrange(PLAYING_FIELD_WIDTH)
231 sprite.center_y = random.randrange(PLAYING_FIELD_HEIGHT)
232 self.enemy_sprite_list.append(sprite)
233
234 def on_draw(self):
235 """ Render the screen. """
236 # This command has to happen before we start drawing
237 self.clear()
238
239 # --- Bloom related ---
240
241 # Draw to the 'bloom' layer
242 self.bloom_screen.use()
243 self.bloom_screen.clear(arcade.color.TRANSPARENT_BLACK)
244
245 arcade.set_viewport(self.view_left,
246 SCREEN_WIDTH + self.view_left,
247 self.view_bottom,
248 SCREEN_HEIGHT + self.view_bottom)
249
250 # Draw all the sprites on the screen that should have a bloom
251 self.star_sprite_list.draw()
252 self.bullet_sprite_list.draw()
253
254 # Now draw to the actual screen
255 self.use()
256
257 arcade.set_viewport(self.view_left,
258 SCREEN_WIDTH + self.view_left,
259 self.view_bottom,
260 SCREEN_HEIGHT + self.view_bottom)
261
262 # --- Bloom related ---
263
264 # Draw the bloom layers
265 self.bloom_postprocessing.render(self.bloom_color_attachment, self)
266
267 # Draw the sprites / items that have no bloom
268 self.enemy_sprite_list.draw()
269 self.player_list.draw()
270
271 # Draw the ground
272 arcade.draw_line(0, 0, PLAYING_FIELD_WIDTH, 0, arcade.color.WHITE)
273
274 def on_update(self, delta_time):
275 """ Movement and game logic """
276
277 # Calculate speed based on the keys pressed
278 if self.up_pressed and not self.down_pressed:
279 self.player_sprite.accelerate_up()
280 elif self.down_pressed and not self.up_pressed:
281 self.player_sprite.accelerate_down()
282
283 if self.left_pressed and not self.right_pressed:
284 self.player_sprite.accelerate_left()
285 elif self.right_pressed and not self.left_pressed:
286 self.player_sprite.accelerate_right()
287
288 # Call update to move the sprite
289 self.player_list.update()
290 self.bullet_sprite_list.update()
291
292 for bullet in self.bullet_sprite_list:
293 enemy_hit_list = arcade.check_for_collision_with_list(bullet,
294 self.enemy_sprite_list)
295 for enemy in enemy_hit_list:
296 enemy.remove_from_sprite_lists()
297 for i in range(10):
298 particle = Particle(4, 4, arcade.color.RED)
299 while particle.change_y == 0 and particle.change_x == 0:
300 particle.change_y = random.randrange(-2, 3)
301 particle.change_x = random.randrange(-2, 3)
302 particle.center_x = enemy.center_x
303 particle.center_y = enemy.center_y
304 self.bullet_sprite_list.append(particle)
305
306 # Scroll left
307 left_boundary = self.view_left + VIEWPORT_MARGIN
308 if self.player_sprite.left < left_boundary:
309 self.view_left -= left_boundary - self.player_sprite.left
310
311 # Scroll right
312 right_boundary = self.view_left + SCREEN_WIDTH - VIEWPORT_MARGIN
313 if self.player_sprite.right > right_boundary:
314 self.view_left += self.player_sprite.right - right_boundary
315
316 # Scroll up
317 self.view_bottom = DEFAULT_BOTTOM_VIEWPORT
318 top_boundary = self.view_bottom + SCREEN_HEIGHT - TOP_VIEWPORT_MARGIN
319 if self.player_sprite.top > top_boundary:
320 self.view_bottom += self.player_sprite.top - top_boundary
321
322 self.view_left = int(self.view_left)
323 self.view_bottom = int(self.view_bottom)
324
325 def on_key_press(self, key, modifiers):
326 """Called whenever a key is pressed. """
327
328 if key == arcade.key.UP:
329 self.up_pressed = True
330 elif key == arcade.key.DOWN:
331 self.down_pressed = True
332 elif key == arcade.key.LEFT:
333 self.left_pressed = True
334 elif key == arcade.key.RIGHT:
335 self.right_pressed = True
336 elif key == arcade.key.SPACE:
337 # Shoot out a bullet/laser
338 bullet = arcade.SpriteSolidColor(35, 3, arcade.color.WHITE)
339 bullet.center_x = self.player_sprite.center_x
340 bullet.center_y = self.player_sprite.center_y
341 bullet.change_x = max(12, abs(self.player_sprite.change_x) + 10)
342
343 if not self.player_sprite.face_right:
344 bullet.change_x *= -1
345
346 self.bullet_sprite_list.append(bullet)
347
348 def on_key_release(self, key, modifiers):
349 """Called when the user releases a key. """
350
351 if key == arcade.key.UP:
352 self.up_pressed = False
353 elif key == arcade.key.DOWN:
354 self.down_pressed = False
355 elif key == arcade.key.LEFT:
356 self.left_pressed = False
357 elif key == arcade.key.RIGHT:
358 self.right_pressed = False
359
360
361def main():
362 """ Main function """
363 window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
364 window.setup()
365 arcade.run()
366
367
368if __name__ == "__main__":
369 main()