littlejsengine 1.18.24 β†’ 1.18.25

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.
package/README.md CHANGED
@@ -23,6 +23,8 @@ The code is very clean and well documented with many examples to get you started
23
23
 
24
24
  *The Third Annual LittleJS Game Jam will take place From Oct 2 to Nov 2! Unleash your creativity and develop amazing games using the LittleJS game engine.*
25
25
 
26
+ ### πŸ˜ΌπŸ‘ [LittleJS + JS13k](https://github.com/KilledByAPixel/LittleJS/tree/js13k) - We made a special branch designed for size coding events like JS13k.
27
+
26
28
  <div align='center' markdown='1'>
27
29
 
28
30
  ## [Demos](https://killedbyapixel.github.io/LittleJS/examples) | [Arcade](https://killedbyapixel.github.io/LittleJSArcade) | [Docs](https://killedbyapixel.github.io/LittleJS/docs) | [FAQ](https://github.com/KilledByAPixel/LittleJS/blob/main/FAQ.md) | [Trailer](https://youtu.be/chuBzGjv7Ms) | [Discord](https://discord.gg/zb7hcGkyZe)
@@ -38,6 +40,7 @@ The code is very clean and well documented with many examples to get you started
38
40
  - Blazing fast WebGL2 + Canvas2D hybrid rendering system
39
41
  - Apply [Shadertoy](https://www.shadertoy.com) style shaders for post-processing effects
40
42
  - Robust particle effect system and [effect design tool](https://killedbyapixel.github.io/LittleJS/examples/particles/)
43
+ - Load sprites and animations into texture sheets at runtime, or import [TexturePacker](https://www.codeandweb.com/texturepacker) and [Aseprite](https://www.aseprite.org) atlases
41
44
  - Optional 3D rendering with the [Three.js](https://threejs.org) plugin
42
45
 
43
46
  ### πŸ”Š Audio
@@ -146,6 +146,17 @@ declare module "littlejsengine" {
146
146
  * );
147
147
  * @memberof Engine */
148
148
  export function engineInit(gameInit: GameInitCallback, gameUpdate: GameCallback, gameUpdatePost: GameCallback, gameRender: GameCallback, gameRenderPost: GameCallback, imageSources?: Array<string>, rootElement?: HTMLElement): Promise<void>;
149
+ /** Advance the engine by a number of frames
150
+ * Requires setEngineManualStep(true) before engineInit
151
+ * Respects paused exactly as the normal update loop does
152
+ * @param {number} [frames] - number of engine update ticks, max 36000, each running one fixed update at timeScale 1
153
+ * @example
154
+ * setHeadlessMode(true);
155
+ * setEngineManualStep(true);
156
+ * await engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost);
157
+ * engineStep(600); // advance 10 seconds of game time
158
+ * @memberof Engine */
159
+ export function engineStep(frames?: number): void;
149
160
  /** Update each engine object, remove destroyed objects, and update time
150
161
  * can be called manually if objects need to be updated outside of main loop
151
162
  * @memberof Engine */
@@ -417,6 +428,12 @@ declare module "littlejsengine" {
417
428
  * @default
418
429
  * @memberof Settings */
419
430
  export let headlessMode: boolean;
431
+ /** Disables the automatic requestAnimationFrame loop so the engine only
432
+ * advances when engineStep is called, for tests and frame-stepping tools
433
+ * @type {boolean}
434
+ * @default
435
+ * @memberof Settings */
436
+ export let engineManualStep: boolean;
420
437
  /** Default size of tiles in pixels
421
438
  * @type {Vector2}
422
439
  * @default Vector2(16,16)
@@ -690,6 +707,11 @@ declare module "littlejsengine" {
690
707
  * @param {boolean} headless
691
708
  * @memberof Settings */
692
709
  export function setHeadlessMode(headless: boolean): void;
710
+ /** Set if the engine only advances when engineStep is called
711
+ * Must be set before engineInit
712
+ * @param {boolean} [enable]
713
+ * @memberof Settings */
714
+ export function setEngineManualStep(enable?: boolean): void;
693
715
  /** Set if WebGL rendering is enabled
694
716
  * @param {boolean} enable
695
717
  * @memberof Settings */
@@ -1087,7 +1109,7 @@ declare module "littlejsengine" {
1087
1109
  export function shareURL(title: string, url: string, callback?: Function): void;
1088
1110
  /** Read save data from local storage
1089
1111
  * @param {string} saveName - unique name for the game/save
1090
- * @param {Object} [defaultSaveData] - default values for save
1112
+ * @param {Object} [defaultSaveData] - default values, result is {...default, ...loaded} so this must be an object
1091
1113
  * @return {Object}
1092
1114
  * @memberof Utilities */
1093
1115
  export function readSaveData(saveName: string, defaultSaveData?: any): any;
@@ -35,7 +35,7 @@ const engineName = 'LittleJS';
35
35
  * @type {string}
36
36
  * @default
37
37
  * @memberof Engine */
38
- const engineVersion = '1.18.24';
38
+ const engineVersion = '1.18.25';
39
39
 
40
40
  /** Frames per second to update
41
41
  * @type {number}
@@ -92,6 +92,7 @@ function setPaused(isPaused=true) { paused = isPaused; }
92
92
 
93
93
  // Engine internal variables
94
94
  let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
95
+ let engineUpdateInternal; // assigned by engineInit so engineStep can drive it
95
96
  let showEngineVersion = true;
96
97
 
97
98
  ///////////////////////////////////////////////////////////////////////////////
@@ -280,7 +281,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
280
281
 
281
282
  if (!debugVideoCaptureIsActive())
282
283
  renderFrame();
283
- requestAnimationFrame(engineUpdate);
284
+ if (!engineManualStep)
285
+ requestAnimationFrame(engineUpdate);
284
286
 
285
287
  function renderFrame()
286
288
  {
@@ -308,6 +310,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
308
310
  primitiveCount = 0;
309
311
  }
310
312
  }
313
+ engineUpdateInternal = engineUpdate;
311
314
 
312
315
  function updateCanvas()
313
316
  {
@@ -462,10 +465,39 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
462
465
  {
463
466
  // wait for gameInit to load
464
467
  await gameInit();
465
- engineUpdate();
468
+ engineManualStep || engineUpdate();
466
469
  }
467
470
  }
468
471
 
472
+ // max frames engineStep can advance in one call, 10 minutes at 60fps
473
+ // large counts block until they finish, so this catches runaway values
474
+ const engineStepMaxFrames = 36000;
475
+
476
+ /** Advance the engine by a number of frames
477
+ * Requires setEngineManualStep(true) before engineInit
478
+ * Respects paused exactly as the normal update loop does
479
+ * @param {number} [frames] - number of engine update ticks, max 36000, each running one fixed update at timeScale 1
480
+ * @example
481
+ * setHeadlessMode(true);
482
+ * setEngineManualStep(true);
483
+ * await engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost);
484
+ * engineStep(600); // advance 10 seconds of game time
485
+ * @memberof Engine */
486
+ function engineStep(frames=1)
487
+ {
488
+ ASSERT(engineManualStep,
489
+ 'engineStep requires setEngineManualStep(true) before engineInit');
490
+ ASSERT(engineUpdateInternal, 'engineStep requires engineInit to complete');
491
+ // runtime guard so release builds (where the asserts are stripped) can't
492
+ // start a second requestAnimationFrame chain or call an undefined update
493
+ if (!engineManualStep || !engineUpdateInternal) return;
494
+ ASSERT(Number.isInteger(frames) && frames >= 0 && frames <= engineStepMaxFrames,
495
+ 'engineStep requires a whole frame count from 0 to ' + engineStepMaxFrames);
496
+ frames = min(frames, engineStepMaxFrames); // release has no asserts, don't freeze
497
+ for (let i = frames; i > 0; --i)
498
+ engineUpdateInternal(frameTimeLastMS + 1e3 / frameRate);
499
+ }
500
+
469
501
  /** Update each engine object, remove destroyed objects, and update time
470
502
  * can be called manually if objects need to be updated outside of main loop
471
503
  * @memberof Engine */
@@ -2688,12 +2720,17 @@ function shareURL(title, url, callback)
2688
2720
 
2689
2721
  /** Read save data from local storage
2690
2722
  * @param {string} saveName - unique name for the game/save
2691
- * @param {Object} [defaultSaveData] - default values for save
2723
+ * @param {Object} [defaultSaveData] - default values, result is {...default, ...loaded} so this must be an object
2692
2724
  * @return {Object}
2693
2725
  * @memberof Utilities */
2694
2726
  function readSaveData(saveName, defaultSaveData)
2695
2727
  {
2696
- ASSERT(isStringLike(saveName), 'loadData requires saveName string');
2728
+ ASSERT(isStringLike(saveName), 'readSaveData requires saveName string');
2729
+ ASSERT(defaultSaveData === undefined ||
2730
+ (typeof defaultSaveData === 'object' && defaultSaveData !== null),
2731
+ 'readSaveData: default must be an object - the result is ' +
2732
+ '{...default, ...loaded}, so a scalar default yields {}. ' +
2733
+ 'Use readSaveData(key, {best:0}).best');
2697
2734
 
2698
2735
  // tolerate localStorage being unavailable (iOS private mode, sandboxed
2699
2736
  // iframes) and corrupt JSON in stored data
@@ -2717,7 +2754,7 @@ function readSaveData(saveName, defaultSaveData)
2717
2754
  * @memberof Utilities */
2718
2755
  function writeSaveData(saveName, saveData)
2719
2756
  {
2720
- ASSERT(isStringLike(saveName), 'saveData requires saveName string');
2757
+ ASSERT(isStringLike(saveName), 'writeSaveData requires saveName string');
2721
2758
  // tolerate localStorage being unavailable or quota exceeded
2722
2759
  try { localStorage[saveName] = JSON.stringify(saveData); }
2723
2760
  catch { LOG('writeSaveData: failed to write', saveName); }
@@ -2885,6 +2922,13 @@ let showSplashScreen = false;
2885
2922
  * @memberof Settings */
2886
2923
  let headlessMode = false;
2887
2924
 
2925
+ /** Disables the automatic requestAnimationFrame loop so the engine only
2926
+ * advances when engineStep is called, for tests and frame-stepping tools
2927
+ * @type {boolean}
2928
+ * @default
2929
+ * @memberof Settings */
2930
+ let engineManualStep = false;
2931
+
2888
2932
  ///////////////////////////////////////////////////////////////////////////////
2889
2933
  // WebGL settings
2890
2934
 
@@ -3235,6 +3279,12 @@ function setShowSplashScreen(show) { showSplashScreen = show; }
3235
3279
  * @memberof Settings */
3236
3280
  function setHeadlessMode(headless) { headlessMode = headless; }
3237
3281
 
3282
+ /** Set if the engine only advances when engineStep is called
3283
+ * Must be set before engineInit
3284
+ * @param {boolean} [enable]
3285
+ * @memberof Settings */
3286
+ function setEngineManualStep(enable=true) { engineManualStep = enable; }
3287
+
3238
3288
  /** Set if WebGL rendering is enabled
3239
3289
  * @param {boolean} enable
3240
3290
  * @memberof Settings */
@@ -16871,6 +16921,7 @@ export
16871
16921
  getPaused,
16872
16922
  setPaused,
16873
16923
  engineInit,
16924
+ engineStep,
16874
16925
  engineObjectsUpdate,
16875
16926
  engineObjectsDestroy,
16876
16927
  engineObjectsCollect,
@@ -16918,6 +16969,7 @@ export
16918
16969
  fontDefault,
16919
16970
  showSplashScreen,
16920
16971
  headlessMode,
16972
+ engineManualStep,
16921
16973
  tileDefaultSize,
16922
16974
  tileDefaultPadding,
16923
16975
  tileDefaultBleed,
@@ -16972,6 +17024,7 @@ export
16972
17024
  setFontDefault,
16973
17025
  setShowSplashScreen,
16974
17026
  setHeadlessMode,
17027
+ setEngineManualStep,
16975
17028
  setGLEnable,
16976
17029
  setTileDefaultSize,
16977
17030
  setTileDefaultPadding,