littlejsengine 1.17.1 → 1.17.10
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/LICENSE +10 -10
- package/dist/littlejs.d.ts +254 -171
- package/dist/littlejs.esm.js +892 -628
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +889 -626
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +885 -622
- package/examples/box2d/gameObjects.js +3 -3
- package/examples/electron/index.html +2 -2
- package/examples/index.html +10 -9
- package/examples/logo.png +0 -0
- package/examples/logo2.png +0 -0
- package/examples/module/build.bat +7 -0
- package/examples/module/build.js +121 -0
- package/examples/particles/index.html +1 -1
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameEffects.js +5 -10
- package/examples/platformer/gameLevel.js +3 -3
- package/examples/puzzle/game.js +2 -2
- package/examples/shorts/base.html +4 -1
- package/examples/shorts/box2dPool.js +2 -2
- package/examples/shorts/box2dTileLayer.js +48 -0
- package/examples/shorts/colors.js +1 -1
- package/examples/shorts/fps.js +1 -1
- package/examples/shorts/music.js +5 -4
- package/examples/shorts/musicPlayer.js +4 -3
- package/examples/shorts/parallax.js +2 -2
- package/examples/shorts/tiltedView.js +1 -1
- package/examples/starter/index.html +2 -2
- package/package.json +5 -5
- package/plugins/box2d.js +166 -63
- package/plugins/pluginExport.js +1 -1
- package/plugins/postProcess.js +1 -1
- package/plugins/uiSystem.js +33 -20
- package/plugins/zzfxm.js +2 -2
- package/reference.md +9 -8
- package/src/engine.js +111 -134
- package/src/engineAudio.js +44 -58
- package/src/engineDebug.js +4 -4
- package/src/engineDraw.js +7 -4
- package/src/engineExport.js +2 -1
- package/src/engineInput.js +5 -5
- package/src/engineMath.js +9 -9
- package/src/engineObject.js +25 -25
- package/src/engineParticles.js +241 -118
- package/src/engineTileLayer.js +117 -88
- package/src/engineUtilities.js +105 -78
- package/src/engineWebGL.js +2 -2
- package/plugins/Box2D_License.txt +0 -17
package/src/engineParticles.js
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LittleJS Particle System
|
|
3
|
+
* - A simple but fast and flexible particle system
|
|
4
|
+
* - Lightweight Particles are created and managed by ParticleEmitters
|
|
5
|
+
* - The particle design tool can be used to help create emitters
|
|
6
|
+
* @namespace Particles
|
|
3
7
|
*/
|
|
4
8
|
|
|
5
9
|
'use strict';
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
|
-
* @callback
|
|
12
|
+
* @callback ParticleCallback - Function that processes a particle
|
|
9
13
|
* @param {Particle} particle
|
|
10
|
-
* @memberof
|
|
14
|
+
* @memberof Particles
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @callback ParticleCollideCallback - Collide callback for particles
|
|
19
|
+
* @param {Particle} particle
|
|
20
|
+
* @param {number} tileData
|
|
21
|
+
* @param {Vector2} pos
|
|
22
|
+
* @memberof Particles
|
|
11
23
|
*/
|
|
12
24
|
|
|
13
25
|
/**
|
|
14
26
|
* Particle Emitter - Spawns particles with the given settings
|
|
15
27
|
* @extends EngineObject
|
|
16
|
-
* @memberof
|
|
28
|
+
* @memberof Particles
|
|
17
29
|
* @example
|
|
18
30
|
* // create a particle emitter
|
|
19
31
|
* let pos = vec2(2,3);
|
|
@@ -31,7 +43,7 @@
|
|
|
31
43
|
class ParticleEmitter extends EngineObject
|
|
32
44
|
{
|
|
33
45
|
/** Create a particle system with the given settings
|
|
34
|
-
* @param {Vector2}
|
|
46
|
+
* @param {Vector2} pos - World space position of the emitter
|
|
35
47
|
* @param {number} [angle] - Angle to emit the particles
|
|
36
48
|
* @param {number|Vector2} [emitSize] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
37
49
|
* @param {number} [emitTime] - How long to stay alive (0 is forever)
|
|
@@ -61,7 +73,7 @@ class ParticleEmitter extends EngineObject
|
|
|
61
73
|
*/
|
|
62
74
|
constructor
|
|
63
75
|
(
|
|
64
|
-
|
|
76
|
+
pos,
|
|
65
77
|
angle,
|
|
66
78
|
emitSize = 0,
|
|
67
79
|
emitTime = 0,
|
|
@@ -90,12 +102,13 @@ class ParticleEmitter extends EngineObject
|
|
|
90
102
|
localSpace = false
|
|
91
103
|
)
|
|
92
104
|
{
|
|
93
|
-
super(
|
|
105
|
+
super(pos, vec2(), tileInfo, angle, undefined, renderOrder);
|
|
94
106
|
|
|
95
107
|
// emitter settings
|
|
108
|
+
/** @property {boolean} - Should particles be emitted in a circle */
|
|
109
|
+
this.emitCircle = typeof emitSize === 'number';
|
|
96
110
|
/** @property {number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
97
|
-
this.emitSize = emitSize
|
|
98
|
-
emitSize.copy() : emitSize;
|
|
111
|
+
this.emitSize = typeof emitSize === 'number' ? vec2(emitSize) : emitSize.copy();
|
|
99
112
|
/** @property {number} - How long to stay alive (0 is forever) */
|
|
100
113
|
this.emitTime = emitTime;
|
|
101
114
|
/** @property {number} - How many particles per second to spawn, does not emit if 0 */
|
|
@@ -146,26 +159,31 @@ class ParticleEmitter extends EngineObject
|
|
|
146
159
|
this.localSpace = localSpace;
|
|
147
160
|
/** @property {number} - If non zero the particle is drawn as a trail, stretched in the direction of velocity */
|
|
148
161
|
this.trailScale = 0;
|
|
149
|
-
/** @property {
|
|
150
|
-
this.particleDestroyCallback = undefined;
|
|
151
|
-
/** @property {ParticleCallbackFunction} - Callback when particle is created */
|
|
162
|
+
/** @property {ParticleCallback} - Callback when particle is created */
|
|
152
163
|
this.particleCreateCallback = undefined;
|
|
153
|
-
/** @property {
|
|
154
|
-
this.
|
|
164
|
+
/** @property {ParticleCallback} - Callback when particle is destroyed */
|
|
165
|
+
this.particleDestroyCallback = undefined;
|
|
166
|
+
/** @property {ParticleCollideCallback} - Callback when particle collides */
|
|
167
|
+
this.particleCollideCallback = undefined;
|
|
155
168
|
/** @property {number} - Percentage of velocity to pass to particles (0-1) */
|
|
156
169
|
this.velocityInheritance = 0;
|
|
170
|
+
/** @property {number} - Track particle emit time */
|
|
171
|
+
this.emitTimeBuffer = 0;
|
|
172
|
+
/** @property {Array<Particle>} - Array of particles for this emitter */
|
|
173
|
+
this.particles = [];
|
|
157
174
|
|
|
158
175
|
// track previous position and angle
|
|
159
176
|
this.previousAngle = this.angle;
|
|
160
177
|
this.previousPos = this.pos.copy();
|
|
161
178
|
}
|
|
162
179
|
|
|
163
|
-
/** Emitters do not have physics */
|
|
164
|
-
updatePhysics() {}
|
|
165
|
-
|
|
166
180
|
/** Update the emitter to spawn particles, called automatically by engine once each frame */
|
|
167
181
|
update()
|
|
168
182
|
{
|
|
183
|
+
// physics sanity checks
|
|
184
|
+
ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
|
|
185
|
+
ASSERT(this.damping >= 0 && this.damping <= 1);
|
|
186
|
+
|
|
169
187
|
if (this.velocityInheritance)
|
|
170
188
|
{
|
|
171
189
|
// pass emitter velocity to particles
|
|
@@ -179,7 +197,7 @@ class ParticleEmitter extends EngineObject
|
|
|
179
197
|
}
|
|
180
198
|
|
|
181
199
|
// update emitter
|
|
182
|
-
if (
|
|
200
|
+
if (this.isActive())
|
|
183
201
|
{
|
|
184
202
|
// emit particles
|
|
185
203
|
if (this.emitRate && particleEmitRateScale)
|
|
@@ -189,14 +207,21 @@ class ParticleEmitter extends EngineObject
|
|
|
189
207
|
this.emitParticle();
|
|
190
208
|
}
|
|
191
209
|
}
|
|
192
|
-
else
|
|
193
|
-
this.destroy();
|
|
210
|
+
else if (this.particles.length === 0)
|
|
211
|
+
this.destroy(true);
|
|
212
|
+
|
|
213
|
+
// update and remove destroyed particles
|
|
214
|
+
this.particles = this.particles.filter((p)=>
|
|
215
|
+
{
|
|
216
|
+
p.update();
|
|
217
|
+
return !p.destroyed;
|
|
218
|
+
});
|
|
194
219
|
|
|
195
220
|
if (debugParticles)
|
|
196
221
|
{
|
|
197
222
|
// show emitter bounds
|
|
198
|
-
if (
|
|
199
|
-
debugCircle(this.pos, this.emitSize/2, '#0f0');
|
|
223
|
+
if (this.emitCircle)
|
|
224
|
+
debugCircle(this.pos, this.emitSize.x/2, '#0f0');
|
|
200
225
|
else
|
|
201
226
|
debugRect(this.pos, this.emitSize, '#0f0', 0, this.angle);
|
|
202
227
|
}
|
|
@@ -207,14 +232,15 @@ class ParticleEmitter extends EngineObject
|
|
|
207
232
|
emitParticle()
|
|
208
233
|
{
|
|
209
234
|
// spawn a particle
|
|
210
|
-
let pos =
|
|
211
|
-
randInCircle(this.emitSize/2)
|
|
212
|
-
: vec2(rand(-.5,.5), rand(-.5,.5))
|
|
235
|
+
let pos = this.emitCircle ? // check if circle emitter
|
|
236
|
+
randInCircle(this.emitSize.x/2) // circle emitter
|
|
237
|
+
: vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
|
|
213
238
|
.multiply(this.emitSize).rotate(this.angle)
|
|
214
239
|
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
215
240
|
if (!this.localSpace)
|
|
216
241
|
{
|
|
217
|
-
pos
|
|
242
|
+
pos.x += this.pos.x;
|
|
243
|
+
pos.y += this.pos.y;
|
|
218
244
|
angle += this.angle;
|
|
219
245
|
}
|
|
220
246
|
|
|
@@ -234,25 +260,17 @@ class ParticleEmitter extends EngineObject
|
|
|
234
260
|
const velocityAngle = this.localSpace ? coneAngle : this.angle + coneAngle;
|
|
235
261
|
|
|
236
262
|
// build particle
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
particle.angleVelocity = angleSpeed;
|
|
263
|
+
const velocity = vec2(speed*sin(velocityAngle), speed*cos(velocityAngle));
|
|
264
|
+
let angleVelocity = angleSpeed;
|
|
240
265
|
if (!this.localSpace && this.velocityInheritance > 0)
|
|
241
266
|
{
|
|
242
267
|
// apply emitter velocity to particle
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
268
|
+
velocity.x += this.velocity.x;
|
|
269
|
+
velocity.y += this.velocity.y;
|
|
270
|
+
angleVelocity += this.angleVelocity;
|
|
246
271
|
}
|
|
247
|
-
particle
|
|
248
|
-
|
|
249
|
-
particle.angleDamping = this.angleDamping;
|
|
250
|
-
particle.restitution = this.restitution;
|
|
251
|
-
particle.friction = this.friction;
|
|
252
|
-
particle.gravityScale = this.gravityScale;
|
|
253
|
-
particle.collideTiles = this.collideTiles;
|
|
254
|
-
particle.renderOrder = this.renderOrder;
|
|
255
|
-
particle.mirror = randBool();
|
|
272
|
+
const particle = new Particle(this, pos, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, velocity, angleVelocity);
|
|
273
|
+
this.particles.push(particle);
|
|
256
274
|
|
|
257
275
|
// call particle create callback
|
|
258
276
|
this.particleCreateCallback?.(particle);
|
|
@@ -261,140 +279,245 @@ class ParticleEmitter extends EngineObject
|
|
|
261
279
|
return particle;
|
|
262
280
|
}
|
|
263
281
|
|
|
264
|
-
|
|
265
|
-
|
|
282
|
+
/** Particle emitters do not have physics */
|
|
283
|
+
updatePhysics() {}
|
|
284
|
+
|
|
285
|
+
/** Render all particles for this emitter */
|
|
286
|
+
render()
|
|
287
|
+
{
|
|
288
|
+
// render all particles
|
|
289
|
+
for (const particle of this.particles)
|
|
290
|
+
particle.render();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** is emitter actively spawning */
|
|
294
|
+
isActive() { return !this.emitTime || this.getAliveTime() < this.emitTime; }
|
|
295
|
+
|
|
296
|
+
/** Destroy the particle emitter
|
|
297
|
+
* @param {boolean} [immediate] - should particle emitters and other attached effects be allowed to die off */
|
|
298
|
+
destroy(immediate=false)
|
|
299
|
+
{
|
|
300
|
+
if (this.destroyed) return;
|
|
301
|
+
|
|
302
|
+
super.destroy(immediate);
|
|
303
|
+
if (!immediate && this.particles.length > 0)
|
|
304
|
+
{
|
|
305
|
+
// wait for particles to die off
|
|
306
|
+
this.destroyed = false;
|
|
307
|
+
this.emitTime = -1;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
266
310
|
}
|
|
267
311
|
|
|
268
312
|
///////////////////////////////////////////////////////////////////////////////
|
|
269
313
|
/**
|
|
270
314
|
* Particle Object - Created automatically by Particle Emitters
|
|
271
|
-
* @
|
|
272
|
-
* @memberof Engine
|
|
315
|
+
* @memberof Particles
|
|
273
316
|
*/
|
|
274
|
-
class Particle
|
|
317
|
+
class Particle
|
|
275
318
|
{
|
|
276
319
|
/**
|
|
277
320
|
* Create a particle with the passed in settings
|
|
278
321
|
* Typically this is created automatically by a ParticleEmitter
|
|
279
|
-
* @param {
|
|
280
|
-
* @param {
|
|
281
|
-
* @param {number}
|
|
282
|
-
* @param {Color}
|
|
283
|
-
* @param {Color}
|
|
284
|
-
* @param {number}
|
|
285
|
-
* @param {number}
|
|
286
|
-
* @param {number}
|
|
287
|
-
* @param {
|
|
288
|
-
* @param {
|
|
289
|
-
* @param {number} trailScale - If a trail, how long to make it
|
|
290
|
-
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
291
|
-
* @param {ParticleCallbackFunction} [destroyCallback] - Callback when particle dies
|
|
322
|
+
* @param {ParticleEmitter} emitter - The emitter that created this particle
|
|
323
|
+
* @param {Vector2} pos - World or local space position
|
|
324
|
+
* @param {number} angle - Angle of the particle
|
|
325
|
+
* @param {Color} colorStart - Color at start of life
|
|
326
|
+
* @param {Color} colorEnd - Color at end of life
|
|
327
|
+
* @param {number} lifeTime - How long to live for
|
|
328
|
+
* @param {number} sizeStart - Size at start of life
|
|
329
|
+
* @param {number} sizeEnd - Size at end of life
|
|
330
|
+
* @param {Vector2} [velocity] - Velocity of the particle
|
|
331
|
+
* @param {number} [angleVelocity] - Angular speed of the particle
|
|
292
332
|
*/
|
|
293
|
-
constructor(
|
|
294
|
-
)
|
|
333
|
+
constructor(emitter, pos, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, velocity = vec2(), angleVelocity = 0)
|
|
295
334
|
{
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
/** @property {
|
|
335
|
+
/** @property {ParticleEmitter} */
|
|
336
|
+
this.emitter = emitter;
|
|
337
|
+
/** @property {Vector2} */
|
|
338
|
+
this.pos = pos;
|
|
339
|
+
/** @property {number} */
|
|
340
|
+
this.angle = angle;
|
|
341
|
+
/** @property {Vector2} */
|
|
342
|
+
this.size = vec2(sizeStart);
|
|
343
|
+
/** @property {Color} */
|
|
344
|
+
this.color = colorStart.copy();
|
|
345
|
+
/** @property {Color} */
|
|
299
346
|
this.colorStart = colorStart;
|
|
300
|
-
/** @property {Color}
|
|
347
|
+
/** @property {Color} */
|
|
301
348
|
this.colorEnd = colorEnd;
|
|
302
|
-
/** @property {number}
|
|
349
|
+
/** @property {number} */
|
|
303
350
|
this.lifeTime = lifeTime;
|
|
304
|
-
/** @property {number}
|
|
351
|
+
/** @property {number} */
|
|
305
352
|
this.sizeStart = sizeStart;
|
|
306
|
-
/** @property {number}
|
|
353
|
+
/** @property {number} */
|
|
307
354
|
this.sizeEnd = sizeEnd;
|
|
308
|
-
/** @property {
|
|
309
|
-
this.
|
|
310
|
-
/** @property {
|
|
311
|
-
this.
|
|
312
|
-
/** @property {number}
|
|
313
|
-
this.
|
|
314
|
-
/** @property {
|
|
315
|
-
this.
|
|
316
|
-
/** @property {
|
|
317
|
-
this.
|
|
318
|
-
|
|
319
|
-
this.
|
|
355
|
+
/** @property {Vector2} */
|
|
356
|
+
this.velocity = velocity;
|
|
357
|
+
/** @property {number} */
|
|
358
|
+
this.angleVelocity = angleVelocity;
|
|
359
|
+
/** @property {number} */
|
|
360
|
+
this.spawnTime = time;
|
|
361
|
+
/** @property {boolean} */
|
|
362
|
+
this.mirror = randBool();
|
|
363
|
+
/** @property {EngineObject} */
|
|
364
|
+
this.groundObject = undefined;
|
|
365
|
+
/** @property {boolean} */
|
|
366
|
+
this.destroyed = false;
|
|
367
|
+
/** @property {TileInfo} */
|
|
368
|
+
this.tileInfo = emitter.tileInfo;
|
|
320
369
|
}
|
|
321
370
|
|
|
322
|
-
/** Update the
|
|
371
|
+
/** Update the particle */
|
|
323
372
|
update()
|
|
324
373
|
{
|
|
325
|
-
|
|
374
|
+
// emitter properties
|
|
375
|
+
const emitter = this.emitter;
|
|
376
|
+
const damping = emitter.damping;
|
|
377
|
+
const angleDamping = emitter.angleDamping;
|
|
378
|
+
const restitution = emitter.restitution;
|
|
379
|
+
const friction = emitter.friction;
|
|
380
|
+
const gravityScale = emitter.gravityScale;
|
|
381
|
+
const collideTiles = emitter.collideTiles;
|
|
382
|
+
const collideCallback = emitter.particleCollideCallback;
|
|
383
|
+
|
|
384
|
+
// destroy particle when its time runs out
|
|
385
|
+
if (this.lifeTime > 0 && time - this.spawnTime > this.lifeTime)
|
|
326
386
|
{
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if (length2 > objectMaxSpeed*objectMaxSpeed)
|
|
330
|
-
{
|
|
331
|
-
const s = objectMaxSpeed / length2**.5;
|
|
332
|
-
this.velocity.x *= s;
|
|
333
|
-
this.velocity.y *= s;
|
|
334
|
-
}
|
|
387
|
+
this.destroy();
|
|
388
|
+
return;
|
|
335
389
|
}
|
|
336
390
|
|
|
337
|
-
|
|
391
|
+
// apply physics
|
|
392
|
+
const oldPos = this.pos.copy();
|
|
393
|
+
this.velocity.x *= damping;
|
|
394
|
+
this.velocity.y *= damping;
|
|
395
|
+
this.pos.x += this.velocity.x += gravity.x * gravityScale;
|
|
396
|
+
this.pos.y += this.velocity.y += gravity.y * gravityScale;
|
|
397
|
+
this.angle += this.angleVelocity *= angleDamping;
|
|
398
|
+
|
|
399
|
+
// don't do collision if solver disabled
|
|
400
|
+
if (!enablePhysicsSolver || !collideTiles) return;
|
|
401
|
+
|
|
402
|
+
// apply max circular speed to prevent going through collision
|
|
403
|
+
const length2 = this.velocity.lengthSquared();
|
|
404
|
+
if (length2 > objectMaxSpeed*objectMaxSpeed)
|
|
405
|
+
{
|
|
406
|
+
const s = objectMaxSpeed / length2**.5;
|
|
407
|
+
this.velocity.x *= s;
|
|
408
|
+
this.velocity.y *= s;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// check collision against tiles
|
|
412
|
+
this.groundObject = undefined;
|
|
413
|
+
const testCollision = collideCallback ? (pos)=>
|
|
414
|
+
{
|
|
415
|
+
const data = tileCollisionGetData(pos);
|
|
416
|
+
return data && collideCallback(this, data, pos);
|
|
417
|
+
} : (pos)=> tileCollisionGetData(pos) > 0;
|
|
418
|
+
|
|
419
|
+
if (testCollision(this.pos))
|
|
338
420
|
{
|
|
339
|
-
//
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
421
|
+
// if already was stuck in collision, don't do anything
|
|
422
|
+
const hitLayer = tileCollisionTest(this.pos);
|
|
423
|
+
if (!testCollision(oldPos))
|
|
424
|
+
{
|
|
425
|
+
if (!collideCallback || collideCallback?.(this, hitLayer))
|
|
426
|
+
{
|
|
427
|
+
// test which side we bounced off (or both if a corner)
|
|
428
|
+
const isBlockedX = testCollision(vec2(this.pos.x, oldPos.y));
|
|
429
|
+
const isBlockedY = testCollision(vec2(oldPos.x, this.pos.y));
|
|
430
|
+
const hitRestitution = max(restitution, hitLayer.restitution);
|
|
431
|
+
const hitFriction = max(friction, hitLayer.friction);
|
|
432
|
+
if (isBlockedX)
|
|
433
|
+
{
|
|
434
|
+
// move to previous X position and bounce
|
|
435
|
+
this.pos.x = oldPos.x;
|
|
436
|
+
this.velocity.x *= -hitRestitution;
|
|
437
|
+
this.velocity.y *= hitFriction;
|
|
438
|
+
}
|
|
439
|
+
if (isBlockedY || !isBlockedX)
|
|
440
|
+
{
|
|
441
|
+
const wasFalling = this.velocity.y < 0 && gravity.y < 0 || this.velocity.y > 0 && gravity.y > 0;
|
|
442
|
+
if (wasFalling)
|
|
443
|
+
this.groundObject = hitLayer;
|
|
444
|
+
|
|
445
|
+
// move to previous Y position and bounce
|
|
446
|
+
this.pos.y = oldPos.y;
|
|
447
|
+
this.velocity.y *= -hitRestitution;
|
|
448
|
+
this.velocity.x *= hitFriction;
|
|
449
|
+
}
|
|
450
|
+
debugPhysics && debugRect(this.pos, this.size, '#f00');
|
|
451
|
+
}
|
|
452
|
+
}
|
|
345
453
|
}
|
|
346
454
|
}
|
|
347
455
|
|
|
348
|
-
/**
|
|
456
|
+
/** Destroy this particle */
|
|
457
|
+
destroy()
|
|
458
|
+
{
|
|
459
|
+
const destroyCallback = this.emitter.particleDestroyCallback;
|
|
460
|
+
const c = this.colorEnd;
|
|
461
|
+
this.color.set(c.r, c.g, c.b, c.a);
|
|
462
|
+
this.size.set(this.sizeEnd, this.sizeEnd);
|
|
463
|
+
this.destroyed = true;
|
|
464
|
+
destroyCallback?.(this);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** Render the particle, automatically called each frame */
|
|
349
468
|
render()
|
|
350
469
|
{
|
|
470
|
+
// emitter properties
|
|
471
|
+
const emitter = this.emitter;
|
|
472
|
+
const localSpace = emitter.localSpace;
|
|
473
|
+
const additive = emitter.additive;
|
|
474
|
+
const trailScale = emitter.trailScale;
|
|
475
|
+
const fadeRate = emitter.fadeRate / 2;
|
|
476
|
+
|
|
351
477
|
// lerp color and size
|
|
352
478
|
const p1 = this.lifeTime > 0 ? min((time - this.spawnTime) / this.lifeTime, 1) : 1, p2 = 1-p1;
|
|
353
479
|
const radius = p2 * this.sizeStart + p1 * this.sizeEnd;
|
|
354
480
|
const size = vec2(radius);
|
|
481
|
+
const alphaFade = p1 < fadeRate ? p1/fadeRate :
|
|
482
|
+
p1 > 1-fadeRate ? (1-p1)/fadeRate : 1;
|
|
355
483
|
this.color.r = p2 * this.colorStart.r + p1 * this.colorEnd.r;
|
|
356
484
|
this.color.g = p2 * this.colorStart.g + p1 * this.colorEnd.g;
|
|
357
485
|
this.color.b = p2 * this.colorStart.b + p1 * this.colorEnd.b;
|
|
358
|
-
this.color.a = p2 * this.colorStart.a + p1 * this.colorEnd.a;
|
|
359
|
-
|
|
360
|
-
// fade alpha
|
|
361
|
-
const fadeRate = this.fadeRate/2;
|
|
362
|
-
this.color.a *= p1 < fadeRate ? p1/fadeRate :
|
|
363
|
-
p1 > 1-fadeRate ? (1-p1)/fadeRate : 1;
|
|
486
|
+
this.color.a = (p2 * this.colorStart.a + p1 * this.colorEnd.a) * alphaFade;
|
|
364
487
|
|
|
365
488
|
// draw the particle
|
|
366
|
-
|
|
489
|
+
additive && setBlendMode(true);
|
|
367
490
|
|
|
368
491
|
// update the position and angle for drawing
|
|
369
|
-
|
|
370
|
-
|
|
492
|
+
const pos = this.pos.copy();
|
|
493
|
+
let angle = this.angle;
|
|
494
|
+
if (localSpace)
|
|
371
495
|
{
|
|
372
496
|
// in local space of emitter
|
|
373
|
-
const a =
|
|
497
|
+
const a = emitter.angle;
|
|
374
498
|
const c = cos(a), s = sin(a);
|
|
375
|
-
pos
|
|
376
|
-
|
|
377
|
-
angle +=
|
|
499
|
+
pos.set(emitter.pos.x + pos.x*c - pos.y*s,
|
|
500
|
+
emitter.pos.y + pos.x*s + pos.y*c);
|
|
501
|
+
angle += a;
|
|
378
502
|
}
|
|
379
|
-
if (
|
|
503
|
+
if (trailScale)
|
|
380
504
|
{
|
|
381
505
|
// trail style particles
|
|
382
|
-
const
|
|
383
|
-
this.velocity.rotate(-
|
|
384
|
-
|
|
385
|
-
const speed = direction.length();
|
|
506
|
+
const velocity = localSpace ?
|
|
507
|
+
this.velocity.rotate(-emitter.angle) : this.velocity;
|
|
508
|
+
const speed = velocity.length();
|
|
386
509
|
if (speed)
|
|
387
510
|
{
|
|
388
511
|
// stretch in direction of motion
|
|
389
|
-
const trailLength = speed *
|
|
512
|
+
const trailLength = speed * trailScale;
|
|
390
513
|
size.y = max(size.x, trailLength);
|
|
391
|
-
angle = atan2(
|
|
514
|
+
angle = atan2(velocity.x, velocity.y);
|
|
392
515
|
drawTile(pos, size, this.tileInfo, this.color, angle, this.mirror);
|
|
393
516
|
}
|
|
394
517
|
}
|
|
395
518
|
else
|
|
396
519
|
drawTile(pos, size, this.tileInfo, this.color, angle, this.mirror);
|
|
397
|
-
|
|
520
|
+
additive && setBlendMode();
|
|
398
521
|
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
399
522
|
}
|
|
400
523
|
}
|