@basementuniverse/particles-2d 1.0.1

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/.prettierrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "es5",
4
+ "arrowParens": "avoid"
5
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "github.copilot.enable": {
3
+ "suggestions": true,
4
+ "inlineSuggestions": true
5
+ },
6
+ "editor.codeActionsOnSave": {
7
+ "source.organizeImports": "explicit"
8
+ },
9
+ "[typescript]": {
10
+ "editor.formatOnSave": true,
11
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
12
+ },
13
+ "[typescriptreact]": {
14
+ "editor.formatOnSave": true,
15
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
16
+ }
17
+ }
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 Gordon Larrigan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,347 @@
1
+ # Game Component: Particle System 2D
2
+
3
+ A basic particle system component for use in 2d games.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @basementuniverse/particles-2d
9
+ ```
10
+
11
+ ## How to use
12
+
13
+ See `/demos` for some examples.
14
+
15
+ 1. Import the component and any other classes you need:
16
+
17
+ ```js
18
+ import {
19
+ ParticleSystem,
20
+ Emitter,
21
+ Attractor,
22
+ ForceField,
23
+ Collider,
24
+ } from '@basementuniverse/particles-2d';
25
+ ```
26
+
27
+ 2. Create a `ParticleSystem` instance:
28
+
29
+ ```js
30
+ const particleSystem = new ParticleSystem();
31
+ ```
32
+
33
+ 3. Add emitters, attractors, force fields, and colliders as needed:
34
+
35
+ ```js
36
+ particleSystem.emitters.push(
37
+ new Emitter(/* See below for options */)
38
+ );
39
+
40
+ particleSystem.attractors.push(
41
+ new Attractor(/* See below for options */)
42
+ );
43
+
44
+ particleSystem.forceFields.push(
45
+ new ForceField(/* See below for options */)
46
+ );
47
+
48
+ particleSystem.colliders.push(
49
+ new Collider(/* See below for options */)
50
+ );
51
+ ```
52
+
53
+ 4. Update and render the particle system in your game loop:
54
+
55
+ ```js
56
+ function update(deltaTime) {
57
+ particleSystem.update(deltaTime);
58
+
59
+ // Other game logic...
60
+ }
61
+
62
+ function draw(context) {
63
+ particleSystem.draw(context);
64
+
65
+ // Other rendering logic...
66
+ }
67
+ ```
68
+
69
+ ## Utility types
70
+
71
+ #### `vec2`
72
+
73
+ ```ts
74
+ { x: number, y: number }
75
+ ```
76
+
77
+ #### `Color`
78
+
79
+ ```ts
80
+ { r: number, g: number, b: number, a?: number }
81
+ ```
82
+
83
+ ## Emitters
84
+
85
+ Emitters are responsible for generating and emitting particles.
86
+
87
+ ```ts
88
+ new Emitter(
89
+ position: vec2, // { x: number, y: number }
90
+ size: vec2, // { x: number, y: number }
91
+ lifespan: number, // in seconds, use -1 for infinite lifespan
92
+ options?: EmitterOptions // see below...
93
+ );
94
+ ```
95
+
96
+ ### Emitter Options
97
+
98
+ ```js
99
+ {
100
+ particles: {
101
+ position
102
+ speed
103
+ direction
104
+ size
105
+ rotation
106
+ lifespan
107
+ style
108
+ options
109
+ },
110
+
111
+ emission: {
112
+ type
113
+ rate, n, delay, f // depends on type
114
+ }
115
+ }
116
+ ```
117
+
118
+ #### `particles.position`
119
+
120
+ Define the initial position of particles:
121
+
122
+ - `'uniform'`: particles will be uniformly distributed within the emitter's area as defined by its `position` (this is the center) and `size`
123
+ - `'normal'`: particles will be normally distributed within the emitter's area, i.e. more particles will be generated near the center of the emitter and fewer towards the edges
124
+ - `(n: number) => vec2`: a function that returns a position for each particle, where `n` is the index of the particle being emitted (maybe useful if we're emitting multiple particles in a single frame)
125
+
126
+ #### `particles.speed`
127
+
128
+ Define the initial speed of particles:
129
+
130
+ - `number`: a constant speed for all particles
131
+ - `{ min: number, max: number }`: a range of speeds for particles, each particle will have a random speed within this range
132
+ - `(n: number) => number`: a function that returns a speed for each particle, where `n` is the index of the particle being emitted
133
+
134
+ #### `particles.direction`
135
+
136
+ Define the initial direction of particles in radians (0 is right):
137
+
138
+ - `number`: a constant direction for all particles
139
+ - `{ min: number, max: number }`: a range of directions for particles, each particle will have a random direction within this range
140
+ - `(n: number) => number`: a function that returns a direction for each particle, where `n` is the index of the particle being emitted
141
+
142
+ #### `particles.size`
143
+
144
+ Define the initial size of particles:
145
+
146
+ - `{ x: number, y: number }`: a constant size for all particles
147
+ - `{ min: { x, y }, max: { x, y } }`: a range of sizes for particles, each particle will have a random size within this range
148
+ - `(n: number) => { x: number, y: number }`: a function that returns a size for each particle, where `n` is the index of the particle being emitted
149
+
150
+ #### `particles.rotation`
151
+
152
+ Define the initial rotation of particles in radians (0 is right):
153
+
154
+ - `null`: rotation will be calculated based on the particle's velocity
155
+ - `number`: a constant rotation for all particles
156
+ - `{ min: number, max: number }`: a range of rotations for particles, each particle will have a random rotation within this range
157
+ - `(n: number) => number`: a function that returns a rotation for each particle, where `n` is the index of the particle being emitted
158
+
159
+ #### `particles.lifespan`
160
+
161
+ Define the lifespan of particles in seconds:
162
+
163
+ - `number`: a constant lifespan for all particles
164
+ - `{ min: number, max: number }`: a range of lifespans for particles, each particle will have a random lifespan within this range
165
+ - `(n: number) => number`: a function that returns a lifespan for each particle, where `n` is the index of the particle being emitted
166
+
167
+ #### `particles.style`
168
+
169
+ There are a few default "built-in" styles:
170
+
171
+ ```ts
172
+ {
173
+ // the size of the dot will be max(size.x, size.y)
174
+ style: 'dot';
175
+ color: Color | string | Color[] | string[]; // fixed or random color
176
+ glow?: {
177
+ color: Color | string | Color[] | string[];
178
+ amount: number;
179
+ };
180
+ fade?: {
181
+ in: number; // fade in duration in seconds
182
+ out: number; // fade out duration in seconds
183
+ };
184
+ }
185
+ ```
186
+
187
+ ```ts
188
+ {
189
+ // the size of the radial gradient will be max(size.x, size.y)
190
+ style: 'radial';
191
+ color: Color | string | Color[] | string[]; // fixed or random color
192
+ fade?: {
193
+ in: number; // fade in duration in seconds
194
+ out: number; // fade out duration in seconds
195
+ };
196
+ }
197
+ ```
198
+
199
+ ```ts
200
+ {
201
+ // the length of the line will be size.x, and the width will be size.y
202
+ style: 'line';
203
+ color: Color | string | Color[] | string[];
204
+ rotationOffset?: number; // how much to offset the particle's rotation in radians
205
+ glow?: {
206
+ color: Color | string | Color[] | string[];
207
+ amount: number;
208
+ };
209
+ fade?: {
210
+ in: number; // fade in duration in seconds
211
+ out: number; // fade out duration in seconds
212
+ };
213
+ }
214
+ ```
215
+
216
+ ```ts
217
+ {
218
+ // the size of the image is defined by size.x and size.y
219
+ style: 'image';
220
+ image: HTMLImageElement; // the image to render
221
+ rotationOffset?: number; // how much to offset the particle's rotation in radians
222
+ }
223
+ ```
224
+
225
+ #### `particles.options`
226
+
227
+ ```ts
228
+ {
229
+ useAttractors: boolean; // whether particles from this emitter should be affected by attractors
230
+ useForceFields: boolean; // whether particles from this emitter should be affected by force fields
231
+ useColliders: boolean; // whether particles from this emitter should be affected by colliders
232
+
233
+ defaultUpdates: 'none' | 'all' | ParticleDefaultUpdateTypes;
234
+ update?: (system: ParticleSystem, dt: number) => void;
235
+
236
+ defaultDraws: 'none' | 'all' | ParticleDefaultDrawTypes;
237
+ preDraw?: (system: ParticleSystem, context: CanvasRenderingContext2D) => void;
238
+ postDraw?: (system: ParticleSystem, context: CanvasRenderingContext2D) => void;
239
+ }
240
+ ```
241
+
242
+ Default update types:
243
+ - `age`: update the age of particles and handle disposal
244
+ - `physics`: update the velocity of particles based on forces, collisions, etc.
245
+ - `direction`: update the direction of particles based on their velocity
246
+ - `position`: integrate the position of particles based on their velocity
247
+
248
+ Default draw types:
249
+ - `transforms`: apply transformations like translation and rotation
250
+ - `fade`: apply fade in/out effects to particles
251
+ - `styles`: render the particle's style (dot, radial, line, image)
252
+
253
+ #### `emission`
254
+
255
+ Various particle emission types are available:
256
+
257
+ ```ts
258
+ {
259
+ // emit some number of particles per second
260
+ type: 'rate';
261
+ rate: number | { min: number, max: number };
262
+ }
263
+ ```
264
+
265
+ If rate is a random range, the rate will be updated every second.
266
+
267
+ ```ts
268
+ {
269
+ // emit some number of particles immediately and then automatically dispose the emitter
270
+ type: 'burst';
271
+ n: number | { min: number, max: number };
272
+ delay?: number; // optional delay in seconds before emitting
273
+ }
274
+ ```
275
+
276
+ ```ts
277
+ {
278
+ // custom function, this will be called every frame and should return the number of particles to emit
279
+ type: 'custom';
280
+ f: () => number;
281
+ }
282
+ ```
283
+
284
+ ## Attractors
285
+
286
+ Attractors can attract or repel particles in range of the attractor.
287
+
288
+ ```ts
289
+ new Attractor(
290
+ position: vec2, // { x: number, y: number }
291
+ range: number,
292
+ force: number, // negative for repulsion, positive for attraction
293
+ falloff: number,
294
+ lifespan: number // use -1 for infinite lifespan
295
+ );
296
+ ```
297
+
298
+ ## Force Fields
299
+
300
+ Force fields apply a force to all particles.
301
+
302
+ ```ts
303
+ new ForceField(
304
+ force: vec2, // { x: number, y: number }
305
+ lifespan: number // use -1 for infinite lifespan
306
+ );
307
+ ```
308
+
309
+ ## Colliders
310
+
311
+ Colliders are used for detecting and handling collisions with particles.
312
+
313
+ ```ts
314
+ new Collider(
315
+ geometry, // see below...
316
+ restitution: number, // how bouncy the collider is, 0 is no bounce, 1 is full bounce
317
+ friction: number, // how much friction the collider has, 0 is no friction, 1 is full friction
318
+ );
319
+ ```
320
+
321
+ ### Collider Geometry
322
+
323
+ Collider geometry can be defined in various ways:
324
+
325
+ ```ts
326
+ {
327
+ type: 'circle';
328
+ position: vec2; // { x: number, y: number }
329
+ radius: number;
330
+ }
331
+ ```
332
+
333
+ ```ts
334
+ {
335
+ type: 'rectangle';
336
+ position: vec2; // { x: number, y: number }
337
+ size: vec2; // { x: number, y: number }
338
+ rotation?: number; // optional rotation in radians
339
+ }
340
+ ```
341
+
342
+ ```ts
343
+ {
344
+ type: 'polygon';
345
+ vertices: vec2[]; // array of vertices defining the polygon
346
+ }
347
+ ```