@guinetik/gcanvas 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yaml +70 -0
- package/.jshintrc +4 -0
- package/.vscode/settings.json +22 -0
- package/CLAUDE.md +310 -0
- package/blackhole.jpg +0 -0
- package/demo.png +0 -0
- package/demos/CNAME +1 -0
- package/demos/animations.html +31 -0
- package/demos/basic.html +38 -0
- package/demos/baskara.html +31 -0
- package/demos/bezier.html +35 -0
- package/demos/beziersignature.html +29 -0
- package/demos/blackhole.html +28 -0
- package/demos/blob.html +35 -0
- package/demos/demos.css +289 -0
- package/demos/easing.html +28 -0
- package/demos/events.html +195 -0
- package/demos/fluent.html +647 -0
- package/demos/fractals.html +36 -0
- package/demos/genart.html +26 -0
- package/demos/gendream.html +26 -0
- package/demos/group.html +36 -0
- package/demos/home.html +587 -0
- package/demos/index.html +364 -0
- package/demos/isometric.html +34 -0
- package/demos/js/animations.js +452 -0
- package/demos/js/basic.js +204 -0
- package/demos/js/baskara.js +751 -0
- package/demos/js/bezier.js +692 -0
- package/demos/js/beziersignature.js +241 -0
- package/demos/js/blackhole/accretiondisk.obj.js +379 -0
- package/demos/js/blackhole/blackhole.obj.js +318 -0
- package/demos/js/blackhole/index.js +409 -0
- package/demos/js/blackhole/particle.js +56 -0
- package/demos/js/blackhole/starfield.obj.js +218 -0
- package/demos/js/blob.js +2263 -0
- package/demos/js/easing.js +477 -0
- package/demos/js/fluent.js +183 -0
- package/demos/js/fractals.js +931 -0
- package/demos/js/fractalworker.js +93 -0
- package/demos/js/genart.js +268 -0
- package/demos/js/gendream.js +209 -0
- package/demos/js/group.js +140 -0
- package/demos/js/info-toggle.js +25 -0
- package/demos/js/isometric.js +863 -0
- package/demos/js/kerr.js +1556 -0
- package/demos/js/lavalamp.js +590 -0
- package/demos/js/layout.js +354 -0
- package/demos/js/mondrian.js +285 -0
- package/demos/js/opacity.js +275 -0
- package/demos/js/painter.js +484 -0
- package/demos/js/particles-showcase.js +514 -0
- package/demos/js/particles.js +299 -0
- package/demos/js/patterns.js +397 -0
- package/demos/js/penrose/artifact.js +69 -0
- package/demos/js/penrose/blackhole.js +121 -0
- package/demos/js/penrose/constants.js +73 -0
- package/demos/js/penrose/game.js +943 -0
- package/demos/js/penrose/lore.js +278 -0
- package/demos/js/penrose/penrosescene.js +892 -0
- package/demos/js/penrose/ship.js +216 -0
- package/demos/js/penrose/sounds.js +211 -0
- package/demos/js/penrose/voidparticle.js +55 -0
- package/demos/js/penrose/voidscene.js +258 -0
- package/demos/js/penrose/voidship.js +144 -0
- package/demos/js/penrose/wormhole.js +46 -0
- package/demos/js/pipeline.js +555 -0
- package/demos/js/scene.js +304 -0
- package/demos/js/scenes.js +320 -0
- package/demos/js/schrodinger.js +410 -0
- package/demos/js/schwarzschild.js +1023 -0
- package/demos/js/shapes.js +628 -0
- package/demos/js/space/alien.js +171 -0
- package/demos/js/space/boom.js +98 -0
- package/demos/js/space/boss.js +353 -0
- package/demos/js/space/buff.js +73 -0
- package/demos/js/space/bullet.js +102 -0
- package/demos/js/space/constants.js +85 -0
- package/demos/js/space/game.js +1884 -0
- package/demos/js/space/hud.js +112 -0
- package/demos/js/space/laserbeam.js +179 -0
- package/demos/js/space/lightning.js +277 -0
- package/demos/js/space/minion.js +192 -0
- package/demos/js/space/missile.js +212 -0
- package/demos/js/space/player.js +430 -0
- package/demos/js/space/powerup.js +90 -0
- package/demos/js/space/starfield.js +58 -0
- package/demos/js/space/starpower.js +90 -0
- package/demos/js/spacetime.js +559 -0
- package/demos/js/svgtween.js +204 -0
- package/demos/js/tde/accretiondisk.js +418 -0
- package/demos/js/tde/blackhole.js +219 -0
- package/demos/js/tde/blackholescene.js +209 -0
- package/demos/js/tde/config.js +59 -0
- package/demos/js/tde/index.js +695 -0
- package/demos/js/tde/jets.js +290 -0
- package/demos/js/tde/lensedstarfield.js +147 -0
- package/demos/js/tde/tdestar.js +317 -0
- package/demos/js/tde/tidalstream.js +356 -0
- package/demos/js/tde_old/blackhole.obj.js +354 -0
- package/demos/js/tde_old/debris.obj.js +791 -0
- package/demos/js/tde_old/flare.obj.js +239 -0
- package/demos/js/tde_old/index.js +448 -0
- package/demos/js/tde_old/star.obj.js +812 -0
- package/demos/js/tiles.js +312 -0
- package/demos/js/tweendemo.js +79 -0
- package/demos/js/visibility.js +102 -0
- package/demos/kerr.html +28 -0
- package/demos/lavalamp.html +27 -0
- package/demos/layouts.html +37 -0
- package/demos/logo.svg +4 -0
- package/demos/loop.html +84 -0
- package/demos/mondrian.html +32 -0
- package/demos/og_image.png +0 -0
- package/demos/opacity.html +36 -0
- package/demos/painter.html +39 -0
- package/demos/particles-showcase.html +28 -0
- package/demos/particles.html +24 -0
- package/demos/patterns.html +33 -0
- package/demos/penrose-game.html +31 -0
- package/demos/pipeline.html +737 -0
- package/demos/scene.html +33 -0
- package/demos/scenes.html +96 -0
- package/demos/schrodinger.html +27 -0
- package/demos/schwarzschild.html +27 -0
- package/demos/shapes.html +16 -0
- package/demos/space.html +85 -0
- package/demos/spacetime.html +27 -0
- package/demos/svgtween.html +29 -0
- package/demos/tde.html +28 -0
- package/demos/tiles.html +28 -0
- package/demos/transforms.html +400 -0
- package/demos/tween.html +45 -0
- package/demos/visibility.html +33 -0
- package/disk_example.png +0 -0
- package/docs/README.md +222 -0
- package/docs/concepts/architecture-overview.md +204 -0
- package/docs/concepts/lifecycle.md +255 -0
- package/docs/concepts/rendering-pipeline.md +279 -0
- package/docs/concepts/tde-zorder.md +106 -0
- package/docs/concepts/two-layer-architecture.md +229 -0
- package/docs/getting-started/first-game.md +354 -0
- package/docs/getting-started/hello-world.md +269 -0
- package/docs/getting-started/installation.md +157 -0
- package/docs/modules/collision/README.md +453 -0
- package/docs/modules/fluent/README.md +1075 -0
- package/docs/modules/game/README.md +303 -0
- package/docs/modules/isometric-camera.md +210 -0
- package/docs/modules/isometric.md +275 -0
- package/docs/modules/painter/README.md +328 -0
- package/docs/modules/particle/README.md +559 -0
- package/docs/modules/shapes/README.md +221 -0
- package/docs/modules/shapes/base/euclidian.md +123 -0
- package/docs/modules/shapes/base/geometry2d.md +204 -0
- package/docs/modules/shapes/base/renderable.md +215 -0
- package/docs/modules/shapes/base/shape.md +262 -0
- package/docs/modules/shapes/base/transformable.md +243 -0
- package/docs/modules/shapes/hierarchy.md +218 -0
- package/docs/modules/state/README.md +577 -0
- package/docs/modules/util/README.md +99 -0
- package/docs/modules/util/camera3d.md +412 -0
- package/docs/modules/util/scene3d.md +395 -0
- package/index.html +17 -0
- package/jsdoc.json +50 -0
- package/package.json +55 -0
- package/readme.md +599 -0
- package/scripts/build-demo.js +69 -0
- package/scripts/bundle4llm.js +276 -0
- package/scripts/clearconsole.js +48 -0
- package/src/collision/collision-system.js +332 -0
- package/src/collision/collision.js +303 -0
- package/src/collision/index.js +10 -0
- package/src/fluent/fluent-game.js +430 -0
- package/src/fluent/fluent-go.js +1060 -0
- package/src/fluent/fluent-layer.js +152 -0
- package/src/fluent/fluent-scene.js +291 -0
- package/src/fluent/index.js +98 -0
- package/src/fluent/sketch.js +380 -0
- package/src/game/game.js +467 -0
- package/src/game/index.js +49 -0
- package/src/game/objects/go.js +220 -0
- package/src/game/objects/imagego.js +30 -0
- package/src/game/objects/index.js +54 -0
- package/src/game/objects/isometric-scene.js +260 -0
- package/src/game/objects/layoutscene.js +549 -0
- package/src/game/objects/scene.js +175 -0
- package/src/game/objects/scene3d.js +118 -0
- package/src/game/objects/text.js +221 -0
- package/src/game/objects/wrapper.js +232 -0
- package/src/game/pipeline.js +243 -0
- package/src/game/ui/button.js +396 -0
- package/src/game/ui/cursor.js +93 -0
- package/src/game/ui/fps.js +91 -0
- package/src/game/ui/index.js +5 -0
- package/src/game/ui/togglebutton.js +93 -0
- package/src/game/ui/tooltip.js +249 -0
- package/src/index.js +25 -0
- package/src/io/events.js +20 -0
- package/src/io/index.js +86 -0
- package/src/io/input.js +70 -0
- package/src/io/keys.js +152 -0
- package/src/io/mouse.js +61 -0
- package/src/io/touch.js +39 -0
- package/src/logger/debugtab.js +138 -0
- package/src/logger/index.js +3 -0
- package/src/logger/loggable.js +47 -0
- package/src/logger/logger.js +113 -0
- package/src/math/complex.js +37 -0
- package/src/math/constants.js +1 -0
- package/src/math/fractal.js +1271 -0
- package/src/math/gr.js +201 -0
- package/src/math/heat.js +202 -0
- package/src/math/index.js +12 -0
- package/src/math/noise.js +433 -0
- package/src/math/orbital.js +191 -0
- package/src/math/patterns.js +1339 -0
- package/src/math/penrose.js +259 -0
- package/src/math/quantum.js +115 -0
- package/src/math/random.js +195 -0
- package/src/math/tensor.js +1009 -0
- package/src/mixins/anchor.js +131 -0
- package/src/mixins/draggable.js +72 -0
- package/src/mixins/index.js +2 -0
- package/src/motion/bezier.js +132 -0
- package/src/motion/bounce.js +58 -0
- package/src/motion/easing.js +349 -0
- package/src/motion/float.js +130 -0
- package/src/motion/follow.js +125 -0
- package/src/motion/hop.js +52 -0
- package/src/motion/index.js +82 -0
- package/src/motion/motion.js +1124 -0
- package/src/motion/orbit.js +49 -0
- package/src/motion/oscillate.js +39 -0
- package/src/motion/parabolic.js +141 -0
- package/src/motion/patrol.js +147 -0
- package/src/motion/pendulum.js +48 -0
- package/src/motion/pulse.js +88 -0
- package/src/motion/shake.js +83 -0
- package/src/motion/spiral.js +144 -0
- package/src/motion/spring.js +150 -0
- package/src/motion/swing.js +47 -0
- package/src/motion/tween.js +92 -0
- package/src/motion/tweenetik.js +139 -0
- package/src/motion/waypoint.js +210 -0
- package/src/painter/index.js +8 -0
- package/src/painter/painter.colors.js +331 -0
- package/src/painter/painter.effects.js +230 -0
- package/src/painter/painter.img.js +229 -0
- package/src/painter/painter.js +295 -0
- package/src/painter/painter.lines.js +189 -0
- package/src/painter/painter.opacity.js +41 -0
- package/src/painter/painter.shapes.js +277 -0
- package/src/painter/painter.text.js +273 -0
- package/src/particle/emitter.js +124 -0
- package/src/particle/index.js +11 -0
- package/src/particle/particle-system.js +322 -0
- package/src/particle/particle.js +71 -0
- package/src/particle/updaters.js +170 -0
- package/src/shapes/arc.js +43 -0
- package/src/shapes/arrow.js +33 -0
- package/src/shapes/bezier.js +42 -0
- package/src/shapes/circle.js +62 -0
- package/src/shapes/clouds.js +56 -0
- package/src/shapes/cone.js +219 -0
- package/src/shapes/cross.js +70 -0
- package/src/shapes/cube.js +244 -0
- package/src/shapes/cylinder.js +254 -0
- package/src/shapes/diamond.js +48 -0
- package/src/shapes/euclidian.js +111 -0
- package/src/shapes/figure.js +115 -0
- package/src/shapes/geometry.js +220 -0
- package/src/shapes/group.js +375 -0
- package/src/shapes/heart.js +42 -0
- package/src/shapes/hexagon.js +26 -0
- package/src/shapes/image.js +192 -0
- package/src/shapes/index.js +111 -0
- package/src/shapes/line.js +29 -0
- package/src/shapes/pattern.js +90 -0
- package/src/shapes/pin.js +44 -0
- package/src/shapes/poly.js +31 -0
- package/src/shapes/prism.js +226 -0
- package/src/shapes/rect.js +35 -0
- package/src/shapes/renderable.js +333 -0
- package/src/shapes/ring.js +26 -0
- package/src/shapes/roundrect.js +95 -0
- package/src/shapes/shape.js +117 -0
- package/src/shapes/slice.js +26 -0
- package/src/shapes/sphere.js +314 -0
- package/src/shapes/sphere3d.js +537 -0
- package/src/shapes/square.js +15 -0
- package/src/shapes/star.js +99 -0
- package/src/shapes/svg.js +408 -0
- package/src/shapes/text.js +553 -0
- package/src/shapes/traceable.js +83 -0
- package/src/shapes/transform.js +357 -0
- package/src/shapes/transformable.js +172 -0
- package/src/shapes/triangle.js +26 -0
- package/src/sound/index.js +17 -0
- package/src/sound/sound.js +473 -0
- package/src/sound/synth.analyzer.js +149 -0
- package/src/sound/synth.effects.js +207 -0
- package/src/sound/synth.envelope.js +59 -0
- package/src/sound/synth.js +229 -0
- package/src/sound/synth.musical.js +160 -0
- package/src/sound/synth.noise.js +85 -0
- package/src/sound/synth.oscillators.js +293 -0
- package/src/state/index.js +10 -0
- package/src/state/state-machine.js +371 -0
- package/src/util/camera3d.js +438 -0
- package/src/util/index.js +6 -0
- package/src/util/isometric-camera.js +235 -0
- package/src/util/layout.js +317 -0
- package/src/util/position.js +147 -0
- package/src/util/tasks.js +47 -0
- package/src/util/zindex.js +287 -0
- package/src/webgl/index.js +9 -0
- package/src/webgl/shaders/sphere-shaders.js +994 -0
- package/src/webgl/webgl-renderer.js +388 -0
- package/tde.png +0 -0
- package/test/math/orbital.test.js +61 -0
- package/test/math/tensor.test.js +114 -0
- package/test/particle/emitter.test.js +204 -0
- package/test/particle/particle-system.test.js +310 -0
- package/test/particle/particle.test.js +116 -0
- package/test/particle/updaters.test.js +386 -0
- package/test/setup.js +120 -0
- package/test/shapes/euclidian.test.js +44 -0
- package/test/shapes/geometry.test.js +86 -0
- package/test/shapes/group.test.js +86 -0
- package/test/shapes/rectangle.test.js +64 -0
- package/test/shapes/transform.test.js +379 -0
- package/test/util/camera3d.test.js +428 -0
- package/test/util/scene3d.test.js +352 -0
- package/types/collision.d.ts +249 -0
- package/types/common.d.ts +155 -0
- package/types/game.d.ts +497 -0
- package/types/index.d.ts +309 -0
- package/types/io.d.ts +188 -0
- package/types/logger.d.ts +127 -0
- package/types/math.d.ts +268 -0
- package/types/mixins.d.ts +92 -0
- package/types/motion.d.ts +678 -0
- package/types/painter.d.ts +378 -0
- package/types/shapes.d.ts +864 -0
- package/types/sound.d.ts +672 -0
- package/types/state.d.ts +251 -0
- package/types/util.d.ts +253 -0
- package/vite.config.js +50 -0
- package/vitest.config.js +13 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
# Particle Module
|
|
2
|
+
|
|
3
|
+
> High-performance particle systems with object pooling, composable updaters, and optional 3D projection.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The particle module provides a flexible system for creating visual effects like fire, smoke, sparks, rain, and explosions. It features:
|
|
8
|
+
|
|
9
|
+
- **Object pooling** to minimize garbage collection
|
|
10
|
+
- **Composable updaters** for modular physics and effects
|
|
11
|
+
- **Named emitters** for easy management
|
|
12
|
+
- **Optional Camera3D integration** with depth sorting
|
|
13
|
+
- **Blend modes** for additive/screen effects
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import { Game, ParticleSystem, ParticleEmitter, Updaters } from 'gcanvas';
|
|
19
|
+
|
|
20
|
+
class MyGame extends Game {
|
|
21
|
+
init() {
|
|
22
|
+
super.init();
|
|
23
|
+
|
|
24
|
+
// Create particle system
|
|
25
|
+
this.particles = new ParticleSystem(this, {
|
|
26
|
+
maxParticles: 5000,
|
|
27
|
+
blendMode: "screen",
|
|
28
|
+
updaters: [
|
|
29
|
+
Updaters.velocity,
|
|
30
|
+
Updaters.lifetime,
|
|
31
|
+
Updaters.gravity(150),
|
|
32
|
+
Updaters.fadeOut,
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Add a fountain emitter
|
|
37
|
+
this.particles.addEmitter("fountain", new ParticleEmitter({
|
|
38
|
+
position: { x: 400, y: 500 },
|
|
39
|
+
velocity: { x: 0, y: -300 },
|
|
40
|
+
velocitySpread: { x: 50, y: 30 },
|
|
41
|
+
lifetime: { min: 1, max: 3 },
|
|
42
|
+
size: { min: 2, max: 6 },
|
|
43
|
+
color: { r: 100, g: 180, b: 255, a: 1 },
|
|
44
|
+
rate: 100,
|
|
45
|
+
shape: "circle",
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
this.pipeline.add(this.particles);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
update(dt) {
|
|
52
|
+
super.update(dt);
|
|
53
|
+
// Particles update automatically via pipeline
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Core Classes
|
|
59
|
+
|
|
60
|
+
| Class | Description |
|
|
61
|
+
|-------|-------------|
|
|
62
|
+
| **ParticleSystem** | GameObject that manages particles and emitters |
|
|
63
|
+
| **ParticleEmitter** | Defines spawn position, velocity, and appearance |
|
|
64
|
+
| **Updaters** | Composable behavior functions |
|
|
65
|
+
| **Particle** | Individual particle data (managed internally) |
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## ParticleSystem
|
|
70
|
+
|
|
71
|
+
The main container that manages particles, emitters, and rendering.
|
|
72
|
+
|
|
73
|
+
### Constructor Options
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
const particles = new ParticleSystem(game, {
|
|
77
|
+
maxParticles: 5000, // Maximum active particles
|
|
78
|
+
camera: myCamera, // Optional Camera3D for 3D projection
|
|
79
|
+
depthSort: true, // Sort by depth (requires camera)
|
|
80
|
+
blendMode: "screen", // Canvas blend mode
|
|
81
|
+
worldSpace: false, // Position in world vs screen space
|
|
82
|
+
updaters: [ // Behavior functions
|
|
83
|
+
Updaters.velocity,
|
|
84
|
+
Updaters.lifetime,
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
| Option | Type | Default | Description |
|
|
90
|
+
|--------|------|---------|-------------|
|
|
91
|
+
| `maxParticles` | `number` | `5000` | Maximum active particles |
|
|
92
|
+
| `camera` | `Camera3D` | `null` | Camera for 3D projection |
|
|
93
|
+
| `depthSort` | `boolean` | `false` | Enable depth sorting |
|
|
94
|
+
| `blendMode` | `string` | `"source-over"` | Canvas composite operation |
|
|
95
|
+
| `worldSpace` | `boolean` | `false` | World vs screen space positioning |
|
|
96
|
+
| `updaters` | `Function[]` | `[velocity, lifetime]` | Behavior functions |
|
|
97
|
+
|
|
98
|
+
### Emitter Management
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
// Add emitter with name
|
|
102
|
+
particles.addEmitter("fire", fireEmitter);
|
|
103
|
+
particles.addEmitter("sparks", sparkEmitter);
|
|
104
|
+
|
|
105
|
+
// Get emitter by name
|
|
106
|
+
const fire = particles.getEmitter("fire");
|
|
107
|
+
fire.position.x = 200;
|
|
108
|
+
|
|
109
|
+
// Remove emitter
|
|
110
|
+
particles.removeEmitter("sparks");
|
|
111
|
+
|
|
112
|
+
// Access all emitters
|
|
113
|
+
for (const [name, emitter] of particles.emitters) {
|
|
114
|
+
emitter.active = false;
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Spawning Particles
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
// Continuous emission via emitter rate
|
|
122
|
+
// (happens automatically in update)
|
|
123
|
+
|
|
124
|
+
// Burst spawn
|
|
125
|
+
particles.burst(50, "fire"); // By emitter name
|
|
126
|
+
particles.burst(50, myEmitter); // By emitter instance
|
|
127
|
+
|
|
128
|
+
// Manual emission
|
|
129
|
+
particles.emit(10, myEmitter);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Properties
|
|
133
|
+
|
|
134
|
+
| Property | Type | Description |
|
|
135
|
+
|----------|------|-------------|
|
|
136
|
+
| `particleCount` | `number` | Current active particles |
|
|
137
|
+
| `poolSize` | `number` | Recycled particles ready for reuse |
|
|
138
|
+
| `emitters` | `Map<string, ParticleEmitter>` | All registered emitters |
|
|
139
|
+
|
|
140
|
+
### Methods
|
|
141
|
+
|
|
142
|
+
| Method | Description |
|
|
143
|
+
|--------|-------------|
|
|
144
|
+
| `addEmitter(name, emitter)` | Register an emitter |
|
|
145
|
+
| `removeEmitter(name)` | Remove an emitter |
|
|
146
|
+
| `getEmitter(name)` | Get emitter by name |
|
|
147
|
+
| `burst(count, emitterOrName)` | Spawn burst of particles |
|
|
148
|
+
| `emit(count, emitter)` | Emit particles from emitter |
|
|
149
|
+
| `clear()` | Remove all active particles |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## ParticleEmitter
|
|
154
|
+
|
|
155
|
+
Defines how particles are spawned - their position, velocity, appearance, and rate.
|
|
156
|
+
|
|
157
|
+
### Constructor Options
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
const emitter = new ParticleEmitter({
|
|
161
|
+
// Emission rate (particles per second, 0 for burst-only)
|
|
162
|
+
rate: 50,
|
|
163
|
+
|
|
164
|
+
// Spawn position and randomization
|
|
165
|
+
position: { x: 400, y: 300, z: 0 },
|
|
166
|
+
spread: { x: 20, y: 0, z: 20 },
|
|
167
|
+
|
|
168
|
+
// Initial velocity and randomization
|
|
169
|
+
velocity: { x: 0, y: -200, z: 0 },
|
|
170
|
+
velocitySpread: { x: 50, y: 30, z: 50 },
|
|
171
|
+
|
|
172
|
+
// Particle lifetime (seconds)
|
|
173
|
+
lifetime: { min: 1, max: 3 },
|
|
174
|
+
|
|
175
|
+
// Particle size (pixels)
|
|
176
|
+
size: { min: 2, max: 8 },
|
|
177
|
+
|
|
178
|
+
// Base color
|
|
179
|
+
color: { r: 255, g: 100, b: 50, a: 1 },
|
|
180
|
+
|
|
181
|
+
// Shape: "circle", "square", "triangle"
|
|
182
|
+
shape: "circle",
|
|
183
|
+
|
|
184
|
+
// Start active?
|
|
185
|
+
active: true,
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
| Option | Type | Default | Description |
|
|
190
|
+
|--------|------|---------|-------------|
|
|
191
|
+
| `rate` | `number` | `10` | Particles per second |
|
|
192
|
+
| `position` | `{x, y, z}` | `{0, 0, 0}` | Spawn center |
|
|
193
|
+
| `spread` | `{x, y, z}` | `{0, 0, 0}` | Position randomization |
|
|
194
|
+
| `velocity` | `{x, y, z}` | `{0, 0, 0}` | Initial velocity |
|
|
195
|
+
| `velocitySpread` | `{x, y, z}` | `{0, 0, 0}` | Velocity randomization |
|
|
196
|
+
| `lifetime` | `{min, max}` | `{1, 2}` | Lifetime in seconds |
|
|
197
|
+
| `size` | `{min, max}` | `{1, 1}` | Size in pixels |
|
|
198
|
+
| `color` | `{r, g, b, a}` | `{255, 255, 255, 1}` | Base color |
|
|
199
|
+
| `shape` | `string` | `"circle"` | Particle shape |
|
|
200
|
+
| `active` | `boolean` | `true` | Is emitting? |
|
|
201
|
+
|
|
202
|
+
### Runtime Modification
|
|
203
|
+
|
|
204
|
+
Emitters can be modified at runtime:
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
const emitter = particles.getEmitter("fountain");
|
|
208
|
+
|
|
209
|
+
// Move emitter
|
|
210
|
+
emitter.position.x = mouseX;
|
|
211
|
+
emitter.position.y = mouseY;
|
|
212
|
+
|
|
213
|
+
// Change color
|
|
214
|
+
emitter.color = { r: 255, g: 0, b: 0, a: 1 };
|
|
215
|
+
|
|
216
|
+
// Adjust rate
|
|
217
|
+
emitter.rate = 200;
|
|
218
|
+
|
|
219
|
+
// Pause/resume
|
|
220
|
+
emitter.active = false;
|
|
221
|
+
emitter.active = true;
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Updaters
|
|
227
|
+
|
|
228
|
+
Updaters are composable functions that define particle behavior. Each updater has the signature:
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
(particle, dt, system) => void
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Built-in Updaters
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
import { Updaters } from 'gcanvas';
|
|
238
|
+
|
|
239
|
+
const particles = new ParticleSystem(game, {
|
|
240
|
+
updaters: [
|
|
241
|
+
// Core physics
|
|
242
|
+
Updaters.velocity, // Apply velocity to position
|
|
243
|
+
Updaters.lifetime, // Track age, kill when expired
|
|
244
|
+
|
|
245
|
+
// Forces
|
|
246
|
+
Updaters.gravity(200), // Downward acceleration
|
|
247
|
+
Updaters.rise(100), // Upward acceleration (fire, smoke)
|
|
248
|
+
Updaters.damping(0.98), // Velocity friction
|
|
249
|
+
|
|
250
|
+
// Visual effects
|
|
251
|
+
Updaters.fadeOut, // Fade alpha over lifetime
|
|
252
|
+
Updaters.fadeInOut, // Fade in then out
|
|
253
|
+
Updaters.shrink(0), // Shrink to 0 over lifetime
|
|
254
|
+
Updaters.grow(2), // Grow to 2x over lifetime
|
|
255
|
+
|
|
256
|
+
// Color
|
|
257
|
+
Updaters.colorOverLife(
|
|
258
|
+
{ r: 255, g: 200, b: 100 }, // Start color
|
|
259
|
+
{ r: 100, g: 50, b: 20 } // End color
|
|
260
|
+
),
|
|
261
|
+
|
|
262
|
+
// Movement modifiers
|
|
263
|
+
Updaters.wobble(10), // Random velocity jitter
|
|
264
|
+
Updaters.attract( // Attract to point
|
|
265
|
+
{ x: 400, y: 300 },
|
|
266
|
+
100
|
|
267
|
+
),
|
|
268
|
+
|
|
269
|
+
// Boundaries
|
|
270
|
+
Updaters.bounds(
|
|
271
|
+
{ left: 0, right: 800, top: 0, bottom: 600 },
|
|
272
|
+
0.8 // Bounce factor
|
|
273
|
+
),
|
|
274
|
+
],
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Updater Reference
|
|
279
|
+
|
|
280
|
+
| Updater | Parameters | Description |
|
|
281
|
+
|---------|------------|-------------|
|
|
282
|
+
| `velocity` | - | Apply velocity to position |
|
|
283
|
+
| `lifetime` | - | Track age, kill when expired |
|
|
284
|
+
| `gravity(strength)` | `strength=200` | Downward acceleration |
|
|
285
|
+
| `rise(strength)` | `strength=100` | Upward acceleration |
|
|
286
|
+
| `damping(factor)` | `factor=0.98` | Velocity friction |
|
|
287
|
+
| `fadeOut` | - | Fade alpha over lifetime |
|
|
288
|
+
| `fadeInOut` | - | Fade in then out |
|
|
289
|
+
| `shrink(endScale)` | `endScale=0` | Shrink over lifetime |
|
|
290
|
+
| `grow(endScale)` | `endScale=2` | Grow over lifetime |
|
|
291
|
+
| `colorOverLife(start, end)` | Color objects | Interpolate color |
|
|
292
|
+
| `wobble(strength)` | `strength=10` | Random velocity jitter |
|
|
293
|
+
| `attract(target, strength)` | Position, `strength=100` | Attract to point |
|
|
294
|
+
| `bounds(bounds, bounce)` | Rect, `bounce=0.8` | Bounce off boundaries |
|
|
295
|
+
|
|
296
|
+
### Custom Updaters
|
|
297
|
+
|
|
298
|
+
Create your own updater functions:
|
|
299
|
+
|
|
300
|
+
```js
|
|
301
|
+
// Simple updater
|
|
302
|
+
const spin = (p, dt) => {
|
|
303
|
+
p.rotation = (p.rotation ?? 0) + dt * 5;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// Factory for configurable updater
|
|
307
|
+
const orbit = (centerX, centerY, speed) => (p, dt) => {
|
|
308
|
+
const angle = Math.atan2(p.y - centerY, p.x - centerX);
|
|
309
|
+
const dist = Math.hypot(p.x - centerX, p.y - centerY);
|
|
310
|
+
const newAngle = angle + speed * dt;
|
|
311
|
+
p.x = centerX + Math.cos(newAngle) * dist;
|
|
312
|
+
p.y = centerY + Math.sin(newAngle) * dist;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// Use in system
|
|
316
|
+
const particles = new ParticleSystem(game, {
|
|
317
|
+
updaters: [
|
|
318
|
+
Updaters.velocity,
|
|
319
|
+
Updaters.lifetime,
|
|
320
|
+
spin,
|
|
321
|
+
orbit(400, 300, 2),
|
|
322
|
+
],
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Particle Properties
|
|
329
|
+
|
|
330
|
+
Each particle has these properties (accessible in updaters):
|
|
331
|
+
|
|
332
|
+
| Property | Type | Description |
|
|
333
|
+
|----------|------|-------------|
|
|
334
|
+
| `x`, `y`, `z` | `number` | Position |
|
|
335
|
+
| `vx`, `vy`, `vz` | `number` | Velocity |
|
|
336
|
+
| `size` | `number` | Current size |
|
|
337
|
+
| `color` | `{r, g, b, a}` | Current color |
|
|
338
|
+
| `age` | `number` | Time alive (seconds) |
|
|
339
|
+
| `lifetime` | `number` | Total lifetime (seconds) |
|
|
340
|
+
| `progress` | `number` | `age / lifetime` (0-1) |
|
|
341
|
+
| `alive` | `boolean` | Is particle active? |
|
|
342
|
+
| `shape` | `string` | Particle shape |
|
|
343
|
+
| `custom` | `Object` | Custom data storage |
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## 3D Particles with Camera3D
|
|
348
|
+
|
|
349
|
+
For 3D particle effects, pass a Camera3D instance:
|
|
350
|
+
|
|
351
|
+
```js
|
|
352
|
+
import { Camera3D, ParticleSystem, ParticleEmitter, Updaters } from 'gcanvas';
|
|
353
|
+
|
|
354
|
+
class My3DParticles extends Game {
|
|
355
|
+
init() {
|
|
356
|
+
super.init();
|
|
357
|
+
|
|
358
|
+
// Create camera
|
|
359
|
+
this.camera = new Camera3D({
|
|
360
|
+
rotationX: 0.3,
|
|
361
|
+
perspective: 800,
|
|
362
|
+
autoRotate: true,
|
|
363
|
+
});
|
|
364
|
+
this.camera.enableMouseControl(this.canvas);
|
|
365
|
+
|
|
366
|
+
// Create 3D particle system
|
|
367
|
+
this.particles = new ParticleSystem(this, {
|
|
368
|
+
camera: this.camera,
|
|
369
|
+
depthSort: true, // Sort back-to-front
|
|
370
|
+
blendMode: "screen",
|
|
371
|
+
updaters: [
|
|
372
|
+
Updaters.velocity,
|
|
373
|
+
Updaters.lifetime,
|
|
374
|
+
Updaters.fadeOut,
|
|
375
|
+
],
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// Emitter with z-axis spread
|
|
379
|
+
this.particles.addEmitter("explosion", new ParticleEmitter({
|
|
380
|
+
position: { x: 0, y: 0, z: 0 }, // Center of 3D space
|
|
381
|
+
spread: { x: 50, y: 50, z: 50 }, // 3D spread
|
|
382
|
+
velocity: { x: 0, y: -100, z: 0 },
|
|
383
|
+
velocitySpread: { x: 100, y: 100, z: 100 },
|
|
384
|
+
lifetime: { min: 1, max: 2 },
|
|
385
|
+
size: { min: 4, max: 10 },
|
|
386
|
+
rate: 50,
|
|
387
|
+
}));
|
|
388
|
+
|
|
389
|
+
this.pipeline.add(this.particles);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
update(dt) {
|
|
393
|
+
super.update(dt);
|
|
394
|
+
this.camera.update(dt);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Common Effects
|
|
402
|
+
|
|
403
|
+
### Fire
|
|
404
|
+
|
|
405
|
+
```js
|
|
406
|
+
particles.addEmitter("fire", new ParticleEmitter({
|
|
407
|
+
position: { x: 400, y: 550 },
|
|
408
|
+
spread: { x: 30, y: 0 },
|
|
409
|
+
velocity: { x: 0, y: -80 },
|
|
410
|
+
velocitySpread: { x: 20, y: 15 },
|
|
411
|
+
lifetime: { min: 0.5, max: 1.5 },
|
|
412
|
+
size: { min: 8, max: 16 },
|
|
413
|
+
color: { r: 255, g: 120, b: 40, a: 1 },
|
|
414
|
+
rate: 100,
|
|
415
|
+
shape: "circle",
|
|
416
|
+
}));
|
|
417
|
+
|
|
418
|
+
// With color updater for orange -> red -> dark
|
|
419
|
+
updaters: [
|
|
420
|
+
Updaters.velocity,
|
|
421
|
+
Updaters.lifetime,
|
|
422
|
+
Updaters.rise(80),
|
|
423
|
+
Updaters.fadeOut,
|
|
424
|
+
Updaters.colorOverLife(
|
|
425
|
+
{ r: 255, g: 150, b: 50 },
|
|
426
|
+
{ r: 80, g: 20, b: 10 }
|
|
427
|
+
),
|
|
428
|
+
]
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Snow
|
|
432
|
+
|
|
433
|
+
```js
|
|
434
|
+
particles.addEmitter("snow", new ParticleEmitter({
|
|
435
|
+
position: { x: 400, y: -20 },
|
|
436
|
+
spread: { x: 500, y: 0 },
|
|
437
|
+
velocity: { x: 0, y: 50 },
|
|
438
|
+
velocitySpread: { x: 20, y: 10 },
|
|
439
|
+
lifetime: { min: 5, max: 10 },
|
|
440
|
+
size: { min: 2, max: 5 },
|
|
441
|
+
color: { r: 255, g: 255, b: 255, a: 0.8 },
|
|
442
|
+
rate: 30,
|
|
443
|
+
shape: "circle",
|
|
444
|
+
}));
|
|
445
|
+
|
|
446
|
+
updaters: [
|
|
447
|
+
Updaters.velocity,
|
|
448
|
+
Updaters.lifetime,
|
|
449
|
+
Updaters.wobble(15),
|
|
450
|
+
]
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Explosion Burst
|
|
454
|
+
|
|
455
|
+
```js
|
|
456
|
+
// Create emitter with rate 0 (burst only)
|
|
457
|
+
const explosionEmitter = new ParticleEmitter({
|
|
458
|
+
position: { x: 0, y: 0 },
|
|
459
|
+
velocity: { x: 0, y: 0 },
|
|
460
|
+
velocitySpread: { x: 300, y: 300 },
|
|
461
|
+
lifetime: { min: 0.5, max: 1.5 },
|
|
462
|
+
size: { min: 4, max: 12 },
|
|
463
|
+
color: { r: 255, g: 200, b: 50, a: 1 },
|
|
464
|
+
rate: 0, // No continuous emission
|
|
465
|
+
shape: "circle",
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// Trigger explosion at position
|
|
469
|
+
explosionEmitter.position.x = clickX;
|
|
470
|
+
explosionEmitter.position.y = clickY;
|
|
471
|
+
particles.burst(100, explosionEmitter);
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Confetti
|
|
475
|
+
|
|
476
|
+
```js
|
|
477
|
+
const colors = [
|
|
478
|
+
{ r: 255, g: 80, b: 80 },
|
|
479
|
+
{ r: 80, g: 255, b: 80 },
|
|
480
|
+
{ r: 80, g: 80, b: 255 },
|
|
481
|
+
{ r: 255, g: 255, b: 80 },
|
|
482
|
+
];
|
|
483
|
+
|
|
484
|
+
const confettiEmitter = new ParticleEmitter({
|
|
485
|
+
velocity: { x: 0, y: -200 },
|
|
486
|
+
velocitySpread: { x: 150, y: 80 },
|
|
487
|
+
lifetime: { min: 2, max: 4 },
|
|
488
|
+
size: { min: 6, max: 12 },
|
|
489
|
+
rate: 0,
|
|
490
|
+
shape: "triangle",
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// Burst with random colors
|
|
494
|
+
for (let i = 0; i < 50; i++) {
|
|
495
|
+
confettiEmitter.color = { ...colors[i % colors.length], a: 1 };
|
|
496
|
+
particles.burst(1, confettiEmitter);
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## Architecture
|
|
503
|
+
|
|
504
|
+
```
|
|
505
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
506
|
+
│ ParticleSystem │
|
|
507
|
+
│ ┌───────────────────────────────────────────────────────┐ │
|
|
508
|
+
│ │ Emitters │ │
|
|
509
|
+
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
|
510
|
+
│ │ │ "fire" │ │ "sparks" │ │ "smoke" │ │ │
|
|
511
|
+
│ │ │ rate: 50 │ │ rate: 20 │ │ rate: 10 │ │ │
|
|
512
|
+
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
|
|
513
|
+
│ └───────────────────────────────────────────────────────┘ │
|
|
514
|
+
│ │ │
|
|
515
|
+
│ ▼ │
|
|
516
|
+
│ ┌───────────────────────────────────────────────────────┐ │
|
|
517
|
+
│ │ Active Particles (pooled) │ │
|
|
518
|
+
│ │ [p1, p2, p3, ... pN] ←→ [pool of recycled] │ │
|
|
519
|
+
│ └───────────────────────────────────────────────────────┘ │
|
|
520
|
+
│ │ │
|
|
521
|
+
│ ▼ │
|
|
522
|
+
│ ┌───────────────────────────────────────────────────────┐ │
|
|
523
|
+
│ │ Updaters │ │
|
|
524
|
+
│ │ velocity → lifetime → gravity → fadeOut → ... │ │
|
|
525
|
+
│ └───────────────────────────────────────────────────────┘ │
|
|
526
|
+
│ │ │
|
|
527
|
+
│ ▼ │
|
|
528
|
+
│ ┌───────────────────────────────────────────────────────┐ │
|
|
529
|
+
│ │ Rendering │ │
|
|
530
|
+
│ │ Simple 2D | Camera3D + Depth Sort │ │
|
|
531
|
+
│ └───────────────────────────────────────────────────────┘ │
|
|
532
|
+
└─────────────────────────────────────────────────────────────┘
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Performance Tips
|
|
538
|
+
|
|
539
|
+
1. **Use object pooling** - The system automatically pools particles. Keep `maxParticles` reasonable.
|
|
540
|
+
|
|
541
|
+
2. **Limit updaters** - Each updater runs on every particle every frame. Use only what you need.
|
|
542
|
+
|
|
543
|
+
3. **Use appropriate shapes** - `"circle"` is fastest, `"triangle"` slightly slower.
|
|
544
|
+
|
|
545
|
+
4. **Disable depth sorting** - If you don't need 3D, skip `depthSort: true`.
|
|
546
|
+
|
|
547
|
+
5. **Use `"screen"` blend mode wisely** - Additive blending is visually nice but can be expensive with many particles.
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Related
|
|
552
|
+
|
|
553
|
+
- [Game Module](../game/README.md) - Game loop and GameObjects
|
|
554
|
+
- [Scene3D](../util/scene3d.md) - 3D scene projection
|
|
555
|
+
|
|
556
|
+
## See Also
|
|
557
|
+
|
|
558
|
+
- [Camera3D](../util/camera3d.md) - 3D camera and projection
|
|
559
|
+
- [Motion Module](../motion/README.md) - Animation patterns
|