littlejsengine 1.9.11 → 1.10.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 (76) hide show
  1. package/README.md +9 -13
  2. package/dist/littlejs.d.ts +129 -10
  3. package/dist/littlejs.esm.js +220 -75
  4. package/dist/littlejs.esm.min.js +1 -1
  5. package/dist/littlejs.js +188 -73
  6. package/dist/littlejs.min.js +1 -1
  7. package/dist/littlejs.release.js +187 -72
  8. package/examples/box2d/game.js +4 -1
  9. package/examples/box2d/index.html +7 -6
  10. package/examples/breakout/game.js +1 -1
  11. package/examples/breakout/index.html +5 -4
  12. package/examples/breakoutTutorial/README.md +9 -7
  13. package/examples/breakoutTutorial/game.js +6 -6
  14. package/examples/breakoutTutorial/index.html +4 -3
  15. package/examples/electron/game.js +1 -1
  16. package/examples/electron/index.html +4 -3
  17. package/examples/empty/game.js +1 -1
  18. package/examples/empty/index.html +2 -1
  19. package/examples/htmlMenu/game.js +67 -0
  20. package/examples/htmlMenu/index.html +56 -0
  21. package/examples/htmlMenu/tiles.png +0 -0
  22. package/examples/index.html +33 -0
  23. package/examples/js13k/build/index.html +1 -0
  24. package/examples/js13k/build/index.js +1 -0
  25. package/examples/js13k/build/tiles.png +0 -0
  26. package/examples/js13k/game - Copy.zip +0 -0
  27. package/examples/js13k/game.js +1 -1
  28. package/examples/js13k/game.zip +0 -0
  29. package/examples/js13k/index.html +17 -14
  30. package/examples/module/game.js +1 -1
  31. package/examples/module/index.html +3 -2
  32. package/examples/particles/index.html +4 -3
  33. package/examples/platformer/game.js +7 -5
  34. package/examples/platformer/gameCharacter.js +12 -11
  35. package/examples/platformer/gameEffects.js +3 -4
  36. package/examples/platformer/gameLevel.js +18 -34
  37. package/examples/platformer/gameObjects.js +9 -15
  38. package/examples/platformer/index.html +10 -9
  39. package/examples/puzzle/game.js +1 -1
  40. package/examples/puzzle/index.html +4 -3
  41. package/examples/starter/build/index.html +2 -0
  42. package/examples/starter/build/index.js +1 -0
  43. package/examples/starter/build/tiles.png +0 -0
  44. package/examples/starter/game.js +4 -4
  45. package/examples/starter/game.zip +0 -0
  46. package/examples/starter/index.html +15 -14
  47. package/examples/stress/index.html +3 -2
  48. package/examples/typescript/build/build/littlejs.esm.js +4327 -0
  49. package/examples/typescript/build/dist/littlejs.esm.js +4425 -0
  50. package/examples/typescript/build/examples/typescript/build.js +24 -0
  51. package/examples/typescript/build/examples/typescript/game.js +100 -0
  52. package/examples/typescript/build/examples/typescript/test/build/littlejs.esm.js +3934 -0
  53. package/examples/typescript/build/examples/typescript/test/examples/typescript/build.js +86 -0
  54. package/examples/typescript/build/examples/typescript/test/examples/typescript/game.js +92 -0
  55. package/examples/typescript/game.js +1 -1
  56. package/examples/typescript/game.ts +1 -1
  57. package/examples/typescript/index.html +3 -2
  58. package/examples/uiSystem/game.js +134 -0
  59. package/examples/uiSystem/index.html +25 -0
  60. package/examples/uiSystem/tiles.png +0 -0
  61. package/jsconfig.json +11 -0
  62. package/package.json +1 -1
  63. package/plugins/box2d.js +2 -1
  64. package/plugins/postProcess.js +14 -8
  65. package/plugins/uiSystem.js +243 -0
  66. package/src/engine.js +42 -22
  67. package/src/engineAudio.js +10 -13
  68. package/src/engineDebug.js +1 -1
  69. package/src/engineDraw.js +102 -27
  70. package/src/engineExport.js +32 -2
  71. package/src/engineInput.js +1 -3
  72. package/src/engineObject.js +1 -0
  73. package/src/engineSettings.js +1 -1
  74. package/src/engineTileLayer.js +2 -1
  75. package/src/engineUtilities.js +2 -1
  76. package/src/engineWebGL.js +26 -4
@@ -128,7 +128,6 @@ function debugPoint(pos, color, time, angle)
128
128
  * @memberof Debug */
129
129
  function debugLine(posA, posB, color, thickness=.1, time)
130
130
  {
131
- ASSERT(typeof color == 'string', 'pass in css color strings');
132
131
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
133
132
  const size = vec2(thickness, halfDelta.length()*2);
134
133
  debugRect(posA.add(halfDelta), size, color, time, halfDelta.angle(), true);
@@ -204,6 +203,7 @@ function debugShowErrors()
204
203
 
205
204
  const showError = (message)=>
206
205
  {
206
+ // replace entire page with error message
207
207
  document.body.style.backgroundColor = '#111';
208
208
  document.body.innerHTML = `<pre style=color:#f00;font-size:50px>` + message;
209
209
  }
@@ -953,6 +953,7 @@ class Vector2
953
953
  * @param {Number} [length] */
954
954
  setDirection(direction, length=1)
955
955
  {
956
+ direction = mod(direction, 4);
956
957
  ASSERT(direction==0 || direction==1 || direction==2 || direction==3);
957
958
  return vec2(direction%2 ? direction-1 ? -length : length : 0,
958
959
  direction%2 ? 0 : direction ? -length : length);
@@ -1205,7 +1206,7 @@ class Color
1205
1206
  * @return {String} */
1206
1207
  toString(useAlpha = true)
1207
1208
  {
1208
- const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
1209
+ const toHex = (c)=> ((c=clamp(c)*255|0)<16 ? '0' : '') + c.toString(16);
1209
1210
  return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
1210
1211
  }
1211
1212
 
@@ -1435,7 +1436,7 @@ let tileSizeDefault = vec2(16);
1435
1436
  * @type {Number}
1436
1437
  * @default
1437
1438
  * @memberof Settings */
1438
- let tileFixBleedScale = .5;
1439
+ let tileFixBleedScale = 0;
1439
1440
 
1440
1441
  ///////////////////////////////////////////////////////////////////////////////
1441
1442
  // Object settings
@@ -2135,6 +2136,7 @@ class EngineObject
2135
2136
  this.pos.x = oldPos.x;
2136
2137
  this.velocity.x *= -this.elasticity;
2137
2138
  }
2139
+ debugOverlay && debugPhysics && debugRect(this.pos, this.size, '#f00');
2138
2140
  }
2139
2141
  }
2140
2142
  }
@@ -2337,12 +2339,13 @@ let drawCount;
2337
2339
  ///////////////////////////////////////////////////////////////////////////////
2338
2340
 
2339
2341
  /**
2340
- * Create a tile info object
2342
+ * Create a tile info object using a grid based system
2341
2343
  * - This can take vecs or floats for easier use and conversion
2342
2344
  * - If an index is passed in, the tile size and index will determine the position
2343
- * @param {(Number|Vector2)} [pos=(0,0)] - Top left corner of tile in pixels or index
2345
+ * @param {(Number|Vector2)} [pos=0] - Index of tile in sheet
2344
2346
  * @param {(Number|Vector2)} [size=tileSizeDefault] - Size of tile in pixels
2345
2347
  * @param {Number} [textureIndex] - Texture index to use
2348
+ * @param {Number} [padding] - How many pixels padding around tiles
2346
2349
  * @return {TileInfo}
2347
2350
  * @example
2348
2351
  * tile(2) // a tile at index 2 using the default tile size of 16
@@ -2351,7 +2354,7 @@ let drawCount;
2351
2354
  * tile(vec2(4,8), vec2(30,10)) // a tile at pixel location (4,8) with a size of (30,10)
2352
2355
  * @memberof Draw
2353
2356
  */
2354
- function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
2357
+ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
2355
2358
  {
2356
2359
  if (headlessMode)
2357
2360
  return new TileInfo;
@@ -2363,17 +2366,17 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
2363
2366
  size = vec2(size);
2364
2367
  }
2365
2368
 
2366
- // if pos is a number, use it as a tile index
2369
+ // use pos as a tile index
2370
+ const textureInfo = textureInfos[textureIndex];
2371
+ ASSERT(textureInfo, 'Texture not loaded');
2372
+ const sizePadded = size.add(vec2(padding*2));
2373
+ const cols = textureInfo.size.x / sizePadded.x |0;
2367
2374
  if (typeof pos === 'number')
2368
- {
2369
- const textureInfo = textureInfos[textureIndex];
2370
- ASSERT(textureInfo, 'Texture not loaded');
2371
- const cols = textureInfo.size.x / size.x |0;
2372
- pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
2373
- }
2375
+ pos = vec2(pos%cols, pos/cols|0);
2376
+ pos = vec2(pos.x*sizePadded.x+padding, pos.y*sizePadded.y+padding);
2374
2377
 
2375
2378
  // return a tile info object
2376
- return new TileInfo(pos, size, textureIndex);
2379
+ return new TileInfo(pos, size, textureIndex, padding);
2377
2380
  }
2378
2381
 
2379
2382
  /**
@@ -2385,8 +2388,9 @@ class TileInfo
2385
2388
  * @param {Vector2} [pos=(0,0)] - Top left corner of tile in pixels
2386
2389
  * @param {Vector2} [size=tileSizeDefault] - Size of tile in pixels
2387
2390
  * @param {Number} [textureIndex] - Texture index to use
2391
+ * @param {Number} [padding] - How many pixels padding around tiles
2388
2392
  */
2389
- constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0)
2393
+ constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
2390
2394
  {
2391
2395
  /** @property {Vector2} - Top left corner of tile in pixels */
2392
2396
  this.pos = pos.copy();
@@ -2394,6 +2398,8 @@ class TileInfo
2394
2398
  this.size = size.copy();
2395
2399
  /** @property {Number} - Texture index to use */
2396
2400
  this.textureIndex = textureIndex;
2401
+ /** @property {Number} - How many pixels padding around tiles */
2402
+ this.padding = padding;
2397
2403
  }
2398
2404
 
2399
2405
  /** Returns a copy of this tile offset by a vector
@@ -2410,7 +2416,7 @@ class TileInfo
2410
2416
  frame(frame)
2411
2417
  {
2412
2418
  ASSERT(typeof frame == 'number');
2413
- return this.offset(vec2(frame*this.size.x, 0));
2419
+ return this.offset(vec2(frame*(this.size.x+this.padding*2), 0));
2414
2420
  }
2415
2421
 
2416
2422
  /** Returns the texture info for this tile
@@ -2435,8 +2441,6 @@ class TextureInfo
2435
2441
  this.size = vec2(image.width, image.height);
2436
2442
  /** @property {WebGLTexture} - webgl texture */
2437
2443
  this.glTexture = glEnable && glCreateTexture(image);
2438
- /** @property {Vector2} - size to adjust tile to fix bleeding */
2439
- this.fixBleedSize = vec2(tileFixBleedScale).divide(this.size);
2440
2444
  }
2441
2445
  }
2442
2446
 
@@ -2505,11 +2509,12 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
2505
2509
  if (textureInfo)
2506
2510
  {
2507
2511
  // calculate uvs and render
2508
- const x = tileInfo.pos.x / textureInfo.size.x;
2509
- const y = tileInfo.pos.y / textureInfo.size.y;
2510
- const w = tileInfo.size.x / textureInfo.size.x;
2511
- const h = tileInfo.size.y / textureInfo.size.y;
2512
- const tileImageFixBleed = textureInfo.fixBleedSize;
2512
+ const sizeInverse = vec2(1).divide(textureInfo.size);
2513
+ const x = tileInfo.pos.x * sizeInverse.x;
2514
+ const y = tileInfo.pos.y * sizeInverse.y;
2515
+ const w = tileInfo.size.x * sizeInverse.x;
2516
+ const h = tileInfo.size.y * sizeInverse.y;
2517
+ const tileImageFixBleed = sizeInverse.scale(tileFixBleedScale);
2513
2518
  glSetTexture(textureInfo.glTexture);
2514
2519
  glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
2515
2520
  x + tileImageFixBleed.x, y + tileImageFixBleed.y,
@@ -2526,6 +2531,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
2526
2531
  {
2527
2532
  // normal canvas 2D rendering method (slower)
2528
2533
  showWatermark && ++drawCount;
2534
+ size = vec2(size.x, -size.y); // fix upside down sprites
2529
2535
  drawCanvas2D(pos, size, angle, mirror, (context)=>
2530
2536
  {
2531
2537
  if (textureInfo)
@@ -2579,6 +2585,74 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, contex
2579
2585
  drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL, screenSpace, context);
2580
2586
  }
2581
2587
 
2588
+ /** Draw colored polygon using passed in points
2589
+ * @param {Array} points - Array of Vector2 points
2590
+ * @param {Color} [color=(1,1,1,1)]
2591
+ * @param {Number} [lineWidth=0]
2592
+ * @param {Color} [lineColor=(0,0,0,1)]
2593
+ * @param {Boolean} [screenSpace=false]
2594
+ * @param {CanvasRenderingContext2D} [context=mainContext]
2595
+ * @memberof Draw */
2596
+ function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2597
+ {
2598
+ context.fillStyle = color.toString();
2599
+ context.beginPath();
2600
+ for (const point of screenSpace ? points : points.map(worldToScreen))
2601
+ context.lineTo(point.x, point.y);
2602
+ context.closePath();
2603
+ context.fill();
2604
+ if (lineWidth)
2605
+ {
2606
+ context.strokeStyle = lineColor.toString();
2607
+ context.lineWidth = screenSpace ? lineWidth : lineWidth*cameraScale;
2608
+ context.stroke();
2609
+ }
2610
+ }
2611
+
2612
+ /** Draw colored ellipse using passed in point
2613
+ * @param {Vector2} pos
2614
+ * @param {Number} [width=1]
2615
+ * @param {Number} [height=1]
2616
+ * @param {Number} [angle=0]
2617
+ * @param {Color} [color=(1,1,1,1)]
2618
+ * @param {Number} [lineWidth=0]
2619
+ * @param {Color} [lineColor=(0,0,0,1)]
2620
+ * @param {Boolean} [screenSpace=false]
2621
+ * @param {CanvasRenderingContext2D} [context=mainContext]
2622
+ * @memberof Draw */
2623
+ function drawEllipse(pos, width=1, height=1, angle=0, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2624
+ {
2625
+ if (!screenSpace)
2626
+ {
2627
+ pos = worldToScreen(pos);
2628
+ width *= cameraScale;
2629
+ height *= cameraScale;
2630
+ lineWidth *= cameraScale;
2631
+ }
2632
+ context.fillStyle = color.toString();
2633
+ context.beginPath();
2634
+ context.ellipse(pos.x, pos.y, width, height, angle, 0, 9);
2635
+ context.fill();
2636
+ if (lineWidth)
2637
+ {
2638
+ context.strokeStyle = lineColor.toString();
2639
+ context.lineWidth = lineWidth;
2640
+ context.stroke();
2641
+ }
2642
+ }
2643
+
2644
+ /** Draw colored circle using passed in point
2645
+ * @param {Vector2} pos
2646
+ * @param {Number} [radius=1]
2647
+ * @param {Color} [color=(1,1,1,1)]
2648
+ * @param {Number} [lineWidth=0]
2649
+ * @param {Color} [lineColor=(0,0,0,1)]
2650
+ * @param {Boolean} [screenSpace=false]
2651
+ * @param {CanvasRenderingContext2D} [context=mainContext]
2652
+ * @memberof Draw */
2653
+ function drawCircle(pos, radius=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2654
+ { drawEllipse(pos, radius, radius, 0, color, lineWidth, lineColor, screenSpace, context); }
2655
+
2582
2656
  /** Draw directly to a 2d canvas context in world space
2583
2657
  * @param {Vector2} pos
2584
2658
  * @param {Vector2} size
@@ -2633,10 +2707,11 @@ function setBlendMode(additive, useWebGL=glEnable, context)
2633
2707
  * @param {CanvasTextAlign} [textAlign='center']
2634
2708
  * @param {String} [font=fontDefault]
2635
2709
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2710
+ * @param {Number} [maxWidth]
2636
2711
  * @memberof Draw */
2637
- function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context)
2712
+ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context, maxWidth)
2638
2713
  {
2639
- drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context);
2714
+ drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context, maxWidth);
2640
2715
  }
2641
2716
 
2642
2717
  /** Draw text on overlay canvas in screen space
@@ -2650,8 +2725,9 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
2650
2725
  * @param {CanvasTextAlign} [textAlign]
2651
2726
  * @param {String} [font=fontDefault]
2652
2727
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2728
+ * @param {Number} [maxWidth]
2653
2729
  * @memberof Draw */
2654
- function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
2730
+ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext, maxWidth=undefined)
2655
2731
  {
2656
2732
  context.fillStyle = color.toString();
2657
2733
  context.lineWidth = lineWidth;
@@ -2664,8 +2740,8 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
2664
2740
  pos = pos.copy();
2665
2741
  (text+'').split('\n').forEach(line=>
2666
2742
  {
2667
- lineWidth && context.strokeText(line, pos.x, pos.y);
2668
- context.fillText(line, pos.x, pos.y);
2743
+ lineWidth && context.strokeText(line, pos.x, pos.y, maxWidth);
2744
+ context.fillText(line, pos.x, pos.y, maxWidth);
2669
2745
  pos.y += size;
2670
2746
  });
2671
2747
  }
@@ -2768,13 +2844,14 @@ function isFullscreen() { return !!document.fullscreenElement; }
2768
2844
  * @memberof Draw */
2769
2845
  function toggleFullscreen()
2770
2846
  {
2847
+ const rootElement = mainCanvas.parentElement;
2771
2848
  if (isFullscreen())
2772
2849
  {
2773
2850
  if (document.exitFullscreen)
2774
2851
  document.exitFullscreen();
2775
2852
  }
2776
- else if (document.body.requestFullscreen)
2777
- document.body.requestFullscreen();
2853
+ else if (rootElement.requestFullscreen)
2854
+ rootElement.requestFullscreen();
2778
2855
  }
2779
2856
  /**
2780
2857
  * LittleJS Input System
@@ -2945,7 +3022,6 @@ function inputInit()
2945
3022
 
2946
3023
  onkeydown = (e)=>
2947
3024
  {
2948
- if (debug && e.target != document.body) return;
2949
3025
  if (!e.repeat)
2950
3026
  {
2951
3027
  isUsingGamepad = false;
@@ -2958,7 +3034,6 @@ function inputInit()
2958
3034
 
2959
3035
  onkeyup = (e)=>
2960
3036
  {
2961
- if (debug && e.target != document.body) return;
2962
3037
  inputData[0][e.code] = 4;
2963
3038
  if (inputWASDEmulateDirection)
2964
3039
  inputData[0][remapKey(e.code)] = 4;
@@ -3160,7 +3235,7 @@ function touchInputInit()
3160
3235
  // set event pos and pass it along
3161
3236
  const p = vec2(e.touches[0].clientX, e.touches[0].clientY);
3162
3237
  mousePosScreen = mouseToScreen(p);
3163
- wasTouching ? isUsingGamepad = false : inputData[0][button] = 3;
3238
+ wasTouching ? isUsingGamepad = touchGamepadEnable : inputData[0][button] = 3;
3164
3239
  }
3165
3240
  else if (wasTouching)
3166
3241
  inputData[0][button] = inputData[0][button] & 2 | 4;
@@ -3610,17 +3685,6 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
3610
3685
  {
3611
3686
  if (!soundEnable || headlessMode) return;
3612
3687
 
3613
- // prevent sounds from building up if they can't be played
3614
- if (audioContext.state != 'running')
3615
- {
3616
- // fix stalled audio
3617
- audioContext.resume().then(()=>
3618
- playSamples(sampleChannels, volume, rate, pan, loop, sampleRate, gainNode));
3619
-
3620
- // prevent suspended sounds from building up
3621
- return;
3622
- }
3623
-
3624
3688
  // create buffer and source
3625
3689
  const channelCount = sampleChannels.length;
3626
3690
  const sampleLength = sampleChannels[0].length;
@@ -3642,8 +3706,16 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
3642
3706
  const pannerNode = new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)});
3643
3707
  source.connect(pannerNode).connect(gainNode);
3644
3708
 
3645
- // play and return sound
3646
- source.start();
3709
+ // play the sound
3710
+ if (audioContext.state != 'running')
3711
+ {
3712
+ // fix stalled audio and play
3713
+ audioContext.resume().then(()=>source.start());
3714
+ }
3715
+ else
3716
+ source.start();
3717
+
3718
+ // return sound
3647
3719
  return source;
3648
3720
  }
3649
3721
 
@@ -3959,6 +4031,7 @@ function tileCollisionTest(pos, size=vec2(), object)
3959
4031
  if (tileData && (!object || object.collideWithTile(tileData, vec2(x, y))))
3960
4032
  return true;
3961
4033
  }
4034
+ return false;
3962
4035
  }
3963
4036
 
3964
4037
  /** Return the center of first tile hit (does not return the exact intersection)
@@ -3982,7 +4055,7 @@ function tileCollisionRaycast(posStart, posEnd, object)
3982
4055
  let xi = unit.x * (delta.x < 0 ? posStart.x - pos.x : pos.x - posStart.x + 1);
3983
4056
  let yi = unit.y * (delta.y < 0 ? posStart.y - pos.y : pos.y - posStart.y + 1);
3984
4057
 
3985
- while (1)
4058
+ while (true)
3986
4059
  {
3987
4060
  // check for tile collision
3988
4061
  const tileData = getTileCollisionData(pos);
@@ -4791,6 +4864,11 @@ let glCanvas;
4791
4864
  * @memberof WebGL */
4792
4865
  let glContext;
4793
4866
 
4867
+ /** Shoule webgl be setup with antialiasing, must be set before calling engineInit
4868
+ * @type {Boolean}
4869
+ * @memberof WebGL */
4870
+ let glAntialias = true;
4871
+
4794
4872
  // WebGL internal variables not exposed to documentation
4795
4873
  let glShader, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glInstanceCount, glAdditive, glBatchAdditive;
4796
4874
 
@@ -4803,10 +4881,11 @@ function glInit()
4803
4881
 
4804
4882
  // create the canvas and textures
4805
4883
  glCanvas = document.createElement('canvas');
4806
- glContext = glCanvas.getContext('webgl2');
4884
+ glContext = glCanvas.getContext('webgl2', {antialias:glAntialias});
4807
4885
 
4808
4886
  // some browsers are much faster without copying the gl buffer so we just overlay it instead
4809
- glOverlay && document.body.appendChild(glCanvas);
4887
+ const rootElement = mainCanvas.parentElement;
4888
+ glOverlay && rootElement.appendChild(glCanvas);
4810
4889
 
4811
4890
  // setup vertex and fragment shaders
4812
4891
  glShader = glCreateProgram(
@@ -4861,7 +4940,8 @@ function glPreRender()
4861
4940
  // set up the shader
4862
4941
  glContext.useProgram(glShader);
4863
4942
  glContext.activeTexture(gl_TEXTURE0);
4864
- glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
4943
+ if (textureInfos[0])
4944
+ glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
4865
4945
 
4866
4946
  // set vertex attributes
4867
4947
  let offset = glAdditive = glBatchAdditive = 0;
@@ -4959,8 +5039,14 @@ function glCreateTexture(image)
4959
5039
  // build the texture
4960
5040
  const texture = glContext.createTexture();
4961
5041
  glContext.bindTexture(gl_TEXTURE_2D, texture);
4962
- if (image)
5042
+ if (image && image.width)
4963
5043
  glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
5044
+ else
5045
+ {
5046
+ // create a white texture
5047
+ const whitePixel = new Uint8Array([255, 255, 255, 255]);
5048
+ glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, 1, 1, 0, gl_RGBA, gl_UNSIGNED_BYTE, whitePixel);
5049
+ }
4964
5050
 
4965
5051
  // use point filtering for pixelated rendering
4966
5052
  const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
@@ -5003,6 +5089,15 @@ function glCopyToContext(context, forceDraw=false)
5003
5089
  context.drawImage(glCanvas, 0, 0);
5004
5090
  }
5005
5091
 
5092
+ /** Set antialiasing for webgl canvas
5093
+ * @param {Boolean} [antialias]
5094
+ * @memberof WebGL */
5095
+ function glSetAntialias(antialias=true)
5096
+ {
5097
+ ASSERT(!glCanvas, 'must be called before engineInit');
5098
+ glAntialias = antialias;
5099
+ }
5100
+
5006
5101
  /** Add a sprite to the gl draw list, used by all gl draw functions
5007
5102
  * @param {Number} x
5008
5103
  * @param {Number} y
@@ -5103,7 +5198,7 @@ const engineName = 'LittleJS';
5103
5198
  * @type {String}
5104
5199
  * @default
5105
5200
  * @memberof Engine */
5106
- const engineVersion = '1.9.11';
5201
+ const engineVersion = '1.10.4';
5107
5202
 
5108
5203
  /** Frames per second to update
5109
5204
  * @type {Number}
@@ -5147,7 +5242,6 @@ let timeReal = 0;
5147
5242
  * @default false
5148
5243
  * @memberof Engine */
5149
5244
  let paused = false;
5150
-
5151
5245
  /** Set if game is paused
5152
5246
  * @param {Boolean} isPaused
5153
5247
  * @memberof Engine */
@@ -5180,9 +5274,10 @@ function engineAddPlugin(updateFunction, renderFunction)
5180
5274
  * @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
5181
5275
  * @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
5182
5276
  * @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
5183
- * @param {Array} [imageSources=['tiles.png']] - Image to load
5277
+ * @param {Array} [imageSources=[]] - Image to load
5278
+ * @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default
5184
5279
  * @memberof Engine */
5185
- function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
5280
+ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
5186
5281
  {
5187
5282
  ASSERT(Array.isArray(imageSources), 'pass in images as array');
5188
5283
 
@@ -5224,6 +5319,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5224
5319
  for (const o of engineObjects)
5225
5320
  o.parent || o.updateTransforms();
5226
5321
  inputUpdate();
5322
+ pluginUpdateList.forEach(f=>f());
5227
5323
  debugUpdate();
5228
5324
  gameUpdatePost();
5229
5325
  inputUpdatePost();
@@ -5339,16 +5435,21 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5339
5435
  }
5340
5436
 
5341
5437
  // setup html
5342
- const styleBody =
5438
+ const styleRoot =
5343
5439
  'margin:0;overflow:hidden;' + // fill the window
5440
+ 'width:100vw;height:100vh;' + // fill the window
5441
+ 'display:flex;' + // use flexbox
5442
+ 'align-items:center;' + // horizontal center
5443
+ (canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
5444
+ 'justify-content:center;' + // vertical center
5344
5445
  'background:#000;' + // set background color
5345
5446
  'user-select:none;' + // prevent hold to select
5346
5447
  '-webkit-user-select:none;' + // compatibility for ios
5347
5448
  (!touchInputEnable ? '' : // no touch css setttings
5348
5449
  'touch-action:none;' + // prevent mobile pinch to resize
5349
5450
  '-webkit-touch-callout:none');// compatibility for ios
5350
- document.body.style.cssText = styleBody;
5351
- document.body.appendChild(mainCanvas = document.createElement('canvas'));
5451
+ rootElement.style.cssText = styleRoot;
5452
+ rootElement.appendChild(mainCanvas = document.createElement('canvas'));
5352
5453
  mainContext = mainCanvas.getContext('2d');
5353
5454
 
5354
5455
  // init stuff and start engine
@@ -5358,13 +5459,14 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5358
5459
  glInit();
5359
5460
 
5360
5461
  // create overlay canvas for hud to appear above gl canvas
5361
- document.body.appendChild(overlayCanvas = document.createElement('canvas'));
5462
+ rootElement.appendChild(overlayCanvas = document.createElement('canvas'));
5362
5463
  overlayContext = overlayCanvas.getContext('2d');
5363
5464
 
5364
5465
  // set canvas style
5365
- const styleCanvas = 'position:absolute;' + // position
5366
- 'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
5367
- (glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
5466
+ const styleCanvas = 'position:absolute'; // allow canvases to overlap
5467
+ mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
5468
+ if (glCanvas)
5469
+ glCanvas.style.cssText = styleCanvas;
5368
5470
  updateCanvas();
5369
5471
 
5370
5472
  // create promises for loading images
@@ -5381,19 +5483,32 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5381
5483
  })
5382
5484
  );
5383
5485
 
5384
- // draw splash screen
5385
- showSplashScreen && promises.push(new Promise(resolve =>
5486
+ if (!imageSources.length)
5386
5487
  {
5387
- let t = 0;
5388
- console.log(`${engineName} Engine v${engineVersion}`);
5389
- updateSplash();
5390
- function updateSplash()
5488
+ // no images to load
5489
+ promises.push(new Promise(resolve =>
5391
5490
  {
5392
- clearInput();
5393
- drawEngineSplashScreen(t+=.01);
5394
- t>1 ? resolve() : setTimeout(updateSplash, 16);
5395
- }
5396
- }));
5491
+ textureInfos[0] = new TextureInfo(new Image);
5492
+ resolve();
5493
+ }));
5494
+ }
5495
+
5496
+ if (showSplashScreen)
5497
+ {
5498
+ // draw splash screen
5499
+ promises.push(new Promise(resolve =>
5500
+ {
5501
+ let t = 0;
5502
+ console.log(`${engineName} Engine v${engineVersion}`);
5503
+ updateSplash();
5504
+ function updateSplash()
5505
+ {
5506
+ clearInput();
5507
+ drawEngineSplashScreen(t+=.01);
5508
+ t>1 ? resolve() : setTimeout(updateSplash, 16);
5509
+ }
5510
+ }));
5511
+ }
5397
5512
 
5398
5513
  // load all of the images
5399
5514
  Promise.all(promises).then(startEngine);
@@ -5665,7 +5780,7 @@ function drawEngineSplashScreen(t)
5665
5780
  x.restore();
5666
5781
  }
5667
5782
 
5668
- /**
5783
+ /**
5669
5784
  * LittleJS Module Export
5670
5785
  * - Export engine as a module
5671
5786
  */
@@ -5687,8 +5802,9 @@ export {
5687
5802
  engineObjectsUpdate,
5688
5803
  engineObjectsDestroy,
5689
5804
  engineObjectsCallback,
5805
+ engineObjectsRaycast,
5690
5806
  engineAddPlugin,
5691
-
5807
+
5692
5808
  // Globals
5693
5809
  debug,
5694
5810
  debugOverlay,
@@ -5807,6 +5923,7 @@ export {
5807
5923
  smoothStep,
5808
5924
  nearestPowerOfTwo,
5809
5925
  isOverlapping,
5926
+ isIntersecting,
5810
5927
  wave,
5811
5928
  formatTime,
5812
5929
 
@@ -5826,6 +5943,20 @@ export {
5826
5943
  vec2,
5827
5944
  rgb,
5828
5945
  hsl,
5946
+ isColor,
5947
+
5948
+ // Default Colors
5949
+ WHITE,
5950
+ BLACK,
5951
+ GRAY,
5952
+ RED,
5953
+ ORANGE,
5954
+ YELLOW,
5955
+ GREEN,
5956
+ CYAN,
5957
+ BLUE,
5958
+ PURPLE,
5959
+ MAGENTA,
5829
5960
 
5830
5961
  // Draw
5831
5962
  textureInfos,
@@ -5842,6 +5973,9 @@ export {
5842
5973
  drawTile,
5843
5974
  drawRect,
5844
5975
  drawLine,
5976
+ drawPoly,
5977
+ drawEllipse,
5978
+ drawCircle,
5845
5979
  drawCanvas2D,
5846
5980
  setBlendMode,
5847
5981
  drawTextScreen,
@@ -5862,6 +5996,17 @@ export {
5862
5996
  glDraw,
5863
5997
  glFlush,
5864
5998
  glSetTexture,
5999
+ glSetAntialias,
6000
+ glAntialias,
6001
+ glShader,
6002
+ glActiveTexture,
6003
+ glArrayBuffer,
6004
+ glGeometryBuffer,
6005
+ glPositionData,
6006
+ glColorData,
6007
+ glInstanceCount,
6008
+ glAdditive,
6009
+ glBatchAdditive,
5865
6010
 
5866
6011
  // Input
5867
6012
  keyIsDown,