littlejsengine 1.12.6 → 1.13.4

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.
Files changed (102) hide show
  1. package/README.md +15 -12
  2. package/dist/littlejs.d.ts +466 -236
  3. package/dist/littlejs.esm.js +6126 -5370
  4. package/dist/littlejs.esm.min.js +4 -1
  5. package/dist/littlejs.js +6093 -5359
  6. package/dist/littlejs.min.js +4 -1
  7. package/dist/littlejs.release.js +5614 -4907
  8. package/examples/box2d/game.js +2 -2
  9. package/examples/box2d/gameObjects.js +8 -7
  10. package/examples/box2d/index.html +1 -1
  11. package/examples/box2d/scenes.js +8 -8
  12. package/examples/breakout/game.js +1 -1
  13. package/examples/breakout/gameObjects.js +2 -2
  14. package/examples/breakout/index.html +1 -1
  15. package/examples/breakoutTutorial/README.md +46 -43
  16. package/examples/breakoutTutorial/game.js +3 -3
  17. package/examples/breakoutTutorial/index.html +1 -1
  18. package/examples/electron/build.bat +7 -0
  19. package/examples/electron/build.js +124 -0
  20. package/examples/electron/electron.js +35 -0
  21. package/examples/electron/game.js +140 -0
  22. package/examples/electron/index.html +13 -0
  23. package/examples/electron/package.json +12 -0
  24. package/examples/electron/tiles.png +0 -0
  25. package/examples/empty/game.js +1 -0
  26. package/examples/empty/index.html +1 -1
  27. package/examples/htmlMenu/game.js +1 -1
  28. package/examples/htmlMenu/index.html +1 -1
  29. package/examples/index.html +486 -182
  30. package/examples/module/game.js +6 -3
  31. package/examples/module/index.html +1 -1
  32. package/examples/particles/index.html +12 -3
  33. package/examples/platformer/game.js +4 -4
  34. package/examples/platformer/gameCharacter.js +6 -6
  35. package/examples/platformer/gameEffects.js +74 -57
  36. package/examples/platformer/gameLevel.js +61 -70
  37. package/examples/platformer/gameObjects.js +4 -4
  38. package/examples/platformer/index.html +1 -1
  39. package/examples/puzzle/index.html +1 -1
  40. package/examples/shorts/base.html +23 -3
  41. package/examples/shorts/blending.js +9 -5
  42. package/examples/shorts/box2d.js +1 -1
  43. package/examples/shorts/box2dCar.js +9 -13
  44. package/examples/shorts/clock.js +1 -1
  45. package/examples/shorts/colors.js +2 -2
  46. package/examples/shorts/flappyGame.js +52 -0
  47. package/examples/shorts/helloWorld.js +6 -3
  48. package/examples/shorts/hillGlideGame.js +56 -0
  49. package/examples/shorts/landerGame.js +32 -0
  50. package/examples/shorts/maze.js +47 -0
  51. package/examples/shorts/medals.js +42 -0
  52. package/examples/shorts/nineSlice.js +21 -0
  53. package/examples/shorts/piano.js +52 -0
  54. package/examples/shorts/platformer.js +24 -20
  55. package/examples/shorts/{pong.js → pongGame.js} +1 -1
  56. package/examples/shorts/raycasting.js +71 -0
  57. package/examples/shorts/shapes.js +9 -9
  58. package/examples/shorts/slidingPuzzle.js +42 -0
  59. package/examples/shorts/spaceGame.js +56 -0
  60. package/examples/shorts/starfield.js +15 -0
  61. package/examples/shorts/systemFont.js +1 -1
  62. package/examples/shorts/tileLayer.js +3 -5
  63. package/examples/shorts/tiles.png +0 -0
  64. package/examples/shorts/tiltedView.js +8 -7
  65. package/examples/shorts/topDown.js +18 -26
  66. package/examples/shorts/uiSystem.js +5 -7
  67. package/examples/starter/build.bat +6 -1
  68. package/examples/starter/build.js +9 -8
  69. package/examples/starter/game.js +7 -4
  70. package/examples/starter/index.html +23 -13
  71. package/examples/stress/index.html +7 -8
  72. package/examples/style.css +139 -0
  73. package/examples/typescript/build.js +2 -3
  74. package/examples/typescript/game.js +4 -4
  75. package/examples/typescript/game.ts +6 -3
  76. package/examples/typescript/index.html +1 -1
  77. package/examples/uiSystem/game.js +10 -25
  78. package/examples/uiSystem/index.html +1 -1
  79. package/package.json +2 -2
  80. package/plugins/box2d.js +22 -97
  81. package/plugins/drawUtilities.js +132 -0
  82. package/plugins/newgrounds.js +1 -1
  83. package/plugins/pluginExport.js +7 -1
  84. package/plugins/postProcess.js +9 -2
  85. package/plugins/uiSystem.js +155 -67
  86. package/plugins/zzfxm.js +1 -1
  87. package/reference.md +10 -10
  88. package/src/engine.js +56 -30
  89. package/src/engineAudio.js +15 -6
  90. package/src/engineBuild.bat +6 -1
  91. package/src/engineBuild.js +20 -5
  92. package/src/engineDebug.js +55 -28
  93. package/src/engineDraw.js +321 -177
  94. package/src/engineExport.js +27 -9
  95. package/src/engineInput.js +110 -38
  96. package/src/engineMedals.js +4 -4
  97. package/src/engineObject.js +71 -70
  98. package/src/engineParticles.js +33 -9
  99. package/src/engineSettings.js +69 -23
  100. package/src/engineTileLayer.js +312 -153
  101. package/src/engineUtilities.js +187 -135
  102. package/src/engineWebGL.js +70 -39
@@ -41,9 +41,13 @@ class EngineObject
41
41
  */
42
42
  constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=new Color, renderOrder=0)
43
43
  {
44
- // set passed in params
45
- ASSERT(isVector2(pos) && isVector2(size), 'ensure pos and size are vec2s');
46
- ASSERT(typeof tileInfo !== 'number' || !tileInfo, 'old style tile setup');
44
+ // check passed in params
45
+ ASSERT(isVector2(pos) && pos.isValid(), 'object pos should be a vec2');
46
+ ASSERT(isVector2(size) && size.isValid(), 'object size should be a vec2');
47
+ ASSERT(!tileInfo || tileInfo instanceof TileInfo, 'object tileInfo should be a TileInfo or undefined');
48
+ ASSERT(typeof angle == 'number' && isFinite(angle), 'object angle should be a number');
49
+ ASSERT(isColor(color) && color.isValid(), 'object color should be a valid rgba color');
50
+ ASSERT(typeof renderOrder == 'number', 'object renderOrder should be a number');
47
51
 
48
52
  /** @property {Vector2} - World space position of the object */
49
53
  this.pos = pos.copy();
@@ -53,49 +57,49 @@ class EngineObject
53
57
  this.drawSize = undefined;
54
58
  /** @property {TileInfo} - Tile info to render object (undefined is untextured) */
55
59
  this.tileInfo = tileInfo;
56
- /** @property {number} - Angle to rotate the object */
60
+ /** @property {number} - Angle to rotate the object */
57
61
  this.angle = angle;
58
- /** @property {Color} - Color to apply when rendered */
62
+ /** @property {Color} - Color to apply when rendered */
59
63
  this.color = color;
60
- /** @property {Color} - Additive color to apply when rendered */
64
+ /** @property {Color} - Additive color to apply when rendered */
61
65
  this.additiveColor = undefined;
62
66
  /** @property {boolean} - Should it flip along y axis when rendered */
63
67
  this.mirror = false;
64
68
 
65
69
  // physical properties
66
- /** @property {number} [mass=objectDefaultMass] - How heavy the object is, static if 0 */
67
- this.mass = objectDefaultMass;
68
- /** @property {number} [damping=objectDefaultDamping] - How much to slow down velocity each frame (0-1) */
69
- this.damping = objectDefaultDamping;
70
+ /** @property {number} [mass=objectDefaultMass] - How heavy the object is, static if 0 */
71
+ this.mass = objectDefaultMass;
72
+ /** @property {number} [damping=objectDefaultDamping] - How much to slow down velocity each frame (0-1) */
73
+ this.damping = objectDefaultDamping;
70
74
  /** @property {number} [angleDamping=objectDefaultAngleDamping] - How much to slow down rotation each frame (0-1) */
71
75
  this.angleDamping = objectDefaultAngleDamping;
72
- /** @property {number} [elasticity=objectDefaultElasticity] - How bouncy the object is when colliding (0-1) */
73
- this.elasticity = objectDefaultElasticity;
74
- /** @property {number} [friction=objectDefaultFriction] - How much friction to apply when sliding (0-1) */
75
- this.friction = objectDefaultFriction;
76
- /** @property {number} - How much to scale gravity by for this object */
76
+ /** @property {number} [restitution=objectDefaultRestitution] - How bouncy the object is when colliding (0-1) */
77
+ this.restitution = objectDefaultRestitution;
78
+ /** @property {number} [friction=objectDefaultFriction] - How much friction to apply when sliding (0-1) */
79
+ this.friction = objectDefaultFriction;
80
+ /** @property {number} - How much to scale gravity by for this object */
77
81
  this.gravityScale = 1;
78
- /** @property {number} - Objects are sorted by render order */
82
+ /** @property {number} - Objects are sorted by render order */
79
83
  this.renderOrder = renderOrder;
80
84
  /** @property {Vector2} - Velocity of the object */
81
85
  this.velocity = vec2();
82
- /** @property {number} - Angular velocity of the object */
86
+ /** @property {number} - Angular velocity of the object */
83
87
  this.angleVelocity = 0;
84
- /** @property {number} - Track when object was created */
88
+ /** @property {number} - Track when object was created */
85
89
  this.spawnTime = time;
86
- /** @property {Array<EngineObject>} - List of children of this object */
90
+ /** @property {Array<EngineObject>} - List of children of this object */
87
91
  this.children = [];
88
- /** @property {boolean} - Limit object speed using linear or circular math */
89
- this.clampSpeedLinear = true;
92
+ /** @property {boolean} - Limit object speed along x and y axis */
93
+ this.clampSpeed = true;
90
94
  /** @property {EngineObject} - Object we are standing on, if any */
91
95
  this.groundObject = undefined;
92
96
 
93
97
  // parent child system
94
98
  /** @property {EngineObject} - Parent of object if in local space */
95
99
  this.parent = undefined;
96
- /** @property {Vector2} - Local position if child */
100
+ /** @property {Vector2} - Local position if child */
97
101
  this.localPos = vec2();
98
- /** @property {number} - Local angle if child */
102
+ /** @property {number} - Local angle if child */
99
103
  this.localAngle = 0;
100
104
 
101
105
  // collision flags
@@ -136,29 +140,20 @@ class EngineObject
136
140
  if (this.parent)
137
141
  return;
138
142
 
139
- // limit max speed to prevent missing collisions
140
- if (this.clampSpeedLinear)
143
+ if (this.clampSpeed)
141
144
  {
145
+ // limit max speed to prevent missing collisions
142
146
  this.velocity.x = clamp(this.velocity.x, -objectMaxSpeed, objectMaxSpeed);
143
147
  this.velocity.y = clamp(this.velocity.y, -objectMaxSpeed, objectMaxSpeed);
144
148
  }
145
- else
146
- {
147
- const length2 = this.velocity.lengthSquared();
148
- if (length2 > objectMaxSpeed*objectMaxSpeed)
149
- {
150
- const s = objectMaxSpeed / length2**.5;
151
- this.velocity.x *= s;
152
- this.velocity.y *= s;
153
- }
154
- }
155
149
 
156
150
  // apply physics
157
151
  const oldPos = this.pos.copy();
158
152
  this.velocity.x *= this.damping;
159
153
  this.velocity.y *= this.damping;
160
- if (this.mass) // don't apply gravity to static objects
154
+ if (this.mass)
161
155
  {
156
+ // apply gravity only if it has mass
162
157
  this.velocity.x += gravity.x * this.gravityScale;
163
158
  this.velocity.y += gravity.y * this.gravityScale;
164
159
  }
@@ -180,7 +175,6 @@ class EngineObject
180
175
  const groundSpeed = this.groundObject.velocity ? this.groundObject.velocity.x : 0;
181
176
  this.velocity.x = groundSpeed + (this.velocity.x - groundSpeed) * friction;
182
177
  this.groundObject = undefined;
183
- //debugOverlay && debugPhysics && debugPoint(this.pos.subtract(vec2(0,this.size.y/2)), '#0f0');
184
178
  }
185
179
 
186
180
  if (this.collideSolidObjects)
@@ -209,12 +203,12 @@ class EngineObject
209
203
  const deltaPos = oldPos.subtract(o.pos);
210
204
  const length = deltaPos.length();
211
205
  const pushAwayAccel = .001; // push away if already overlapping
212
- const velocity = length < .01 ? randVector(pushAwayAccel) : deltaPos.scale(pushAwayAccel/length);
206
+ const velocity = length < .01 ? randVec2(pushAwayAccel) : deltaPos.scale(pushAwayAccel/length);
213
207
  this.velocity = this.velocity.add(velocity);
214
208
  if (o.mass) // push away if not fixed
215
209
  o.velocity = o.velocity.subtract(velocity);
216
210
 
217
- debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
211
+ debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
218
212
  continue;
219
213
  }
220
214
 
@@ -223,7 +217,7 @@ class EngineObject
223
217
  const smallStepUp = (oldPos.y - o.pos.y)*2 > sizeBoth.y + gravity.y; // prefer to push up if small delta
224
218
  const isBlockedX = abs(oldPos.y - o.pos.y)*2 < sizeBoth.y;
225
219
  const isBlockedY = abs(oldPos.x - o.pos.x)*2 < sizeBoth.x;
226
- const elasticity = max(this.elasticity, o.elasticity);
220
+ const restitution = max(this.restitution, o.restitution);
227
221
 
228
222
  if (smallStepUp || isBlockedY || !isBlockedX) // resolve y collision
229
223
  {
@@ -236,7 +230,7 @@ class EngineObject
236
230
  this.groundObject = o;
237
231
 
238
232
  // bounce if other object is fixed or grounded
239
- this.velocity.y *= -elasticity;
233
+ this.velocity.y *= -restitution;
240
234
  }
241
235
  else if (o.mass)
242
236
  {
@@ -249,9 +243,9 @@ class EngineObject
249
243
  const elastic1 = o.velocity.y * (o.mass - this.mass) / (this.mass + o.mass)
250
244
  + this.velocity.y * 2 * this.mass / (this.mass + o.mass);
251
245
 
252
- // lerp between elastic or inelastic based on elasticity
253
- this.velocity.y = lerp(elasticity, inelastic, elastic0);
254
- o.velocity.y = lerp(elasticity, inelastic, elastic1);
246
+ // lerp between elastic or inelastic based on restitution
247
+ this.velocity.y = lerp(inelastic, elastic0, restitution);
248
+ o.velocity.y = lerp(inelastic, elastic1, restitution);
255
249
  }
256
250
  }
257
251
  if (!smallStepUp && isBlockedX) // resolve x collision
@@ -269,14 +263,14 @@ class EngineObject
269
263
  const elastic1 = o.velocity.x * (o.mass - this.mass) / (this.mass + o.mass)
270
264
  + this.velocity.x * 2 * this.mass / (this.mass + o.mass);
271
265
 
272
- // lerp between elastic or inelastic based on elasticity
273
- this.velocity.x = lerp(elasticity, inelastic, elastic0);
274
- o.velocity.x = lerp(elasticity, inelastic, elastic1);
266
+ // lerp between elastic or inelastic based on restitution
267
+ this.velocity.x = lerp(inelastic, elastic0, restitution);
268
+ o.velocity.x = lerp(inelastic, elastic1, restitution);
275
269
  }
276
270
  else // bounce if other object is fixed
277
- this.velocity.x *= -elasticity;
271
+ this.velocity.x *= -restitution;
278
272
  }
279
- debugOverlay && debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f0f');
273
+ debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f0f');
280
274
  }
281
275
  }
282
276
  if (this.collideTiles)
@@ -295,8 +289,8 @@ class EngineObject
295
289
  if (blockedLayerY || !blockedLayerX)
296
290
  {
297
291
  // bounce velocity
298
- const elasticity = max(this.elasticity, hitLayer.elasticity);
299
- this.velocity.y *= -elasticity;
292
+ const restitution = max(this.restitution, hitLayer.restitution);
293
+ this.velocity.y *= -restitution;
300
294
 
301
295
  if (wasMovingDown)
302
296
  {
@@ -319,9 +313,9 @@ class EngineObject
319
313
  {
320
314
  // move to previous position and bounce
321
315
  this.pos.x = oldPos.x;
322
- this.velocity.x *= -this.elasticity;
316
+ this.velocity.x *= -this.restitution;
323
317
  }
324
- debugOverlay && debugPhysics && debugRect(this.pos, this.size, '#f00');
318
+ debugPhysics && debugRect(this.pos, this.size, '#f00');
325
319
  }
326
320
  }
327
321
  }
@@ -334,7 +328,7 @@ class EngineObject
334
328
  drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
335
329
  }
336
330
 
337
- /** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
331
+ /** Destroy this object, destroy its children, detach it's parent, and mark it for removal */
338
332
  destroy()
339
333
  {
340
334
  if (this.destroyed)
@@ -344,7 +338,10 @@ class EngineObject
344
338
  this.destroyed = 1;
345
339
  this.parent && this.parent.removeChild(this);
346
340
  for (const child of this.children)
347
- child.destroy(child.parent = 0);
341
+ {
342
+ child.parent = 0;
343
+ child.destroy();
344
+ }
348
345
  }
349
346
 
350
347
  /** Convert from local space to world space
@@ -367,25 +364,29 @@ class EngineObject
367
364
  * @param {number} tileData - the value of the tile at the position
368
365
  * @param {Vector2} pos - tile where the collision occurred
369
366
  * @return {boolean} - true if the collision should be resolved */
370
- collideWithTile(tileData, pos) { return tileData > 0; }
367
+ collideWithTile(tileData, pos) { return tileData > 0; }
371
368
 
372
369
  /** Called to check if a object collision should be resolved
373
370
  * @param {EngineObject} object - the object to test against
374
371
  * @return {boolean} - true if the collision should be resolved
375
372
  */
376
- collideWithObject(object) { return true; }
373
+ collideWithObject(object) { return true; }
377
374
 
378
375
  /** How long since the object was created
379
376
  * @return {number} */
380
- getAliveTime() { return time - this.spawnTime; }
377
+ getAliveTime() { return time - this.spawnTime; }
381
378
 
382
379
  /** Apply acceleration to this object (adjust velocity, not affected by mass)
383
380
  * @param {Vector2} acceleration */
384
- applyAcceleration(acceleration) { if (this.mass) this.velocity = this.velocity.add(acceleration); }
381
+ applyAcceleration(acceleration) { if (this.mass) this.velocity = this.velocity.add(acceleration); }
382
+
383
+ /** Apply angular acceleration to this object
384
+ * @param {number} acceleration */
385
+ applyAngularAcceleration(acceleration) { if (this.mass) this.angleVelocity += acceleration; }
385
386
 
386
387
  /** Apply force to this object (adjust velocity, affected by mass)
387
388
  * @param {Vector2} force */
388
- applyForce(force) { this.applyAcceleration(force.scale(1/this.mass)); }
389
+ applyForce(force) { this.applyAcceleration(force.scale(1/this.mass)); }
389
390
 
390
391
  /** Get the direction of the mirror
391
392
  * @return {number} -1 if this.mirror is true, or 1 if not mirrored */
@@ -452,15 +453,15 @@ class EngineObject
452
453
  /** Render debug info for this object */
453
454
  renderDebugInfo()
454
455
  {
455
- if (debug)
456
- {
457
- // show object info for debugging
458
- const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
459
- const color1 = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, this.parent?.2:.5);
460
- const color2 = this.parent ? rgb(1,1,1,.5) : rgb(0,0,0,.8);
461
- drawRect(this.pos, size, color1, this.angle, false);
462
- drawRect(this.pos, size.scale(.8), color2, this.angle, false);
463
- this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(0,0,1,.5), false);
464
- }
456
+ if (!debug)
457
+ return;
458
+
459
+ // show object info for debugging
460
+ const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
461
+ const color = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, .5);
462
+ drawRect(this.pos, size, color, this.angle);
463
+ if (this.parent)
464
+ drawRect(this.pos, size.scale(.8), rgb(1,1,1,.5), this.angle);
465
+ this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(1,1,1,.5));
465
466
  }
466
467
  }
@@ -14,7 +14,7 @@
14
14
  * (
15
15
  * pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emitCone
16
16
  * tile(0, 16), // tileInfo
17
- * rgb(1,1,1), rgb(0,0,0), // colorStartA, colorStartB
17
+ * rgb(1,1,1,1), rgb(0,0,0,1), // colorStartA, colorStartB
18
18
  * rgb(1,1,1,0), rgb(0,0,0,0), // colorEndA, colorEndB
19
19
  * 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
20
20
  * .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
@@ -169,7 +169,7 @@ class ParticleEmitter extends EngineObject
169
169
  if (debugParticles)
170
170
  {
171
171
  // show emitter bounds
172
- const emitSize = typeof this.emitSize === 'number' ? vec2(this.emitSize) : this.emitSize;
172
+ const emitSize = typeof this.emitSize == 'number' ? vec2(this.emitSize) : this.emitSize;
173
173
  debugRect(this.pos, emitSize, '#0f0', 0, this.angle);
174
174
  }
175
175
  }
@@ -179,9 +179,9 @@ class ParticleEmitter extends EngineObject
179
179
  emitParticle()
180
180
  {
181
181
  // spawn a particle
182
- let pos = typeof this.emitSize === 'number' ? // check if number was used
183
- randInCircle(this.emitSize/2) // circle emitter
184
- : vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
182
+ let pos = typeof this.emitSize == 'number' ? // check if number was used
183
+ randInCircle(this.emitSize/2) // circle emitter
184
+ : vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
185
185
  .multiply(this.emitSize).rotate(this.angle)
186
186
  let angle = rand(this.particleConeAngle, -this.particleConeAngle);
187
187
  if (!this.localSpace)
@@ -209,15 +209,22 @@ class ParticleEmitter extends EngineObject
209
209
  const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
210
210
  particle.velocity = vec2().setAngle(velocityAngle, speed);
211
211
  particle.angleVelocity = angleSpeed;
212
+ if (!this.localSpace)
213
+ {
214
+ // apply emitter velocity to particle
215
+ particle.velocity.x += this.velocity.x;
216
+ particle.velocity.y += this.velocity.y;
217
+ particle.angleVelocity += this.angleVelocity;
218
+ }
212
219
  particle.fadeRate = this.fadeRate;
213
220
  particle.damping = this.damping;
214
221
  particle.angleDamping = this.angleDamping;
215
- particle.elasticity = this.elasticity;
222
+ particle.restitution = this.restitution;
216
223
  particle.friction = this.friction;
217
224
  particle.gravityScale = this.gravityScale;
218
225
  particle.collideTiles = this.collideTiles;
219
226
  particle.renderOrder = this.renderOrder;
220
- particle.mirror = !!randInt(2);
227
+ particle.mirror = randBool();
221
228
 
222
229
  // call particle create callback
223
230
  this.particleCreateCallback && this.particleCreateCallback(particle);
@@ -279,9 +286,26 @@ class Particle extends EngineObject
279
286
  this.localSpaceEmitter = localSpaceEmitter;
280
287
  /** @property {Function} - Called when particle dies */
281
288
  this.destroyCallback = destroyCallback;
289
+ // particles do not clamp speed by default
290
+ this.clampSpeed = false;
291
+ }
282
292
 
283
- // particles use circular clamped speed
284
- this.clampSpeedLinear = false;
293
+ /** Update the object physics, called automatically by engine once each frame */
294
+ update()
295
+ {
296
+ super.update();
297
+
298
+ if (this.collideTiles || this.collideSolidObjects)
299
+ {
300
+ // only apply max circular speed if particle can collide
301
+ const length2 = this.velocity.lengthSquared();
302
+ if (length2 > objectMaxSpeed*objectMaxSpeed)
303
+ {
304
+ const s = objectMaxSpeed / length2**.5;
305
+ this.velocity.x *= s;
306
+ this.velocity.y *= s;
307
+ }
308
+ }
285
309
  }
286
310
 
287
311
  /** Render the particle, automatically called each frame, sorted by renderOrder */
@@ -15,6 +15,12 @@
15
15
  * @memberof Settings */
16
16
  let cameraPos = vec2();
17
17
 
18
+ /** Rotation angle of camera in world space
19
+ * @type {number}
20
+ * @default
21
+ * @memberof Settings */
22
+ let cameraAngle = 0;
23
+
18
24
  /** Scale of camera in world space
19
25
  * @type {number}
20
26
  * @default
@@ -24,6 +30,13 @@ let cameraScale = 32;
24
30
  ///////////////////////////////////////////////////////////////////////////////
25
31
  // Display settings
26
32
 
33
+ /** Enable applying color to tiles when using canvas2d
34
+ * - This is slower but should be the same as webgl rendering
35
+ * @type {boolean}
36
+ * @default
37
+ * @memberof Settings */
38
+ let canvasColorTiles = true;
39
+
27
40
  /** The max size of the canvas, centered if window is larger
28
41
  * @type {Vector2}
29
42
  * @default Vector2(1920,1080)
@@ -37,14 +50,21 @@ let canvasMaxSize = vec2(1920, 1080);
37
50
  * @memberof Settings */
38
51
  let canvasFixedSize = vec2();
39
52
 
40
- /** Use nearest neighbor canvas scaling for more pixelated look
41
- * - Must be set before startup to take effect
53
+ /** Use nearest canvas scaling for more pixelated look
42
54
  * - If enabled sets css image-rendering:pixelated
43
55
  * @type {boolean}
44
56
  * @default
45
57
  * @memberof Settings */
46
58
  let canvasPixelated = true;
47
59
 
60
+ /** Use nearest canvas scaling for more pixelated look
61
+ * - If enabled sets css image-rendering:pixelated
62
+ * - This defaults to false because text looks better with smoothing
63
+ * @type {boolean}
64
+ * @default
65
+ * @memberof Settings */
66
+ let overlayCanvasPixelated = false;
67
+
48
68
  /** Disables texture filtering for crisper pixel art
49
69
  * @type {boolean}
50
70
  * @default
@@ -80,13 +100,6 @@ let headlessMode = false;
80
100
  * @memberof Settings */
81
101
  let glEnable = true;
82
102
 
83
- /** Fixes slow rendering in some browsers by not compositing the WebGL canvas
84
- * - Must be set before startup to take effect
85
- * @type {boolean}
86
- * @default
87
- * @memberof Settings */
88
- let glOverlay = true;
89
-
90
103
  ///////////////////////////////////////////////////////////////////////////////
91
104
  // Tile sheet settings
92
105
 
@@ -133,7 +146,7 @@ let objectDefaultAngleDamping = 1;
133
146
  * @type {number}
134
147
  * @default
135
148
  * @memberof Settings */
136
- let objectDefaultElasticity = 0;
149
+ let objectDefaultRestitution = 0;
137
150
 
138
151
  /** How much to slow when touching (0-1)
139
152
  * @type {number}
@@ -168,13 +181,13 @@ let particleEmitRateScale = 1;
168
181
  * @memberof Settings */
169
182
  let gamepadsEnable = true;
170
183
 
171
- /** If true, the dpad input is also routed to the left analog stick (for better accessability)
184
+ /** If true, the dpad input is also routed to the left analog stick (for better accessibility)
172
185
  * @type {boolean}
173
186
  * @default
174
187
  * @memberof Settings */
175
188
  let gamepadDirectionEmulateStick = true;
176
189
 
177
- /** If true the WASD keys are also routed to the direction keys (for better accessability)
190
+ /** If true the WASD keys are also routed to the direction keys (for better accessibility)
178
191
  * @type {boolean}
179
192
  * @default
180
193
  * @memberof Settings */
@@ -282,11 +295,24 @@ let medalsPreventUnlock = false;
282
295
  * @memberof Settings */
283
296
  function setCameraPos(pos) { cameraPos = pos; }
284
297
 
298
+ /** Set angle of camera in world space
299
+ * @param {number} angle
300
+ * @memberof Settings */
301
+ function setCameraAngle(angle) { cameraAngle = angle; }
302
+
285
303
  /** Set scale of camera in world space
286
304
  * @param {number} scale
287
305
  * @memberof Settings */
288
306
  function setCameraScale(scale) { cameraScale = scale; }
289
307
 
308
+ /** Set if tiles should be colorized when using canvas2d
309
+ * This can be slower but results should look nearly identical to webgl rendering
310
+ * It can be enabled/disabled at any time
311
+ * Optimized for performance, and will use faster method if color is white or untextured
312
+ * @param {boolean} colorTiles
313
+ * @memberof Settings */
314
+ function setCanvasColorTiles(colorTiles) { canvasColorTiles = colorTiles; }
315
+
290
316
  /** Set max size of the canvas
291
317
  * @param {Vector2} size
292
318
  * @memberof Settings */
@@ -297,10 +323,30 @@ function setCanvasMaxSize(size) { canvasMaxSize = size; }
297
323
  * @memberof Settings */
298
324
  function setCanvasFixedSize(size) { canvasFixedSize = size; }
299
325
 
300
- /** Use nearest neighbor scaling algorithm for canvas for more pixelated look
326
+ /** Use nearest scaling algorithm for canvas for more pixelated look
327
+ * - If enabled sets css image-rendering:pixelated
301
328
  * @param {boolean} pixelated
302
329
  * @memberof Settings */
303
- function setCanvasPixelated(pixelated) { canvasPixelated = pixelated; }
330
+ function setCanvasPixelated(pixelated)
331
+ {
332
+ canvasPixelated = pixelated;
333
+ if (mainCanvas)
334
+ mainCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
335
+ if (glCanvas)
336
+ glCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
337
+ }
338
+
339
+ /** Use nearest scaling algorithm for canvas for more pixelated look
340
+ * - If enabled sets css image-rendering:pixelated
341
+ * - This defaults to false because text looks better with smoothing
342
+ * @param {boolean} pixelated
343
+ * @memberof Settings */
344
+ function setOverlayCanvasPixelated(pixelated)
345
+ {
346
+ overlayCanvasPixelated = pixelated;
347
+ if (overlayCanvas)
348
+ overlayCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
349
+ }
304
350
 
305
351
  /** Disables texture filtering for crisper pixel art
306
352
  * @param {boolean} pixelated
@@ -325,12 +371,12 @@ function setHeadlessMode(headless) { headlessMode = headless; }
325
371
  /** Set if webgl rendering is enabled
326
372
  * @param {boolean} enable
327
373
  * @memberof Settings */
328
- function setGlEnable(enable) { glEnable = enable; }
329
-
330
- /** Set to not composite the WebGL canvas
331
- * @param {boolean} overlay
332
- * @memberof Settings */
333
- function setGlOverlay(overlay) { glOverlay = overlay; }
374
+ function setGLEnable(enable)
375
+ {
376
+ glEnable = enable;
377
+ if (glCanvas) // hide glCanvas if webgl is disabled
378
+ glCanvas.style.visibility = enable ? 'visible' : 'hidden';
379
+ }
334
380
 
335
381
  /** Set default size of tiles in pixels
336
382
  * @param {Vector2} size
@@ -362,10 +408,10 @@ function setObjectDefaultDamping(damp) { objectDefaultDamping = damp; }
362
408
  * @memberof Settings */
363
409
  function setObjectDefaultAngleDamping(damp) { objectDefaultAngleDamping = damp; }
364
410
 
365
- /** Set how much to bounce when a collision occur
366
- * @param {number} elasticity
411
+ /** Set how much to bounce when a collision occurs
412
+ * @param {number} restitution
367
413
  * @memberof Settings */
368
- function setObjectDefaultElasticity(elasticity) { objectDefaultElasticity = elasticity; }
414
+ function setObjectDefaultRestitution(restitution) { objectDefaultRestitution = restitution; }
369
415
 
370
416
  /** Set how much to slow when touching
371
417
  * @param {number} friction