Emitter shapes: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: shapes (line 55), along (line 67), burst (line 76), check (line 102).
1/// Four emitter shapes, each bursting from its own spot so the difference
2/// between them is something you can see rather than something you are told.
3///
4/// Quoted by `particle_emitters.md` and shown whole in the Source tab.
5library;
7import 'package:flutter3d/flutter3d.dart';
8import 'package:flutter3d_particles/flutter3d_particles.dart';
9import 'package:flutter3d_showcase/src/demo/demo.dart';
10import 'package:vector_math/vector_math.dart';
12final class ParticleEmittersDemo extends ShowcaseDemo {
13 late final ParticleSystem _particles;
14 late final ParticleContributor _contributor;
15 late final List<(Vector3, ParticleEmitter)> _shapes;
17 static const int _perBurst = 40;
19 // Four separate bursts that each ran once would leave three of the four
20 // shapes dead within a couple of seconds while the smoke of the fourth
21 // drifted on alone.
22 static const double _pause = 1.0;
23 double _cooldown = 0.0;
26 void configureView(DemoContext context) {
27 context.orbit
28 ..distance = 6.0
29 ..pitch = 0.25
30 ..yaw = 0.5;
31 }
33 ParticleEffect _effect(ParticleEmitter emitter) => ParticleEffect(
34 count: _perBurst,
35 emitter: emitter,
36 lifetime: const Range(0.6, 1.2),
37 size: const Range(0.05, 0.09),
38 color: Vector4(0.8, 0.85, 1.0, 1.0),
39 );
41 void _burstAll() {
42 for (final (Vector3 origin, ParticleEmitter emitter) in _shapes) {
43 _particles.burst(
44 _effect(emitter),
45 origin,
46 direction: Vector3(0.0, 1.0, 0.0),
47 );
48 }
49 }
52 Scene build(DemoContext context) {
53 _particles = ParticleSystem(capacity: _perBurst * 4, seed: 314159);
55 // A sphere throws particles outwards in every direction: an explosion.
56 // A cone narrows that spread around one axis: a muzzle flash, sparks off
57 // a wall. A box fills a volume rather than starting at a point: rain over
58 // an area. A drift barely moves outward at all: smoke.
59 _shapes = <(Vector3, ParticleEmitter)>[
60 (Vector3(-3.0, 0.0, 0.0), const SphereEmitter(speed: Range(1.0, 2.5))),
61 (
62 Vector3(-1.0, 0.0, 0.0),
63 const ConeEmitter(speed: Range(2.0, 4.0), halfAngleDegrees: 18.0),
64 ),
65 (
66 Vector3(1.0, 0.0, 0.0),
67 BoxEmitter(
68 halfExtents: Vector3(0.6, 0.05, 0.6),
69 speed: const Range(0.3, 0.6),
70 along: Vector3(0, 1, 0),
71 ),
72 ),
73 (Vector3(3.0, 0.0, 0.0), const DriftEmitter()),
74 ];
76 _burstAll();
77 _contributor = context.renderer.addContributor(
78 ParticleContributor(_particles),
79 );
81 return Scene()..add(
82 LightNode(name: 'sun', intensity: 2.0)
83 ..setLocalForward(Vector3(-0.3, -0.6, -0.4)),
84 );
85 }
88 void update(DemoContext context, double dt) {
89 _particles.advance(dt);
91 if (_particles.aliveCount == 0) {
92 _cooldown -= dt;
93 if (_cooldown <= 0.0) {
94 _burstAll();
95 _cooldown = _pause;
96 }
97 }
98 }
101 void verify(Scene scene, FrameResult frame) {
102 if (_particles.aliveCount != _perBurst * 4) {
103 throw StateError(
104 'expected ${_perBurst * 4} particles from four emitters, '
105 'got ${_particles.aliveCount}',
106 );
107 }
108 if (!_contributor.isActive || frame.drawCalls < 1) {
109 throw StateError('the emitters did not reach the frame');
110 }
111 }
112}