Particle Systems#
particle_systems.py#
1"""
2Particle Systems
3
4Demonstrate how to use the Emitter and Particle classes to create particle systems.
5
6Demonstrate the different effects possible with Emitter's and Particle's by showing
7a number of different emitters in sequence, with each example often varying just one
8setting from the previous example.
9
10If Python and Arcade are installed, this example can be run from the command line with:
11python -m arcade.examples.particle_systems
12"""
13import arcade
14import pyglet
15import random
16import math
17
18SCREEN_WIDTH = 800
19SCREEN_HEIGHT = 600
20SCREEN_TITLE = "Particle System Examples"
21QUIET_BETWEEN_SPAWNS = 0.25 # time between spawning another particle system
22EMITTER_TIMEOUT = 10 * 60
23CENTER_POS = (SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2)
24BURST_PARTICLE_COUNT = 500
25TEXTURE = ":resources:images/pinball/pool_cue_ball.png"
26TEXTURE2 = ":resources:images/space_shooter/playerShip3_orange.png"
27TEXTURE3 = ":resources:images/pinball/bumper.png"
28TEXTURE4 = ":resources:images/enemies/wormGreen.png"
29TEXTURE5 = ":resources:images/space_shooter/meteorGrey_med1.png"
30TEXTURE6 = ":resources:images/animated_characters/female_person/femalePerson_idle.png"
31TEXTURE7 = ":resources:images/tiles/boxCrate_double.png"
32DEFAULT_SCALE = 0.3
33DEFAULT_ALPHA = 32
34DEFAULT_PARTICLE_LIFETIME = 3.0
35PARTICLE_SPEED_FAST = 1.0
36PARTICLE_SPEED_SLOW = 0.3
37DEFAULT_EMIT_INTERVAL = 0.003
38DEFAULT_EMIT_DURATION = 1.5
39
40
41# Utils
42def sine_wave(t, min_x, max_x, wavelength):
43 spread = max_x - min_x
44 mid = (max_x + min_x) / 2
45 return (spread / 2) * math.sin(2 * math.pi * t / wavelength) + mid
46
47
48# Example emitters
49def emitter_0():
50 """Burst, emit from center, particle with lifetime"""
51 e = arcade.Emitter(
52 center_xy=CENTER_POS,
53 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT),
54 particle_factory=lambda emitter: arcade.LifetimeParticle(
55 filename_or_texture=TEXTURE,
56 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
57 lifetime=DEFAULT_PARTICLE_LIFETIME,
58 scale=DEFAULT_SCALE,
59 alpha=DEFAULT_ALPHA
60 )
61 )
62 return emitter_0.__doc__, e
63
64
65def emitter_1():
66 """Burst, emit from center, particle lifetime 1.0 seconds"""
67 e = arcade.Emitter(
68 center_xy=CENTER_POS,
69 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT),
70 particle_factory=lambda emitter: arcade.LifetimeParticle(
71 filename_or_texture=TEXTURE,
72 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
73 lifetime=1.0,
74 scale=DEFAULT_SCALE,
75 alpha=DEFAULT_ALPHA
76 )
77 )
78 return emitter_1.__doc__, e
79
80
81def emitter_2():
82 """Burst, emit from center, particle lifetime random in range"""
83 e = arcade.Emitter(
84 center_xy=CENTER_POS,
85 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT),
86 particle_factory=lambda emitter: arcade.LifetimeParticle(
87 filename_or_texture=TEXTURE,
88 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
89 lifetime=random.uniform(DEFAULT_PARTICLE_LIFETIME - 1.0, DEFAULT_PARTICLE_LIFETIME),
90 scale=DEFAULT_SCALE,
91 alpha=DEFAULT_ALPHA
92 )
93 )
94 return emitter_2.__doc__, e
95
96
97def emitter_3():
98 """Burst, emit in circle"""
99 e = arcade.Emitter(
100 center_xy=CENTER_POS,
101 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT),
102 particle_factory=lambda emitter: arcade.LifetimeParticle(
103 filename_or_texture=TEXTURE,
104 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_SLOW),
105 lifetime=DEFAULT_PARTICLE_LIFETIME,
106 center_xy=arcade.rand_in_circle((0.0, 0.0), 100),
107 scale=DEFAULT_SCALE,
108 alpha=DEFAULT_ALPHA
109 )
110 )
111 return emitter_3.__doc__, e
112
113
114def emitter_4():
115 """Burst, emit on circle"""
116 e = arcade.Emitter(
117 center_xy=CENTER_POS,
118 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT),
119 particle_factory=lambda emitter: arcade.LifetimeParticle(
120 filename_or_texture=TEXTURE,
121 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_SLOW),
122 lifetime=DEFAULT_PARTICLE_LIFETIME,
123 center_xy=arcade.rand_on_circle((0.0, 0.0), 100),
124 scale=DEFAULT_SCALE,
125 alpha=DEFAULT_ALPHA
126 )
127 )
128 return emitter_4.__doc__, e
129
130
131def emitter_5():
132 """Burst, emit in rectangle"""
133 width, height = 200, 100
134 centering_offset = (-width / 2, -height / 2)
135 e = arcade.Emitter(
136 center_xy=CENTER_POS,
137 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT),
138 particle_factory=lambda emitter: arcade.LifetimeParticle(
139 filename_or_texture=TEXTURE,
140 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_SLOW),
141 lifetime=DEFAULT_PARTICLE_LIFETIME,
142 center_xy=arcade.rand_in_rect(centering_offset, width, height),
143 scale=DEFAULT_SCALE,
144 alpha=DEFAULT_ALPHA
145 )
146 )
147 return emitter_5.__doc__, e
148
149
150def emitter_6():
151 """Burst, emit on line"""
152 e = arcade.Emitter(
153 center_xy=CENTER_POS,
154 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT),
155 particle_factory=lambda emitter: arcade.LifetimeParticle(
156 filename_or_texture=TEXTURE,
157 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_SLOW),
158 lifetime=DEFAULT_PARTICLE_LIFETIME,
159 center_xy=arcade.rand_on_line((0.0, 0.0), (SCREEN_WIDTH, SCREEN_HEIGHT)),
160 scale=DEFAULT_SCALE,
161 alpha=DEFAULT_ALPHA
162 )
163 )
164 return emitter_6.__doc__, e
165
166
167def emitter_7():
168 """Burst, emit from center, velocity fixed speed around 360 degrees"""
169 e = arcade.Emitter(
170 center_xy=CENTER_POS,
171 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT // 4),
172 particle_factory=lambda emitter: arcade.LifetimeParticle(
173 filename_or_texture=TEXTURE,
174 change_xy=arcade.rand_on_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
175 lifetime=DEFAULT_PARTICLE_LIFETIME,
176 scale=DEFAULT_SCALE,
177 alpha=DEFAULT_ALPHA
178 )
179 )
180 return emitter_7.__doc__, e
181
182
183def emitter_8():
184 """Burst, emit from center, velocity in rectangle"""
185 e = arcade.Emitter(
186 center_xy=CENTER_POS,
187 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT),
188 particle_factory=lambda emitter: arcade.LifetimeParticle(
189 filename_or_texture=TEXTURE,
190 change_xy=arcade.rand_in_rect((-2.0, -2.0), 4.0, 4.0),
191 lifetime=DEFAULT_PARTICLE_LIFETIME,
192 scale=DEFAULT_SCALE,
193 alpha=DEFAULT_ALPHA
194 )
195 )
196 return emitter_8.__doc__, e
197
198
199def emitter_9():
200 """Burst, emit from center, velocity in fixed angle and random speed"""
201 e = arcade.Emitter(
202 center_xy=CENTER_POS,
203 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT // 4),
204 particle_factory=lambda emitter: arcade.LifetimeParticle(
205 filename_or_texture=TEXTURE,
206 change_xy=arcade.rand_vec_magnitude(45, 1.0, 4.0),
207 lifetime=DEFAULT_PARTICLE_LIFETIME,
208 scale=DEFAULT_SCALE,
209 alpha=DEFAULT_ALPHA
210 )
211 )
212 return emitter_9.__doc__, e
213
214
215def emitter_10():
216 """Burst, emit from center, velocity from angle with spread"""
217 e = arcade.Emitter(
218 center_xy=CENTER_POS,
219 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT // 4),
220 particle_factory=lambda emitter: arcade.LifetimeParticle(
221 filename_or_texture=TEXTURE,
222 change_xy=arcade.rand_vec_spread_deg(90, 45, 2.0),
223 lifetime=DEFAULT_PARTICLE_LIFETIME,
224 scale=DEFAULT_SCALE,
225 alpha=DEFAULT_ALPHA
226 )
227 )
228 return emitter_10.__doc__, e
229
230
231def emitter_11():
232 """Burst, emit from center, velocity along a line"""
233 e = arcade.Emitter(
234 center_xy=CENTER_POS,
235 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT // 4),
236 particle_factory=lambda emitter: arcade.LifetimeParticle(
237 filename_or_texture=TEXTURE,
238 change_xy=arcade.rand_on_line((-2, 1), (2, 1)),
239 lifetime=DEFAULT_PARTICLE_LIFETIME,
240 scale=DEFAULT_SCALE,
241 alpha=DEFAULT_ALPHA
242 )
243 )
244 return emitter_11.__doc__, e
245
246
247def emitter_12():
248 """Infinite emitting w/ eternal particle"""
249 e = arcade.Emitter(
250 center_xy=CENTER_POS,
251 emit_controller=arcade.EmitInterval(0.02),
252 particle_factory=lambda emitter: arcade.EternalParticle(
253 filename_or_texture=TEXTURE,
254 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
255 scale=DEFAULT_SCALE,
256 alpha=DEFAULT_ALPHA
257 )
258 )
259 return emitter_12.__doc__, e
260
261
262def emitter_13():
263 """Interval, emit particle every 0.01 seconds for one second"""
264 e = arcade.Emitter(
265 center_xy=CENTER_POS,
266 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
267 particle_factory=lambda emitter: arcade.LifetimeParticle(
268 filename_or_texture=TEXTURE,
269 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
270 lifetime=DEFAULT_PARTICLE_LIFETIME,
271 scale=DEFAULT_SCALE,
272 alpha=DEFAULT_ALPHA
273 )
274 )
275 return emitter_13.__doc__, e
276
277
278def emitter_14():
279 """Interval, emit from center, particle lifetime 1.0 seconds"""
280 e = arcade.Emitter(
281 center_xy=CENTER_POS,
282 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
283 particle_factory=lambda emitter: arcade.LifetimeParticle(
284 filename_or_texture=TEXTURE,
285 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
286 lifetime=1.0,
287 scale=DEFAULT_SCALE,
288 alpha=DEFAULT_ALPHA
289 )
290 )
291 return emitter_14.__doc__, e
292
293
294def emitter_15():
295 """Interval, emit from center, particle lifetime random in range"""
296 e = arcade.Emitter(
297 center_xy=CENTER_POS,
298 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
299 particle_factory=lambda emitter: arcade.LifetimeParticle(
300 filename_or_texture=TEXTURE,
301 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
302 lifetime=random.uniform(DEFAULT_PARTICLE_LIFETIME - 1.0, DEFAULT_PARTICLE_LIFETIME),
303 scale=DEFAULT_SCALE,
304 alpha=DEFAULT_ALPHA
305 )
306 )
307 return emitter_15.__doc__, e
308
309
310def emitter_16():
311 """Interval, emit in circle"""
312 e = arcade.Emitter(
313 center_xy=CENTER_POS,
314 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
315 particle_factory=lambda emitter: arcade.LifetimeParticle(
316 filename_or_texture=TEXTURE,
317 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_SLOW),
318 lifetime=DEFAULT_PARTICLE_LIFETIME,
319 center_xy=arcade.rand_in_circle((0.0, 0.0), 100),
320 scale=DEFAULT_SCALE,
321 alpha=DEFAULT_ALPHA
322 )
323 )
324 return emitter_16.__doc__, e
325
326
327def emitter_17():
328 """Interval, emit on circle"""
329 e = arcade.Emitter(
330 center_xy=CENTER_POS,
331 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
332 particle_factory=lambda emitter: arcade.LifetimeParticle(
333 filename_or_texture=TEXTURE,
334 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_SLOW),
335 lifetime=DEFAULT_PARTICLE_LIFETIME,
336 center_xy=arcade.rand_on_circle((0.0, 0.0), 100),
337 scale=DEFAULT_SCALE,
338 alpha=DEFAULT_ALPHA
339 )
340 )
341 return emitter_17.__doc__, e
342
343
344def emitter_18():
345 """Interval, emit in rectangle"""
346 width, height = 200, 100
347 centering_offset = (-width / 2, -height / 2)
348 e = arcade.Emitter(
349 center_xy=CENTER_POS,
350 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
351 particle_factory=lambda emitter: arcade.LifetimeParticle(
352 filename_or_texture=TEXTURE,
353 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_SLOW),
354 lifetime=DEFAULT_PARTICLE_LIFETIME,
355 center_xy=arcade.rand_in_rect(centering_offset, width, height),
356 scale=DEFAULT_SCALE,
357 alpha=DEFAULT_ALPHA
358 )
359 )
360 return emitter_18.__doc__, e
361
362
363def emitter_19():
364 """Interval, emit on line"""
365 e = arcade.Emitter(
366 center_xy=(0.0, 0.0),
367 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
368 particle_factory=lambda emitter: arcade.LifetimeParticle(
369 filename_or_texture=TEXTURE,
370 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_SLOW),
371 lifetime=DEFAULT_PARTICLE_LIFETIME,
372 center_xy=arcade.rand_on_line((0.0, 0.0), (SCREEN_WIDTH, SCREEN_HEIGHT)),
373 scale=DEFAULT_SCALE,
374 alpha=DEFAULT_ALPHA
375 )
376 )
377 return emitter_19.__doc__, e
378
379
380def emitter_20():
381 """Interval, emit from center, velocity fixed speed around 360 degrees"""
382 e = arcade.Emitter(
383 center_xy=CENTER_POS,
384 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
385 particle_factory=lambda emitter: arcade.LifetimeParticle(
386 filename_or_texture=TEXTURE,
387 change_xy=arcade.rand_on_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
388 lifetime=DEFAULT_PARTICLE_LIFETIME,
389 scale=DEFAULT_SCALE,
390 alpha=DEFAULT_ALPHA
391 )
392 )
393 return emitter_20.__doc__, e
394
395
396def emitter_21():
397 """Interval, emit from center, velocity in rectangle"""
398 e = arcade.Emitter(
399 center_xy=CENTER_POS,
400 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
401 particle_factory=lambda emitter: arcade.LifetimeParticle(
402 filename_or_texture=TEXTURE,
403 change_xy=arcade.rand_in_rect((-2.0, -2.0), 4.0, 4.0),
404 lifetime=DEFAULT_PARTICLE_LIFETIME,
405 scale=DEFAULT_SCALE,
406 alpha=DEFAULT_ALPHA
407 )
408 )
409 return emitter_21.__doc__, e
410
411
412def emitter_22():
413 """Interval, emit from center, velocity in fixed angle and speed"""
414 e = arcade.Emitter(
415 center_xy=CENTER_POS,
416 emit_controller=arcade.EmitterIntervalWithTime(0.2, DEFAULT_EMIT_DURATION),
417 particle_factory=lambda emitter: arcade.LifetimeParticle(
418 filename_or_texture=TEXTURE,
419 change_xy=(1.0, 1.0),
420 lifetime=DEFAULT_PARTICLE_LIFETIME,
421 scale=DEFAULT_SCALE,
422 alpha=128
423 )
424 )
425 return emitter_22.__doc__, e
426
427
428def emitter_23():
429 """Interval, emit from center, velocity in fixed angle and random speed"""
430 e = arcade.Emitter(
431 center_xy=CENTER_POS,
432 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL * 8, DEFAULT_EMIT_DURATION),
433 particle_factory=lambda emitter: arcade.LifetimeParticle(
434 filename_or_texture=TEXTURE,
435 change_xy=arcade.rand_vec_magnitude(45, 1.0, 4.0),
436 lifetime=DEFAULT_PARTICLE_LIFETIME,
437 scale=DEFAULT_SCALE,
438 alpha=DEFAULT_ALPHA
439 )
440 )
441 return emitter_23.__doc__, e
442
443
444def emitter_24():
445 """Interval, emit from center, velocity from angle with spread"""
446 e = arcade.Emitter(
447 center_xy=CENTER_POS,
448 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
449 particle_factory=lambda emitter: arcade.LifetimeParticle(
450 filename_or_texture=TEXTURE,
451 change_xy=arcade.rand_vec_spread_deg(90, 45, 2.0),
452 lifetime=DEFAULT_PARTICLE_LIFETIME,
453 scale=DEFAULT_SCALE,
454 alpha=DEFAULT_ALPHA
455 )
456 )
457 return emitter_24.__doc__, e
458
459
460def emitter_25():
461 """Interval, emit from center, velocity along a line"""
462 e = arcade.Emitter(
463 center_xy=CENTER_POS,
464 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
465 particle_factory=lambda emitter: arcade.LifetimeParticle(
466 filename_or_texture=TEXTURE,
467 change_xy=arcade.rand_on_line((-2, 1), (2, 1)),
468 lifetime=DEFAULT_PARTICLE_LIFETIME,
469 scale=DEFAULT_SCALE,
470 alpha=DEFAULT_ALPHA
471 )
472 )
473 return emitter_25.__doc__, e
474
475
476def emitter_26():
477 """Interval, emit particles every 0.4 seconds and stop after emitting 5"""
478 e = arcade.Emitter(
479 center_xy=CENTER_POS,
480 emit_controller=arcade.EmitterIntervalWithCount(0.4, 5),
481 particle_factory=lambda emitter: arcade.LifetimeParticle(
482 filename_or_texture=TEXTURE,
483 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
484 lifetime=DEFAULT_PARTICLE_LIFETIME,
485 scale=0.6,
486 alpha=128
487 )
488 )
489 return emitter_26.__doc__, e
490
491
492def emitter_27():
493 """Maintain a steady count of particles"""
494 e = arcade.Emitter(
495 center_xy=CENTER_POS,
496 emit_controller=arcade.EmitMaintainCount(3),
497 particle_factory=lambda emitter: arcade.LifetimeParticle(
498 filename_or_texture=TEXTURE,
499 change_xy=arcade.rand_on_circle((0.0, 0.0), 2.0),
500 lifetime=random.uniform(1.0, 3.0),
501 )
502 )
503 return emitter_27.__doc__, e
504
505
506def emitter_28():
507 """random particle textures"""
508 e = arcade.Emitter(
509 center_xy=CENTER_POS,
510 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL * 5, DEFAULT_EMIT_DURATION),
511 particle_factory=lambda emitter: arcade.LifetimeParticle(
512 filename_or_texture=random.choice((TEXTURE, TEXTURE2, TEXTURE3)),
513 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
514 lifetime=DEFAULT_PARTICLE_LIFETIME,
515 scale=DEFAULT_SCALE
516 )
517 )
518 return emitter_28.__doc__, e
519
520
521def emitter_29():
522 """random particle scale"""
523 e = arcade.Emitter(
524 center_xy=CENTER_POS,
525 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL * 5, DEFAULT_EMIT_DURATION),
526 particle_factory=lambda emitter: arcade.LifetimeParticle(
527 filename_or_texture=TEXTURE,
528 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
529 lifetime=DEFAULT_PARTICLE_LIFETIME,
530 scale=random.uniform(0.1, 0.8),
531 alpha=DEFAULT_ALPHA
532 )
533 )
534 return emitter_29.__doc__, e
535
536
537def emitter_30():
538 """random particle alpha"""
539 e = arcade.Emitter(
540 center_xy=CENTER_POS,
541 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL * 5, DEFAULT_EMIT_DURATION),
542 particle_factory=lambda emitter: arcade.LifetimeParticle(
543 filename_or_texture=TEXTURE,
544 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
545 lifetime=DEFAULT_PARTICLE_LIFETIME,
546 scale=DEFAULT_SCALE,
547 alpha=int(random.uniform(32, 128))
548 )
549 )
550 return emitter_30.__doc__, e
551
552
553def emitter_31():
554 """Constant particle angle"""
555 e = arcade.Emitter(
556 center_xy=CENTER_POS,
557 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL * 5, DEFAULT_EMIT_DURATION),
558 particle_factory=lambda emitter: arcade.LifetimeParticle(
559 filename_or_texture=TEXTURE2,
560 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
561 lifetime=DEFAULT_PARTICLE_LIFETIME,
562 angle=45,
563 scale=DEFAULT_SCALE
564 )
565 )
566 return emitter_31.__doc__, e
567
568
569def emitter_32():
570 """animate particle angle"""
571 e = arcade.Emitter(
572 center_xy=CENTER_POS,
573 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL * 5, DEFAULT_EMIT_DURATION),
574 particle_factory=lambda emitter: arcade.LifetimeParticle(
575 filename_or_texture=TEXTURE2,
576 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
577 lifetime=DEFAULT_PARTICLE_LIFETIME,
578 change_angle=2,
579 scale=DEFAULT_SCALE
580 )
581 )
582 return emitter_32.__doc__, e
583
584
585def emitter_33():
586 """Particles that fade over time"""
587 e = arcade.Emitter(
588 center_xy=CENTER_POS,
589 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
590 particle_factory=lambda emitter: arcade.FadeParticle(
591 filename_or_texture=TEXTURE,
592 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
593 lifetime=DEFAULT_PARTICLE_LIFETIME,
594 scale=DEFAULT_SCALE
595 )
596 )
597 return emitter_33.__doc__, e
598
599
600def emitter_34():
601 """Dynamically generated textures, burst emitting, fading particles"""
602 textures = [arcade.make_soft_circle_texture(48, p) for p in (arcade.color.GREEN, arcade.color.BLUE_GREEN)]
603 e = arcade.Emitter(
604 center_xy=CENTER_POS,
605 emit_controller=arcade.EmitBurst(BURST_PARTICLE_COUNT),
606 particle_factory=lambda emitter: arcade.FadeParticle(
607 filename_or_texture=random.choice(textures),
608 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST),
609 lifetime=DEFAULT_PARTICLE_LIFETIME,
610 scale=DEFAULT_SCALE
611 )
612 )
613 return emitter_34.__doc__, e
614
615
616def emitter_35():
617 """Use most features"""
618 soft_circle = arcade.make_soft_circle_texture(80, (255, 64, 64))
619 textures = (TEXTURE, TEXTURE2, TEXTURE3, TEXTURE4, TEXTURE5, TEXTURE6, TEXTURE7, soft_circle)
620 e = arcade.Emitter(
621 center_xy=CENTER_POS,
622 emit_controller=arcade.EmitterIntervalWithTime(0.01, 1.0),
623 particle_factory=lambda emitter: arcade.FadeParticle(
624 filename_or_texture=random.choice(textures),
625 change_xy=arcade.rand_in_circle((0.0, 0.0), PARTICLE_SPEED_FAST * 2),
626 lifetime=random.uniform(1.0, 3.5),
627 angle=random.uniform(0, 360),
628 change_angle=random.uniform(-3, 3),
629 scale=random.uniform(0.1, 0.8)
630 )
631 )
632 return emitter_35.__doc__, e
633
634
635def emitter_36():
636 """Moving emitter. Particles spawn relative to emitter."""
637
638 class MovingEmitter(arcade.Emitter):
639 def __init__(self, *args, **kwargs):
640 super().__init__(*args, **kwargs)
641 self.elapsed = 0.0
642
643 def update(self):
644 super().update()
645 self.elapsed += 1 / 60
646 self.center_x = sine_wave(self.elapsed, 0, SCREEN_WIDTH, SCREEN_WIDTH / 100)
647 self.center_y = sine_wave(self.elapsed, 0, SCREEN_HEIGHT, SCREEN_HEIGHT / 100)
648
649 e = MovingEmitter(
650 center_xy=CENTER_POS,
651 emit_controller=arcade.EmitInterval(0.005),
652 particle_factory=lambda emitter: arcade.FadeParticle(
653 filename_or_texture=TEXTURE,
654 change_xy=arcade.rand_in_circle((0.0, 0.0), 0.1),
655 lifetime=random.uniform(1.5, 5.5),
656 scale=random.uniform(0.05, 0.2)
657 )
658 )
659 return emitter_36.__doc__, e
660
661
662def emitter_37():
663 """Rotating emitter. Particles initial velocity is relative to emitter's angle."""
664 e = arcade.Emitter(
665 center_xy=CENTER_POS,
666 emit_controller=arcade.EmitterIntervalWithTime(DEFAULT_EMIT_INTERVAL, DEFAULT_EMIT_DURATION),
667 particle_factory=lambda emitter: arcade.LifetimeParticle(
668 filename_or_texture=TEXTURE,
669 change_xy=(0.0, 2.0),
670 lifetime=2.0,
671 scale=DEFAULT_SCALE
672 )
673 )
674 e.change_angle = 10.0
675 return emitter_37.__doc__, e
676
677
678def emitter_38():
679 """Use simple emitter interface to create a burst emitter"""
680 e = arcade.make_burst_emitter(
681 center_xy=CENTER_POS,
682 filenames_and_textures=(TEXTURE, TEXTURE3, TEXTURE4),
683 particle_count=50,
684 particle_speed=2.5,
685 particle_lifetime_min=1.0,
686 particle_lifetime_max=2.5,
687 particle_scale=0.3,
688 fade_particles=True
689 )
690 return emitter_38.__doc__, e
691
692
693def emitter_39():
694 """Use simple emitter interface to create an interval emitter"""
695 e = arcade.make_interval_emitter(
696 center_xy=CENTER_POS,
697 filenames_and_textures=(TEXTURE, TEXTURE3, TEXTURE4),
698 emit_interval=0.01,
699 emit_duration=2.0,
700 particle_speed=1.5,
701 particle_lifetime_min=1.0,
702 particle_lifetime_max=3.0,
703 particle_scale=0.2,
704 fade_particles=True
705 )
706 return emitter_39.__doc__, e
707
708
709class MyGame(arcade.Window):
710 def __init__(self):
711 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
712
713 arcade.set_background_color(arcade.color.BLACK)
714
715 # collect particle factory functions
716 self.factories = [v for k, v in globals().items() if k.startswith("emitter_")]
717
718 self.emitter_factory_id = -1
719 self.label = None
720 self.emitter = None
721 self.emitter_timeout = 0
722 self.obj = arcade.Sprite(":resources:images/pinball/bumper.png", 0.2, center_x=0, center_y=15)
723 self.obj.change_x = 3
724 pyglet.clock.schedule_once(self.next_emitter, QUIET_BETWEEN_SPAWNS)
725
726 def next_emitter(self, _time_delta):
727 self.emitter_factory_id = (self.emitter_factory_id + 1) % len(self.factories)
728 print("Changing emitter to {}".format(self.emitter_factory_id))
729 self.emitter_timeout = 0
730 self.label, self.emitter = self.factories[self.emitter_factory_id]()
731
732 def on_update(self, delta_time):
733 if self.emitter:
734 self.emitter_timeout += 1
735 self.emitter.update()
736 if self.emitter.can_reap() or self.emitter_timeout > EMITTER_TIMEOUT:
737 pyglet.clock.schedule_once(self.next_emitter, QUIET_BETWEEN_SPAWNS)
738 self.emitter = None
739 self.obj.update()
740 if self.obj.center_x > SCREEN_WIDTH:
741 self.obj.center_x = 0
742
743 def on_draw(self):
744 self.clear()
745 self.obj.draw()
746 if self.label:
747 arcade.draw_text("#{} {}".format(self.emitter_factory_id, self.label),
748 SCREEN_WIDTH / 2, SCREEN_HEIGHT - 20,
749 arcade.color.PALE_GOLD, 20, width=SCREEN_WIDTH, align="center",
750 anchor_x="center", anchor_y="center")
751 if self.emitter:
752 self.emitter.draw()
753 arcade.draw_text("Particles: " + str(self.emitter.get_count()), 10, 30, arcade.color.PALE_GOLD, 12)
754
755 def on_key_press(self, key, modifiers):
756 if key == arcade.key.ESCAPE:
757 arcade.close_window()
758
759
760if __name__ == "__main__":
761 game = MyGame()
762 arcade.run()