flutter3d
Showcase Changelog 38 packages API reference

A particle pool in one draw call: the source

since 0.2.0 Physics and particles

Regions the guide quotes: system (line 35), effect (line 40), burst (line 48), advance (line 61), check (line 74).

1/// One pool of particles, drawn in a single instanced call however many
2/// effects are bursting out of it.
3///
4/// Quoted by `particle_pool.md` and shown whole in the Source tab.
5library;
6
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 ParticlePoolDemo extends ShowcaseDemo {
13 late final ParticleSystem _particles;
14 late final ParticleEffect _sprayEffect;
15 late final ParticleContributor _contributor;
17 static const int _capacity = 512;
18 static const int _burstCount = 120;
20 // The point of the page is a pool that outlives any one burst; a burst
21 // that never repeated would never show it being reused.
22 static const double _pause = 1.0;
23 double _cooldown = 0.0;
25 @override
26 void configureView(DemoContext context) {
27 context.orbit
28 ..distance = 4.0
29 ..pitch = 0.3
30 ..yaw = 0.5;
31 }
33 @override
34 Scene build(DemoContext context) {
35 // A fixed seed, so the same burst comes out byte for byte on every run.
36 // That is what a `ParticleRandom` buys: not randomness, but a stream a
37 // particle owns from the moment it is born.
38 _particles = ParticleSystem(capacity: _capacity, seed: 20260919);
40 _sprayEffect = ParticleEffect(
41 count: _burstCount,
42 emitter: const SphereEmitter(speed: Range(1.5, 3.0)),
43 lifetime: const Range(0.8, 1.6),
44 size: const Range(0.05, 0.12),
45 color: Vector4(1.0, 0.7, 0.3, 1.0),
46 );
48 _particles.burst(_sprayEffect, Vector3.zero());
49 _contributor = context.renderer.addContributor(
50 ParticleContributor(_particles),
51 );
53 return Scene()..add(
54 LightNode(name: 'sun', intensity: 2.0)
55 ..setLocalForward(Vector3(-0.3, -0.6, -0.4)),
56 );
57 }
59 @override
60 void update(DemoContext context, double dt) {
61 _particles.advance(dt);
63 if (_particles.aliveCount == 0) {
64 _cooldown -= dt;
65 if (_cooldown <= 0.0) {
66 _particles.burst(_sprayEffect, Vector3.zero());
67 _cooldown = _pause;
68 }
69 }
70 }
72 @override
73 void verify(Scene scene, FrameResult frame) {
74 if (_particles.aliveCount != _burstCount) {
75 throw StateError(
76 'expected $_burstCount live particles, got ${_particles.aliveCount}',
77 );
78 }
79 if (!_contributor.isActive || frame.drawCalls < 1) {
80 throw StateError('the pool did not reach the frame');
81 }
82 }