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