@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 +5 -0
- package/.vscode/settings.json +17 -0
- package/LICENSE +7 -0
- package/README.md +347 -0
- package/build/index.d.ts +357 -0
- package/build/index.js +129 -0
- package/package.json +32 -0
package/.prettierrc
ADDED
|
@@ -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
|
+
```
|