littlejsengine 1.6.0 → 1.6.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 (49) hide show
  1. package/README.md +133 -33
  2. package/build/littlejs.d.ts +13 -5
  3. package/build/littlejs.esm.js +106 -95
  4. package/build/littlejs.esm.min.js +1 -1
  5. package/build/littlejs.js +98 -93
  6. package/build/littlejs.min.js +1 -1
  7. package/build/littlejs.release.js +91 -91
  8. package/examples/breakout/game.js +5 -0
  9. package/examples/breakout/gameObjects.js +41 -48
  10. package/examples/breakout/index.html +3 -3
  11. package/examples/breakoutTutorial/README.md +514 -0
  12. package/examples/breakoutTutorial/game.js +181 -0
  13. package/examples/breakoutTutorial/images/1.png +0 -0
  14. package/examples/breakoutTutorial/images/10.png +0 -0
  15. package/examples/breakoutTutorial/images/11.png +0 -0
  16. package/examples/breakoutTutorial/images/2.png +0 -0
  17. package/examples/breakoutTutorial/images/3.png +0 -0
  18. package/examples/breakoutTutorial/images/4.png +0 -0
  19. package/examples/breakoutTutorial/images/5.png +0 -0
  20. package/examples/breakoutTutorial/images/6.png +0 -0
  21. package/examples/breakoutTutorial/images/7.png +0 -0
  22. package/examples/breakoutTutorial/images/8.png +0 -0
  23. package/examples/breakoutTutorial/images/9.png +0 -0
  24. package/examples/breakoutTutorial/index.html +10 -0
  25. package/examples/empty/game.js +10 -0
  26. package/examples/module/index.html +1 -1
  27. package/examples/particles/index.html +1 -1
  28. package/examples/platformer/gameLevel.js +1 -1
  29. package/examples/platformer/gamePlayer.js +1 -1
  30. package/examples/platformer/index.html +6 -6
  31. package/examples/puzzle/index.html +2 -2
  32. package/examples/starter/build.bat +3 -2
  33. package/examples/starter/game.js +9 -9
  34. package/examples/starter/index.html +13 -13
  35. package/examples/stress/index.html +1 -1
  36. package/examples/typescript/game.js +89 -89
  37. package/examples/typescript/game.ts +10 -10
  38. package/examples/typescript/index.html +1 -1
  39. package/package.json +3 -3
  40. package/src/engine.js +1 -2
  41. package/src/engineAudio.js +11 -11
  42. package/src/engineBuild.bat +3 -1
  43. package/src/engineDebug.js +8 -2
  44. package/src/engineDraw.js +19 -15
  45. package/src/engineExport.js +8 -2
  46. package/src/engineInput.js +51 -55
  47. package/src/engineObject.js +4 -4
  48. package/src/engineRelease.js +1 -0
  49. package/src/engineUtilities.js +4 -4
@@ -45,6 +45,12 @@ let showWatermark = 1;
45
45
  * @memberof Debug */
46
46
  let godMode = 0;
47
47
 
48
+ /** Key code used to toggle debug mode, Esc by default
49
+ * @type {Boolean}
50
+ * @default
51
+ * @memberof Debug */
52
+ let debugKey = 27;
53
+
48
54
  // Engine internal variables not exposed to documentation
49
55
  let debugPrimitives = [], debugOverlay = 0, debugPhysics = 0, debugRaycast = 0,
50
56
  debugParticles = 0, debugGamepads = 0, debugMedals = 0, debugTakeScreenshot, downloadLink;
@@ -166,7 +172,7 @@ const debugUpdate = ()=>
166
172
  if (!debug)
167
173
  return;
168
174
 
169
- if (keyWasPressed(192)) // ~
175
+ if (keyWasPressed(debugKey)) // Esc
170
176
  debugOverlay = !debugOverlay;
171
177
  if (debugOverlay)
172
178
  {
@@ -351,7 +357,7 @@ const debugRender = ()=>
351
357
  overlayContext.fillText('Time: ' + formatTime(time), x, y += h);
352
358
  overlayContext.fillText('---------', x, y += h);
353
359
  overlayContext.fillStyle = '#f00';
354
- overlayContext.fillText('~: Debug Overlay', x, y += h);
360
+ overlayContext.fillText('ESC: Debug Overlay', x, y += h);
355
361
  overlayContext.fillStyle = debugPhysics ? '#f00' : '#fff';
356
362
  overlayContext.fillText('1: Debug Physics', x, y += h);
357
363
  overlayContext.fillStyle = debugParticles ? '#f00' : '#fff';
@@ -741,7 +747,7 @@ class Vector2
741
747
  * @return {Color}
742
748
  * @memberof Utilities
743
749
  */
744
- const colorRGBA = (r, g, b, a)=> new Color(r, g, b, a);
750
+ const rgb = (r, g, b, a)=> new Color(r, g, b, a);
745
751
 
746
752
  /**
747
753
  * Create a color object with HSLA values
@@ -752,7 +758,7 @@ const colorRGBA = (r, g, b, a)=> new Color(r, g, b, a);
752
758
  * @return {Color}
753
759
  * @memberof Utilities
754
760
  */
755
- const colorHSLA = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
761
+ const hsl = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
756
762
 
757
763
  /**
758
764
  * Color object (red, green, blue, alpha) with some helpful functions
@@ -760,8 +766,8 @@ const colorHSLA = (h, s, l, a)=> new Color().setHSLA(h, s, l, a);
760
766
  * let a = new Color; // white
761
767
  * let b = new Color(1, 0, 0); // red
762
768
  * let c = new Color(0, 0, 0, 0); // transparent black
763
- * let d = colorRGBA(0, 0, 1); // blue using rgb color
764
- * let e = colorHSLA(.3, 1, .5); // green using hsl color
769
+ * let d = RGB(0, 0, 1); // blue using rgb color
770
+ * let e = HSL(.3, 1, .5); // green using hsl color
765
771
  */
766
772
  class Color
767
773
  {
@@ -1336,6 +1342,7 @@ class EngineObject
1336
1342
  this.velocity.y += gravity * this.gravityScale;
1337
1343
  this.pos.x += this.velocity.x *= this.damping;
1338
1344
  this.pos.y += this.velocity.y *= this.damping;
1345
+ this.angle += this.angleVelocity *= this.angleDamping;
1339
1346
 
1340
1347
  // physics sanity checks
1341
1348
  ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
@@ -1392,6 +1399,7 @@ class EngineObject
1392
1399
  const smallStepUp = (oldPos.y - o.pos.y)*2 > sizeBoth.y + gravity; // prefer to push up if small delta
1393
1400
  const isBlockedX = abs(oldPos.y - o.pos.y)*2 < sizeBoth.y;
1394
1401
  const isBlockedY = abs(oldPos.x - o.pos.x)*2 < sizeBoth.x;
1402
+ const elasticity = max(this.elasticity, o.elasticity);
1395
1403
 
1396
1404
  if (smallStepUp | isBlockedY | !isBlockedX) // resolve y collision
1397
1405
  {
@@ -1404,7 +1412,7 @@ class EngineObject
1404
1412
  this.groundObject = o;
1405
1413
 
1406
1414
  // bounce if other object is fixed or grounded
1407
- this.velocity.y *= -this.elasticity;
1415
+ this.velocity.y *= -elasticity;
1408
1416
  }
1409
1417
  else if (o.mass)
1410
1418
  {
@@ -1418,7 +1426,6 @@ class EngineObject
1418
1426
  + this.velocity.y * 2 * this.mass / (this.mass + o.mass);
1419
1427
 
1420
1428
  // lerp betwen elastic or inelastic based on elasticity
1421
- const elasticity = max(this.elasticity, o.elasticity);
1422
1429
  this.velocity.y = lerp(elasticity, inelastic, elastic0);
1423
1430
  o.velocity.y = lerp(elasticity, inelastic, elastic1);
1424
1431
  }
@@ -1439,12 +1446,11 @@ class EngineObject
1439
1446
  + this.velocity.x * 2 * this.mass / (this.mass + o.mass);
1440
1447
 
1441
1448
  // lerp betwen elastic or inelastic based on elasticity
1442
- const elasticity = max(this.elasticity, o.elasticity);
1443
1449
  this.velocity.x = lerp(elasticity, inelastic, elastic0);
1444
1450
  o.velocity.x = lerp(elasticity, inelastic, elastic1);
1445
1451
  }
1446
1452
  else // bounce if other object is fixed
1447
- this.velocity.x *= -this.elasticity;
1453
+ this.velocity.x *= -elasticity;
1448
1454
  }
1449
1455
  debugOverlay && debugPhysics && debugAABB(this.pos, this.size, o.pos, o.size, '#f0f');
1450
1456
  }
@@ -1826,6 +1832,23 @@ function setBlendMode(additive, useWebGL=glEnable)
1826
1832
  mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
1827
1833
  }
1828
1834
 
1835
+ /** Draw text on overlay canvas in world space
1836
+ * Automatically splits new lines into rows
1837
+ * @param {String} text
1838
+ * @param {Vector2} pos
1839
+ * @param {Number} [size=1]
1840
+ * @param {Color} [color=Color()]
1841
+ * @param {Number} [lineWidth=0]
1842
+ * @param {Color} [lineColor=Color(0,0,0)]
1843
+ * @param {String} [textAlign='center']
1844
+ * @param {String} [font=fontDefault]
1845
+ * @param {CanvasRenderingContext2D} [context=overlayContext]
1846
+ * @memberof Draw */
1847
+ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context)
1848
+ {
1849
+ drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context);
1850
+ }
1851
+
1829
1852
  /** Draw text on overlay canvas in screen space
1830
1853
  * Automatically splits new lines into rows
1831
1854
  * @param {String} text
@@ -1835,6 +1858,8 @@ function setBlendMode(additive, useWebGL=glEnable)
1835
1858
  * @param {Number} [lineWidth=0]
1836
1859
  * @param {Color} [lineColor=Color(0,0,0)]
1837
1860
  * @param {String} [textAlign='center']
1861
+ * @param {String} [font=fontDefault]
1862
+ * @param {CanvasRenderingContext2D} [context=overlayContext]
1838
1863
  * @memberof Draw */
1839
1864
  function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
1840
1865
  {
@@ -1855,21 +1880,6 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
1855
1880
  });
1856
1881
  }
1857
1882
 
1858
- /** Draw text on overlay canvas in world space
1859
- * Automatically splits new lines into rows
1860
- * @param {String} text
1861
- * @param {Vector2} pos
1862
- * @param {Number} [size=1]
1863
- * @param {Color} [color=Color()]
1864
- * @param {Number} [lineWidth=0]
1865
- * @param {Color} [lineColor=Color(0,0,0)]
1866
- * @param {String} [textAlign='center']
1867
- * @memberof Draw */
1868
- function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font)
1869
- {
1870
- drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, mainContext);
1871
- }
1872
-
1873
1883
  ///////////////////////////////////////////////////////////////////////////////
1874
1884
 
1875
1885
  let engineFontImage;
@@ -2273,6 +2283,9 @@ if (isTouchDevice)
2273
2283
  return true;
2274
2284
  }
2275
2285
 
2286
+ // try to create touch game pad
2287
+ touchGamepadEnable && touchGamepadCreate();
2288
+
2276
2289
  return ontouchstart(e);
2277
2290
  }
2278
2291
  }
@@ -2286,76 +2299,69 @@ let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
2286
2299
  // create the touch gamepad, called automatically by the engine
2287
2300
  function touchGamepadCreate()
2288
2301
  {
2289
- if (!touchGamepadEnable || !isTouchDevice)
2290
- return;
2291
-
2292
2302
  // touch input internal variables
2293
2303
  touchGamepadButtons = [];
2294
2304
  touchGamepadStick = vec2();
2295
2305
 
2296
- // setup touch input
2297
- ontouchstart = (e)=>
2306
+ let touchHandler = ontouchstart;
2307
+ ontouchstart = ontouchmove = ontouchend = (e)=>
2298
2308
  {
2299
- // fix mobile audio, force it to play a sound on first touch
2300
- zzfx(0);
2301
-
2302
- ontouchstart = ontouchmove = ontouchend = (e)=>
2309
+ // clear touch gamepad input
2310
+ touchGamepadStick = vec2();
2311
+ touchGamepadButtons = [];
2312
+
2313
+ const touching = e.touches.length;
2314
+ if (touching)
2303
2315
  {
2304
- // clear touch gamepad input
2305
- touchGamepadStick = vec2();
2306
- touchGamepadButtons = [];
2307
-
2308
- const touching = e.touches.length;
2309
- if (touching)
2316
+ touchGamepadTimer.set();
2317
+ if (paused)
2310
2318
  {
2311
- // set that gamepad is active
2312
- isUsingGamepad = 1;
2313
- touchGamepadTimer.set();
2314
-
2315
- if (paused)
2316
- {
2317
- // touch anywhere to press start when paused
2318
- touchGamepadButtons[9] = 1;
2319
- return;
2320
- }
2319
+ // touch anywhere to press start when paused
2320
+ touchGamepadButtons[9] = 1;
2321
+ return;
2321
2322
  }
2323
+ }
2322
2324
 
2323
- // get center of left and right sides
2324
- const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
2325
- const buttonCenter = mainCanvasSize.subtract(vec2(touchGamepadSize, touchGamepadSize));
2326
- const startCenter = mainCanvasSize.scale(.5);
2325
+ // get center of left and right sides
2326
+ const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
2327
+ const buttonCenter = mainCanvasSize.subtract(vec2(touchGamepadSize, touchGamepadSize));
2328
+ const startCenter = mainCanvasSize.scale(.5);
2327
2329
 
2328
- // check each touch point
2329
- for (const touch of e.touches)
2330
+ // check each touch point
2331
+ for (const touch of e.touches)
2332
+ {
2333
+ const touchPos = mouseToScreen(vec2(touch.clientX, touch.clientY));
2334
+ if (touchPos.distance(stickCenter) < touchGamepadSize)
2330
2335
  {
2331
- const touchPos = mouseToScreen(vec2(touch.clientX, touch.clientY));
2332
- if (touchPos.distance(stickCenter) < touchGamepadSize)
2336
+ // virtual analog stick
2337
+ if (touchGamepadAnalog)
2338
+ touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
2339
+ else
2333
2340
  {
2334
- // virtual analog stick
2335
- if (touchGamepadAnalog)
2336
- touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
2337
- else
2338
- {
2339
- // 8 way dpad
2340
- const angle = touchPos.subtract(stickCenter).angle();
2341
- touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
2342
- }
2343
- }
2344
- else if (touchPos.distance(buttonCenter) < touchGamepadSize)
2345
- {
2346
- // virtual face buttons
2347
- const button = touchPos.subtract(buttonCenter).direction();
2348
- touchGamepadButtons[button] = 1;
2349
- }
2350
- else if (touchPos.distance(startCenter) < touchGamepadSize)
2351
- {
2352
- // virtual start button in center
2353
- touchGamepadButtons[9] = 1;
2341
+ // 8 way dpad
2342
+ const angle = touchPos.subtract(stickCenter).angle();
2343
+ touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
2354
2344
  }
2355
2345
  }
2346
+ else if (touchPos.distance(buttonCenter) < touchGamepadSize)
2347
+ {
2348
+ // virtual face buttons
2349
+ const button = touchPos.subtract(buttonCenter).direction();
2350
+ touchGamepadButtons[button] = 1;
2351
+ }
2352
+ else if (touchPos.distance(startCenter) < touchGamepadSize)
2353
+ {
2354
+ // virtual start button in center
2355
+ touchGamepadButtons[9] = 1;
2356
+ }
2356
2357
  }
2357
2358
 
2358
- return ontouchstart(e);
2359
+ // call default touch handler and set to using gamepad
2360
+ touchHandler(e);
2361
+ isUsingGamepad = 1;
2362
+
2363
+ // must return true so the document will get focus
2364
+ return true;
2359
2365
  }
2360
2366
  }
2361
2367
 
@@ -2381,7 +2387,7 @@ function touchGamepadRender()
2381
2387
  overlayContext.beginPath();
2382
2388
 
2383
2389
  const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
2384
- if (touchGamepadAnalog)
2390
+ if (touchGamepadAnalog) // draw circle shaped gamepad
2385
2391
  {
2386
2392
  overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
2387
2393
  overlayContext.fill();
@@ -2686,7 +2692,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
2686
2692
  }
2687
2693
 
2688
2694
  ///////////////////////////////////////////////////////////////////////////////
2689
- // ZzFXMicro - Zuper Zmall Zound Zynth - v1.1.8 by Frank Force
2695
+ // ZzFXMicro - Zuper Zmall Zound Zynth - v1.2.0 by Frank Force
2690
2696
 
2691
2697
  /** Generate and play a ZzFX sound
2692
2698
  * <br>
@@ -2712,11 +2718,11 @@ function zzfxG
2712
2718
  bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0
2713
2719
  )
2714
2720
  {
2715
- // init parameters
2721
+ // locals
2716
2722
  let PI2 = PI*2, startSlide = slide *= 500 * PI2 / zzfxR / zzfxR, b=[],
2717
2723
  startFrequency = frequency *= (1 + randomness*rand(-1,1)) * PI2 / zzfxR,
2718
- t=0, tm=0, i=0, j=1, r=0, c=0, s=0, f, length;
2719
-
2724
+ t=0, tm=0, i=0, j=1, r=0, c=0, s=0, f, length
2725
+
2720
2726
  // scale by sample rate
2721
2727
  attack = attack * zzfxR + 9; // minimum attack to prevent pop
2722
2728
  decay *= zzfxR;
@@ -2766,18 +2772,18 @@ function zzfxG
2766
2772
  Math.cos(modulation*tm++); // modulation
2767
2773
  t += f - f*noise*(1 - (Math.sin(i)+1)*1e9%2); // noise
2768
2774
 
2769
- if (j && ++j > pitchJumpTime) // pitch jump
2775
+ if (j && ++j > pitchJumpTime) // pitch jump
2770
2776
  {
2771
- frequency += pitchJump; // apply pitch jump
2772
- startFrequency += pitchJump; // also apply to start
2773
- j = 0; // reset pitch jump time
2777
+ frequency += pitchJump; // apply pitch jump
2778
+ startFrequency += pitchJump; // also apply to start
2779
+ j = 0; // reset pitch jump time
2774
2780
  }
2775
2781
 
2776
2782
  if (repeatTime && !(++r % repeatTime)) // repeat
2777
2783
  {
2778
- frequency = startFrequency; // reset frequency
2779
- slide = startSlide; // reset slide
2780
- j = j || 1; // reset pitch jump time
2784
+ frequency = startFrequency; // reset frequency
2785
+ slide = startSlide; // reset slide
2786
+ j ||= 1; // reset pitch jump time
2781
2787
  }
2782
2788
  }
2783
2789
 
@@ -4269,7 +4275,7 @@ const engineName = 'LittleJS';
4269
4275
  * @type {String}
4270
4276
  * @default
4271
4277
  * @memberof Engine */
4272
- const engineVersion = '1.6.0';
4278
+ const engineVersion = '1.6.4';
4273
4279
 
4274
4280
  /** Frames per second to update objects
4275
4281
  * @type {Number}
@@ -4360,7 +4366,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4360
4366
  (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
4361
4367
 
4362
4368
  gameInit();
4363
- touchGamepadCreate();
4364
4369
  engineUpdate();
4365
4370
  };
4366
4371
 
@@ -4756,6 +4761,11 @@ const setShowWatermark = (show)=> showWatermark = show;
4756
4761
  * @memberof Debug */
4757
4762
  const setGodMode = (enable)=> godMode = enable;
4758
4763
 
4764
+ /** Set key code used to toggle debug mode, Esc by default
4765
+ * @param {Number} key
4766
+ * @memberof Debug */
4767
+ const setDebugKey = (key)=> debugKey = key;
4768
+
4759
4769
  export {
4760
4770
  // Setters for global variables
4761
4771
  setCameraPos,
@@ -4796,6 +4806,7 @@ export {
4796
4806
  setMedalsPreventUnlock,
4797
4807
  setShowWatermark,
4798
4808
  setGodMode,
4809
+ setDebugKey,
4799
4810
 
4800
4811
  // Settings
4801
4812
  canvasMaxSize,
@@ -4882,8 +4893,8 @@ export {
4882
4893
  Color,
4883
4894
  Timer,
4884
4895
  vec2,
4885
- colorRGBA,
4886
- colorHSLA,
4896
+ rgb,
4897
+ hsl,
4887
4898
 
4888
4899
  // Base
4889
4900
  EngineObject,