littlejsengine 1.14.16 → 1.14.23

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 (62) hide show
  1. package/dist/littlejs.d.ts +173 -50
  2. package/dist/littlejs.esm.js +892 -419
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +879 -419
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +854 -420
  7. package/examples/box2d/game.js +3 -2
  8. package/examples/box2d/tiles.png +0 -0
  9. package/examples/breakout/game.js +5 -5
  10. package/examples/electron/game.js +1 -41
  11. package/examples/electron/index.html +2 -2
  12. package/examples/htmlMenu/game.js +1 -1
  13. package/examples/index.html +69 -55
  14. package/examples/module/game.js +6 -8
  15. package/examples/particles/index.html +6 -9
  16. package/examples/platformer/gameCharacter.js +1 -1
  17. package/examples/platformer/gameEffects.js +8 -3
  18. package/examples/platformer/gameObjects.js +3 -3
  19. package/examples/shorts/animation.js +2 -2
  20. package/examples/shorts/base.html +4 -1
  21. package/examples/shorts/cameraDrag.js +22 -0
  22. package/examples/shorts/debugDraw.js +38 -0
  23. package/examples/shorts/fps.js +90 -0
  24. package/examples/shorts/helloWorld.js +1 -1
  25. package/examples/shorts/hillGlideGame.js +1 -1
  26. package/examples/shorts/landerGame.js +1 -1
  27. package/examples/shorts/musicPlayer.js +1 -0
  28. package/examples/shorts/parallax.js +1 -1
  29. package/examples/shorts/postProcess.js +1 -1
  30. package/examples/shorts/sequencer.js +5 -5
  31. package/examples/shorts/shader.js +29 -0
  32. package/examples/shorts/spaceGame.js +1 -1
  33. package/examples/shorts/spriteAtlas.js +6 -6
  34. package/examples/shorts/starfield.js +1 -1
  35. package/examples/shorts/tileLayer.js +1 -1
  36. package/examples/shorts/tileRaycast.js +39 -0
  37. package/examples/shorts/tiles.png +0 -0
  38. package/examples/shorts/tiltedView.js +14 -6
  39. package/examples/shorts/topDown.js +1 -1
  40. package/examples/starter/game.js +6 -8
  41. package/examples/starter/index.html +2 -2
  42. package/examples/style.css +1 -0
  43. package/examples/typescript/game.js +5 -4
  44. package/examples/typescript/game.ts +5 -7
  45. package/examples/uiSystem/game.js +3 -2
  46. package/package.json +1 -1
  47. package/plugins/postProcess.js +71 -51
  48. package/plugins/uiSystem.js +76 -23
  49. package/src/engine.js +30 -16
  50. package/src/engineAudio.js +33 -23
  51. package/src/engineDebug.js +27 -0
  52. package/src/engineDraw.js +103 -49
  53. package/src/engineExport.js +13 -0
  54. package/src/engineInput.js +14 -16
  55. package/src/engineObject.js +31 -6
  56. package/src/engineParticles.js +37 -16
  57. package/src/engineRelease.js +2 -1
  58. package/src/engineSettings.js +20 -1
  59. package/src/engineTileLayer.js +89 -94
  60. package/src/engineUtilities.js +206 -58
  61. package/src/engineWebGL.js +145 -69
  62. package/examples/shorts/raycasting.js +0 -79
@@ -47,23 +47,22 @@ function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
47
47
  }
48
48
  }
49
49
 
50
- /** Return the center of first tile hit, undefined if nothing was hit.
51
- * This does not return the exact intersection, but the center of the tile hit.
50
+ /** Return the exact position of the boudnary of first tile hit, undefined if nothing was hit.
52
51
  * @param {Vector2} posStart
53
52
  * @param {Vector2} posEnd
54
53
  * @param {EngineObject} [object] - An object or undefined for generic test
54
+ * @param {Vector2} [normal] - Optional normal of the surface hit
55
55
  * @param {boolean} [solidOnly=true] - Only check solid layers if true
56
- * @return {Vector2}
56
+ * @return {Vector2|undefined} - position of the center of the tile hit or undefined if no hit
57
57
  * @memberof TileLayers */
58
- function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
58
+ function tileCollisionRaycast(posStart, posEnd, object, normal, solidOnly=true)
59
59
  {
60
60
  for (const layer of tileCollisionLayers)
61
61
  {
62
62
  if (!solidOnly || layer.isSolid)
63
63
  {
64
- const hitPos = layer.collisionRaycast(posStart, posEnd, object)
65
- if (hitPos)
66
- return hitPos;
64
+ const hitPos = layer.collisionRaycast(posStart, posEnd, object, normal)
65
+ if (hitPos) return hitPos;
67
66
  }
68
67
  }
69
68
  }
@@ -197,10 +196,15 @@ class CanvasLayer extends EngineObject
197
196
  /** @property {HTMLCanvasElement} - The canvas used by this layer */
198
197
  this.canvas = headlessMode ? undefined : new OffscreenCanvas(canvasSize.x, canvasSize.y);
199
198
  /** @property {OffscreenCanvasRenderingContext2D} - The 2D canvas context used by this layer */
200
- this.context = headlessMode ? undefined : this.canvas.getContext('2d');
201
- /** @property {WebGLTexture} - Texture if using WebGL for this layer, call useWebGL to enable */
202
- this.glTexture = undefined;
203
- this.gravityScale = 0; // disable gravity by default for canvas layers
199
+ this.context = this.canvas?.getContext('2d');
200
+ /** @property {TextureInfo} - Texture info to use for this object rendering */
201
+ const useWebGL = false; // do not use webgl by default
202
+ this.textureInfo = new TextureInfo(this.canvas, useWebGL);
203
+ /** @property {boolean} - True if WebGL texture needs to be refreshed */
204
+ this.refreshWebGL = false;
205
+
206
+ // disable physics by default
207
+ this.mass = this.gravityScale = this.friction = this.restitution = 0;
204
208
  }
205
209
 
206
210
  /** Destroy this canvas layer */
@@ -209,9 +213,7 @@ class CanvasLayer extends EngineObject
209
213
  if (this.destroyed)
210
214
  return;
211
215
 
212
- // free up the WebGL texture
213
- if (this.glTexture)
214
- glDeleteTexture(this.glTexture);
216
+ this.textureInfo.destroyWebGLTexture();
215
217
  super.destroy();
216
218
  }
217
219
 
@@ -233,9 +235,16 @@ class CanvasLayer extends EngineObject
233
235
  * @memberof Draw */
234
236
  draw(pos, size, angle=0, color=WHITE, mirror=false, additiveColor, screenSpace=false, context)
235
237
  {
238
+ const useWebGL = glEnable && this.textureInfo.hasWebGL();
239
+ if (useWebGL && this.refreshWebGL)
240
+ {
241
+ // update the WebGL texture
242
+ this.textureInfo.createWebGLTexture();
243
+ this.refreshWebGL = false;
244
+ }
245
+
236
246
  // draw the canvas layer as a single tile that uses the whole texture
237
- const useWebGL = glEnable && this.glTexture !== undefined;
238
- const tileInfo = new TileInfo().setFullImage(this.canvas, this.glTexture);
247
+ const tileInfo = new TileInfo().setFullImage(this.textureInfo);
239
248
  drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
240
249
  }
241
250
 
@@ -253,6 +262,8 @@ class CanvasLayer extends EngineObject
253
262
  * @param {Canvas2DDrawCallback} drawFunction */
254
263
  drawCanvas2D(pos, size, angle, mirror, drawFunction)
255
264
  {
265
+ if (!this.context) return;
266
+
256
267
  const context = this.context;
257
268
  context.save();
258
269
  pos = pos.subtract(this.pos).multiply(this.tileInfo.size);
@@ -302,18 +313,22 @@ class CanvasLayer extends EngineObject
302
313
  { this.drawTile(pos, size, undefined, color, angle); }
303
314
 
304
315
  /** Create or update the WebGL texture for this layer
305
- * @param {boolean} [enable] - enable WebGL rendering and update the texture */
306
- useWebGL(enable=true)
316
+ * @param {boolean} [enable] - enable WebGL rendering and update the texture
317
+ * @param {boolean} [immediate] - shoulkd the texture be updated immediately
318
+ */
319
+ useWebGL(enable=true, immediate=false)
307
320
  {
308
- if (glEnable && enable)
321
+ if (!immediate && enable && this.textureInfo.hasWebGL())
309
322
  {
310
- if (this.glTexture)
311
- glSetTextureData(this.glTexture, this.canvas);
312
- else
313
- this.glTexture = glCreateTexture(this.canvas);
323
+ // refresh the texture when needed
324
+ this.refreshWebGL = true;
325
+ return;
314
326
  }
327
+
328
+ if (enable)
329
+ this.textureInfo.createWebGLTexture();
315
330
  else
316
- this.glTexture = undefined;
331
+ this.textureInfo.destroyWebGLTexture();
317
332
  }
318
333
  }
319
334
 
@@ -337,24 +352,14 @@ class TileLayer extends CanvasLayer
337
352
  * @param {Vector2} size - World space size
338
353
  * @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
339
354
  * @param {number} [renderOrder] - Objects are sorted by renderOrder
340
- * @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
341
355
  */
342
- constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
356
+ constructor(position, size, tileInfo=tile(), renderOrder=0)
343
357
  {
344
- super(position, size, 0, renderOrder, size);
345
-
358
+ const canvasSize = tileInfo ? size.multiply(tileInfo.size) : size;
359
+ super(position, size, 0, renderOrder, canvasSize);
360
+
361
+ // set tile info
346
362
  this.tileInfo = tileInfo;
347
- const canvasSize = size.multiply(tileInfo.size);
348
- /** @property {HTMLCanvasElement} - The canvas used by this tile layer */
349
- this.canvas = new OffscreenCanvas(canvasSize.x, canvasSize.y);
350
- /** @property {OffscreenCanvasRenderingContext2D} - The 2D canvas context used by this tile layer */
351
- this.context = this.canvas.getContext('2d');
352
- /** @property {WebGLTexture} - Texture if using WebGL for this layer */
353
- this.glTexture = useWebGL ? glCreateTexture(this.canvas) : undefined;
354
- // set no friction by default, applied friction is max of both objects
355
- this.friction = 0;
356
- // set no restitution by default, applied restitution is max of both objects
357
- this.restitution = 0;
358
363
 
359
364
  // init tile data
360
365
  this.data = [];
@@ -403,11 +408,18 @@ class TileLayer extends CanvasLayer
403
408
  {
404
409
  ASSERT(drawContext !== this.context, 'must call redrawEnd() after drawing tiles!');
405
410
 
411
+ if (this.refreshWebGL)
412
+ {
413
+ // update the WebGL texture
414
+ this.textureInfo.createWebGLTexture();
415
+ this.refreshWebGL = false;
416
+ }
417
+
406
418
  // draw the tile layer as a single tile
407
- const tileInfo = new TileInfo().setFullImage(this.canvas, this.glTexture);
419
+ const tileInfo = new TileInfo().setFullImage(this.textureInfo);
408
420
  const size = this.drawSize || this.size;
409
421
  const pos = this.pos.add(size.scale(.5));
410
- const useWebGL = glEnable && this.glTexture !== undefined;
422
+ const useWebGL = glEnable && this.textureInfo.hasWebGL();
411
423
  drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
412
424
  }
413
425
 
@@ -420,8 +432,7 @@ class TileLayer extends CanvasLayer
420
432
  for (let y = this.size.y; y--;)
421
433
  this.drawTileData(vec2(x,y), false);
422
434
  this.redrawEnd();
423
- if (this.glTexture)
424
- this.useWebGL(); // update WebGL texture
435
+ this.useWebGL();
425
436
  }
426
437
 
427
438
  /** Call to start the redraw process
@@ -429,6 +440,8 @@ class TileLayer extends CanvasLayer
429
440
  * @param {boolean} [clear] - Should it clear the canvas before drawing */
430
441
  redrawStart(clear=false)
431
442
  {
443
+ if (!this.context) return;
444
+
432
445
  // save current render settings
433
446
  /** @type {[HTMLCanvasElement|OffscreenCanvas, CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number]} */
434
447
  this.savedRenderSettings = [drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale];
@@ -438,8 +451,9 @@ class TileLayer extends CanvasLayer
438
451
  drawCanvas = this.canvas;
439
452
  drawContext = this.context;
440
453
  cameraPos = this.size.scale(.5);
441
- cameraScale = this.tileInfo.size.x;
442
- mainCanvasSize = this.size.multiply(this.tileInfo.size);
454
+ const tileSize = this.tileInfo ? this.tileInfo.size : vec2(1);
455
+ cameraScale = tileSize.x;
456
+ mainCanvasSize = this.size.multiply(tileSize);
443
457
  if (clear)
444
458
  {
445
459
  // clear and set size
@@ -457,6 +471,8 @@ class TileLayer extends CanvasLayer
457
471
  /** Call to end the redraw process */
458
472
  redrawEnd()
459
473
  {
474
+ if (!this.context) return;
475
+
460
476
  ASSERT(drawContext === this.context, 'must call redrawStart() before drawing tiles');
461
477
  glCopyToContext(drawContext);
462
478
  //debugSaveCanvas(this.canvas);
@@ -473,6 +489,8 @@ class TileLayer extends CanvasLayer
473
489
  */
474
490
  drawTileData(layerPos, clear=true)
475
491
  {
492
+ if (!this.context) return;
493
+
476
494
  // clear out where the tile was, for full opaque tiles this can be skipped
477
495
  const s = this.tileInfo.size;
478
496
  if (clear)
@@ -509,11 +527,10 @@ class TileCollisionLayer extends TileLayer
509
527
  * @param {Vector2} size - World space size
510
528
  * @param {TileInfo} [tileInfo] - Tile info for layer
511
529
  * @param {number} [renderOrder] - Objects are sorted by renderOrder
512
- * @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
513
530
  */
514
- constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
531
+ constructor(position, size, tileInfo=tile(), renderOrder=0)
515
532
  {
516
- super(position, size.floor(), tileInfo, renderOrder, useWebGL);
533
+ super(position, size.floor(), tileInfo, renderOrder);
517
534
 
518
535
  /** @property {Array<number>} - The tile collision grid */
519
536
  this.collisionData = [];
@@ -596,65 +613,43 @@ class TileCollisionLayer extends TileLayer
596
613
  // check if the object should collide with this tile
597
614
  const tileData = this.collisionData[y*this.size.x+x];
598
615
  if (tileData)
599
- if (!object || object.collideWithTile(tileData, hitPos.set(x, y)))
616
+ if (!object || object.collideWithTile(tileData,
617
+ hitPos.set(x + this.pos.x, y + this.pos.y)))
600
618
  return true;
601
619
  }
602
620
  return false;
603
621
  }
604
622
 
605
- /** Return the center of first tile hit, undefined if nothing was hit.
606
- * This does not return the exact intersection, but the center of the tile hit.
623
+ /** Return the exact position of the boudnary of first tile hit, undefined if nothing was hit.
607
624
  * @param {Vector2} posStart
608
625
  * @param {Vector2} posEnd
609
- * @param {EngineObject} [object]
610
- * @return {Vector2} */
611
- collisionRaycast(posStart, posEnd, object)
626
+ * @param {EngineObject} [object] - An object or undefined for generic test
627
+ * @param {Vector2} [normal] - Optional normal of the surface hit
628
+ * @return {Vector2|undefined} */
629
+ collisionRaycast(posStart, posEnd, object, normal)
612
630
  {
613
631
  ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
614
632
  ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
615
-
616
- // transform to local layer space
617
- const posStartX = posStart.x - this.pos.x;
618
- const posStartY = posStart.y - this.pos.y;
619
- const posEndX = posEnd.x - this.pos.x;
620
- const posEndY = posEnd.y - this.pos.y;
621
-
622
- // test if a ray collides with tiles from start to end
623
- const deltaX = posEndX - posStartX;
624
- const deltaY = posEndY - posStartY;
625
- const totalLength = (deltaX**2 + deltaY**2)**.5;
626
- const unitX = abs(totalLength/deltaX);
627
- const unitY = abs(totalLength/deltaY);
628
-
629
- // setup iteration variables
630
- const pos = posStart.floor(), signDeltaX = sign(deltaX), signDeltaY = sign(deltaY);
631
- let xi = unitX * (deltaX < 0 ? posStart.x - pos.x : pos.x - posStart.x + 1) || 0;
632
- let yi = unitY * (deltaY < 0 ? posStart.y - pos.y : pos.y - posStart.y + 1) || 0;
633
-
634
- // use line drawing algorithm to test for collisions
635
- while (true)
633
+
634
+ const localPos = new Vector2;
635
+ const collisionTest = (pos)=>
636
636
  {
637
637
  // check for tile collision
638
- const tileData = this.getCollisionData(pos);
639
- if (tileData && (!object || object.collideWithTile(tileData, pos)))
640
- {
641
- pos.x += .5; pos.y += .5;
642
- debugRaycast && debugLine(posStart, posEnd, '#f00', .02);
643
- debugRaycast && debugPoint(pos, '#ff0');
644
- return pos;
645
- }
646
-
647
- // check if past the end
648
- if (xi >= totalLength && yi >= totalLength)
649
- break;
650
-
651
- // get coordinates of next tile to check
652
- if (xi > yi)
653
- pos.y += signDeltaY, yi += unitY;
654
- else
655
- pos.x += signDeltaX, xi += unitX;
638
+ localPos.set(pos.x - this.pos.x, pos.y - this.pos.y);
639
+ const tileData = this.getCollisionData(localPos);
640
+ return tileData && (!object || object.collideWithTile(tileData, pos));
656
641
  }
657
-
658
642
  debugRaycast && debugLine(posStart, posEnd, '#00f', .02);
643
+ const hitPos = lineTest(posStart, posEnd, collisionTest, normal);
644
+ if (hitPos)
645
+ {
646
+ const tilePos = hitPos.floor().add(vec2(.5));
647
+ debugRaycast && debugRect(tilePos, vec2(1), '#f008');
648
+ debugRaycast && debugLine(posStart, hitPos, '#f00', .02);
649
+ debugRaycast && debugPoint(hitPos, '#0f0');
650
+ debugRaycast && normal &&
651
+ debugLine(hitPos, hitPos.add(normal), '#ff0', .02);
652
+ return hitPos;
653
+ }
659
654
  }
660
655
  }