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