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