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
@@ -122,11 +122,20 @@ class Sound
122
122
  this.gainNode.gain.value = volume;
123
123
  }
124
124
 
125
- /** Stop the last instance of this sound that was played */
126
- stop()
125
+ /** Stop the last instance of this sound that was played
126
+ * @param {number} [fadeTime] - How long to fade out (seconds)
127
+ */
128
+ stop(fadeTime=0)
127
129
  {
128
- if (this.source)
129
- this.source.stop();
130
+ if (!this.source)
131
+ return;
132
+
133
+ // ramp off gain
134
+ const startFade = audioContext.currentTime;
135
+ const endFade = startFade + fadeTime;
136
+ this.gainNode.gain.linearRampToValueAtTime(1, startFade);
137
+ this.gainNode.gain.linearRampToValueAtTime(0, endFade);
138
+ this.source.stop(endFade);
130
139
  this.source = undefined;
131
140
  }
132
141
 
@@ -414,7 +423,7 @@ function zzfxG
414
423
  repeatTime = repeatTime * sampleRate | 0;
415
424
 
416
425
  // generate waveform
417
- for(length = attack + decay + sustain + release + delay | 0;
426
+ for (length = attack + decay + sustain + release + delay | 0;
418
427
  i < length; b[i++] = s * volume) // sample
419
428
  {
420
429
  if (!(++crush%(bitCrush*100|0))) // bit crush
@@ -469,4 +478,4 @@ function zzfxG
469
478
  }
470
479
 
471
480
  return b; // return sample buffer
472
- }
481
+ }
@@ -1,2 +1,7 @@
1
1
  rem LittleJS Build Script
2
- call npm run build
2
+ call npm run build
3
+ if %errorlevel% neq 0 (
4
+ echo Build failed with error level %errorlevel%
5
+ pause
6
+ exit /b %errorlevel%
7
+ )
@@ -26,7 +26,6 @@ const engineSourceFiles =
26
26
  `${SOURCE_FOLDER}/engineParticles.js`,
27
27
  `${SOURCE_FOLDER}/engineMedals.js`,
28
28
  `${SOURCE_FOLDER}/engineWebGL.js`,
29
- `${SOURCE_FOLDER}/engine.js`,
30
29
  ];
31
30
  const enginePluginFiles =
32
31
  [
@@ -35,6 +34,7 @@ const enginePluginFiles =
35
34
  `${PLUGIN_FOLDER}/zzfxm.js`,
36
35
  `${PLUGIN_FOLDER}/uiSystem.js`,
37
36
  `${PLUGIN_FOLDER}/box2d.js`,
37
+ `${PLUGIN_FOLDER}/drawUtilities.js`,
38
38
  ];
39
39
  const engineExtraFiles =
40
40
  [
@@ -49,7 +49,7 @@ const asciiArt =`
49
49
  OOO OOO OO OO OO=OO-oo\\
50
50
  `;
51
51
  const license = '// LittleJS Engine - MIT License - Copyright 2021 Frank Force\n'+
52
- '// https://github.com/KilledByAPixel/LittleJS\n';
52
+ '// https://github.com/KilledByAPixel/LittleJS\n\n';
53
53
 
54
54
  console.log(asciiArt);
55
55
  console.log('Choo Choo... Building LittleJS Engine!\n');
@@ -75,6 +75,7 @@ Build
75
75
  'Build Engine -- all',
76
76
  `${BUILD_FOLDER}/${ENGINE_NAME}.js`,
77
77
  [
78
+ `${SOURCE_FOLDER}/engine.js`,
78
79
  `${SOURCE_FOLDER}/engineDebug.js`,
79
80
  ...engineSourceFiles,
80
81
  ...enginePluginFiles
@@ -87,6 +88,7 @@ Build
87
88
  'Build Engine -- release',
88
89
  `${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
89
90
  [
91
+ `${SOURCE_FOLDER}/engine.js`,
90
92
  `${SOURCE_FOLDER}/engineRelease.js`,
91
93
  ...engineSourceFiles,
92
94
  ...enginePluginFiles
@@ -99,7 +101,7 @@ Build
99
101
  'Build Engine -- minified',
100
102
  `${BUILD_FOLDER}/${ENGINE_NAME}.min.js`,
101
103
  [`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`],
102
- [closureCompilerStep, uglifyBuildStep]
104
+ [closureCompilerStep, uglifyBuildStep, addLicenseStep]
103
105
  );
104
106
 
105
107
  Build
@@ -123,7 +125,7 @@ Build
123
125
  `${SOURCE_FOLDER}/engineExport.js`,
124
126
  `${PLUGIN_FOLDER}/pluginExport.js`
125
127
  ],
126
- [uglifyBuildStep]
128
+ [uglifyBuildStep, addLicenseStep]
127
129
  );
128
130
 
129
131
  console.log(`Engine built in ${((Date.now() - startTime)/1e3).toFixed(2)} seconds!`);
@@ -141,7 +143,7 @@ function Build(message, outputFile, files=[], buildSteps=[], isPrimaryBuild)
141
143
  if (isPrimaryBuild)
142
144
  {
143
145
  // add license and strict mode to top
144
- buffer += license + '\n';
146
+ buffer += license;
145
147
  buffer += "'use strict';\n\n";
146
148
  }
147
149
 
@@ -189,6 +191,19 @@ function uglifyBuildStep(filename)
189
191
  catch (e) { handleError(e,'Failed to run Uglify minification step!'); }
190
192
  };
191
193
 
194
+ // Add license to top of file
195
+ function addLicenseStep(filename)
196
+ {
197
+ try
198
+ {
199
+ // add license to top of minified file
200
+ let fileContent = fs.readFileSync(filename, 'utf8');
201
+ fileContent = license + fileContent;
202
+ fs.writeFileSync(filename, fileContent);
203
+ }
204
+ catch (e) { handleError(e, 'Failed to add license to minified file!'); }
205
+ };
206
+
192
207
  // Build TypeScript definitions
193
208
  function typeScriptBuildStep(filename)
194
209
  {
@@ -47,19 +47,19 @@ let debugKey = 'Escape';
47
47
  let debugOverlay = false;
48
48
 
49
49
  // Engine internal variables not exposed to documentation
50
- let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugMedals = false, debugTakeScreenshot, downloadLink;
50
+ let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugMedals = false, debugTakeScreenshot, downloadLink, debugCanvas;
51
51
 
52
52
  ///////////////////////////////////////////////////////////////////////////////
53
53
  // Debug helper functions
54
54
 
55
55
  /** Asserts if the expression is false, does not do anything in release builds
56
- * @param {boolean} assert
57
- * @param {Object} [output]
56
+ * @param {boolean} assert
57
+ * @param {...Object} [output] - error message output
58
58
  * @memberof Debug */
59
- function ASSERT(assert, output)
59
+ function ASSERT(assert, ...output)
60
60
  {
61
61
  if (enableAsserts)
62
- output ? console.assert(assert, output) : console.assert(assert);
62
+ console.assert(assert, ...output);
63
63
  }
64
64
 
65
65
  /** Draw a debug rectangle in world space
@@ -132,16 +132,22 @@ function debugLine(posA, posB, color, thickness=.1, time)
132
132
  }
133
133
 
134
134
  /** Draw a debug combined axis aligned bounding box in world space
135
- * @param {Vector2} pA - position A
136
- * @param {Vector2} sA - size A
137
- * @param {Vector2} pB - position B
138
- * @param {Vector2} sB - size B
135
+ * @param {Vector2} posA
136
+ * @param {Vector2} sizeA
137
+ * @param {Vector2} posB
138
+ * @param {Vector2} sizeB
139
139
  * @param {string} [color]
140
140
  * @memberof Debug */
141
- function debugOverlap(pA, sA, pB, sB, color)
141
+ function debugOverlap(posA, sizeA, posB, sizeB, color)
142
142
  {
143
- const minPos = vec2(min(pA.x - sA.x/2, pB.x - sB.x/2), min(pA.y - sA.y/2, pB.y - sB.y/2));
144
- const maxPos = vec2(max(pA.x + sA.x/2, pB.x + sB.x/2), max(pA.y + sA.y/2, pB.y + sB.y/2));
143
+ const minPos = vec2(
144
+ min(posA.x - sizeA.x/2, posB.x - sizeB.x/2),
145
+ min(posA.y - sizeA.y/2, posB.y - sizeB.y/2)
146
+ );
147
+ const maxPos = vec2(
148
+ max(posA.x + sizeA.x/2, posB.x + sizeB.x/2),
149
+ max(posA.y + sizeA.y/2, posB.y + sizeB.y/2)
150
+ );
145
151
  debugRect(minPos.lerp(maxPos,.5), maxPos.subtract(minPos), color);
146
152
  }
147
153
 
@@ -169,12 +175,25 @@ function debugClear() { debugPrimitives = []; }
169
175
  function debugScreenshot() { debugTakeScreenshot = 1; }
170
176
 
171
177
  /** Save a canvas to disk
172
- * @param {HTMLCanvasElement} canvas
173
- * @param {string} [filename]
174
- * @param {string} [type]
178
+ * @param {HTMLCanvasElement|OffscreenCanvas} canvas
179
+ * @param {string} [filename]
180
+ * @param {string} [type]
175
181
  * @memberof Debug */
176
182
  function debugSaveCanvas(canvas, filename='screenshot', type='image/png')
177
- { debugSaveDataURL(canvas.toDataURL(type), filename); }
183
+ {
184
+ if (canvas instanceof OffscreenCanvas)
185
+ {
186
+ // copy to temporary canvas and save
187
+ if (!debugCanvas)
188
+ debugCanvas = document.createElement('canvas');
189
+ debugCanvas.width = canvas.width;
190
+ debugCanvas.height = canvas.height;
191
+ debugCanvas.getContext('2d').drawImage(canvas, 0, 0);
192
+ debugSaveDataURL(debugCanvas.toDataURL(type), filename);
193
+ }
194
+ else
195
+ debugSaveDataURL(canvas.toDataURL(type), filename);
196
+ }
178
197
 
179
198
  /** Save a text file to disk
180
199
  * @param {string} text
@@ -252,7 +271,8 @@ function debugRender()
252
271
  if (debugVideoCaptureIsActive())
253
272
  return; // don't show debug info when capturing video
254
273
 
255
- glCopyToContext(mainContext);
274
+ // flush any gl sprites before drawing debug info
275
+ glFlush();
256
276
 
257
277
  if (debugTakeScreenshot)
258
278
  {
@@ -329,8 +349,11 @@ function debugRender()
329
349
  }
330
350
  }
331
351
 
332
- if (tileCollisionLayers.length) // show floored tile pick if there is tile collision
333
- drawRect(mousePos.floor().add(vec2(.5)), vec2(1), rgb(0,0,1,.5), 0, false);
352
+ if (tileCollisionTest(mousePos))
353
+ {
354
+ // show floored tile pick for tile collision
355
+ drawRect(mousePos.floor().add(vec2(.5)), vec2(1), rgb(1,1,0,.5));
356
+ }
334
357
  mainContext = saveContext;
335
358
  }
336
359
 
@@ -405,10 +428,10 @@ function debugRender()
405
428
  mainContext = overlayContext;
406
429
  const raycastHitPos = tileCollisionRaycast(debugObject.pos, mousePos);
407
430
  raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), rgb(0,1,1,.3));
408
- drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5), false);
431
+ drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5));
409
432
 
410
433
  const debugText = 'mouse pos = ' + mousePos +
411
- '\nmouse collision = ' + getTileCollisionData(mousePos) +
434
+ '\nmouse collision = ' + tileCollisionGetData(mousePos) +
412
435
  '\n\n--- object info ---\n' +
413
436
  debugObject.toString();
414
437
  drawTextScreen(debugText, mousePosScreen, 24, rgb(), .05, undefined, 'center', 'monospace');
@@ -417,20 +440,24 @@ function debugRender()
417
440
 
418
441
  {
419
442
  // draw debug overlay
443
+ const fontSize = 20;
444
+ const lineHeight = fontSize * 1.2 | 0;
420
445
  overlayContext.save();
421
446
  overlayContext.fillStyle = '#fff';
422
447
  overlayContext.textAlign = 'left';
423
448
  overlayContext.textBaseline = 'top';
424
- overlayContext.font = '28px monospace';
449
+ overlayContext.font = fontSize + 'px monospace';
425
450
  overlayContext.shadowColor = '#000';
426
451
  overlayContext.shadowBlur = 9;
427
452
 
428
- let x = 9, y = -20, h = 30;
453
+ let x = 9, y = 0, h = lineHeight;
429
454
  if (debugOverlay)
430
455
  {
431
- overlayContext.fillText(engineName, x, y += h);
432
- overlayContext.fillText('Objects: ' + engineObjects.length, x, y += h);
456
+ overlayContext.fillText(engineName, x, y += h/2 );
433
457
  overlayContext.fillText('Time: ' + formatTime(time), x, y += h);
458
+ overlayContext.fillText('FPS: ' + averageFPS.toFixed(1), x, y += h);
459
+ overlayContext.fillText('Objects: ' + engineObjects.length, x, y += h);
460
+ overlayContext.fillText('Draw Count: ' + drawCount, x, y += h);
434
461
  overlayContext.fillText('---------', x, y += h);
435
462
  overlayContext.fillStyle = '#f00';
436
463
  overlayContext.fillText('ESC: Debug Overlay', x, y += h);
@@ -444,10 +471,10 @@ function debugRender()
444
471
  overlayContext.fillText('4: Debug Raycasts', x, y += h);
445
472
  overlayContext.fillStyle = '#fff';
446
473
  overlayContext.fillText('5: Save Screenshot', x, y += h);
447
- overlayContext.fillText('6: Capture Video', x, y += h);
474
+ overlayContext.fillText('6: Toggle Video Capture', x, y += h);
448
475
 
449
476
  let keysPressed = '';
450
- for(const i in inputData[0])
477
+ for (const i in inputData[0])
451
478
  {
452
479
  if (keyIsDown(i, 0))
453
480
  keysPressed += i + ' ' ;
@@ -456,7 +483,7 @@ function debugRender()
456
483
 
457
484
  let buttonsPressed = '';
458
485
  if (inputData[1])
459
- for(const i in inputData[1])
486
+ for (const i in inputData[1])
460
487
  {
461
488
  if (keyIsDown(i, 1))
462
489
  buttonsPressed += i + ' ' ;