Particle Systems

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
 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()