littlejsengine 1.12.4 → 1.12.6

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/dist/littlejs.js CHANGED
@@ -13,8 +13,6 @@
13
13
  * @namespace Debug
14
14
  */
15
15
 
16
-
17
-
18
16
  /** True if debug is enabled
19
17
  * @type {boolean}
20
18
  * @default
@@ -579,8 +577,6 @@ function debugVideoCaptureUpdate()
579
577
  * @namespace Utilities
580
578
  */
581
579
 
582
-
583
-
584
580
  /** A shortcut to get Math.PI
585
581
  * @type {number}
586
582
  * @default Math.PI
@@ -760,6 +756,16 @@ function wave(frequency=1, amplitude=1, t=time)
760
756
  * @memberof Utilities */
761
757
  function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
762
758
 
759
+ /** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
760
+ * @param {string} url - URL of JSON file
761
+ * @return {Promise<object>}
762
+ * @memberof Utilities */
763
+ async function fetchJSON(url)
764
+ {
765
+ const response = await fetch(url);
766
+ return response.json();
767
+ }
768
+
763
769
  ///////////////////////////////////////////////////////////////////////////////
764
770
 
765
771
  /** Random global functions
@@ -855,6 +861,12 @@ class RandomGenerator
855
861
  /** Randomly returns either -1 or 1 deterministically
856
862
  * @return {number} */
857
863
  sign() { return this.float() > .5 ? 1 : -1; }
864
+
865
+ /** Returns a seeded random value between the two values passed in with a random sign
866
+ * @param {number} [valueA]
867
+ * @param {number} [valueB]
868
+ * @return {number} */
869
+ floatSign(valueA=1, valueB=0) { return this.float(valueA, valueB) * this.sign(); }
858
870
  }
859
871
 
860
872
  ///////////////////////////////////////////////////////////////////////////////
@@ -1495,8 +1507,6 @@ class Timer
1495
1507
  * @namespace Settings
1496
1508
  */
1497
1509
 
1498
-
1499
-
1500
1510
  ///////////////////////////////////////////////////////////////////////////////
1501
1511
  // Camera settings
1502
1512
 
@@ -1981,8 +1991,6 @@ function setDebugKey(key) { debugKey = key; }
1981
1991
  * LittleJS Object System
1982
1992
  */
1983
1993
 
1984
-
1985
-
1986
1994
  /**
1987
1995
  * LittleJS Object Base Object Class
1988
1996
  * - Top level object class used by the engine
@@ -2465,8 +2473,6 @@ class EngineObject
2465
2473
  * @namespace Draw
2466
2474
  */
2467
2475
 
2468
-
2469
-
2470
2476
  /** The primary 2D canvas visible to the user
2471
2477
  * @type {HTMLCanvasElement}
2472
2478
  * @memberof Draw */
@@ -3088,8 +3094,6 @@ function setCursor(cursorStyle = 'auto')
3088
3094
  * @namespace Input
3089
3095
  */
3090
3096
 
3091
-
3092
-
3093
3097
  /** Returns true if device key is down
3094
3098
  * @param {string|number} key
3095
3099
  * @param {number} [device]
@@ -3141,21 +3145,21 @@ function clearInput() { inputData = [[]]; touchGamepadButtons = []; }
3141
3145
  * @param {number} button
3142
3146
  * @return {boolean}
3143
3147
  * @memberof Input */
3144
- const mouseIsDown = keyIsDown;
3148
+ function mouseIsDown(button) { return keyIsDown(button); }
3145
3149
 
3146
3150
  /** Returns true if mouse button was pressed
3147
3151
  * @function
3148
3152
  * @param {number} button
3149
3153
  * @return {boolean}
3150
3154
  * @memberof Input */
3151
- const mouseWasPressed = keyWasPressed;
3155
+ function mouseWasPressed(button) { return keyWasPressed(button); }
3152
3156
 
3153
3157
  /** Returns true if mouse button was released
3154
3158
  * @function
3155
3159
  * @param {number} button
3156
3160
  * @return {boolean}
3157
3161
  * @memberof Input */
3158
- const mouseWasReleased = keyWasReleased;
3162
+ function mouseWasReleased(button) { return keyWasReleased(button); }
3159
3163
 
3160
3164
  /** Mouse pos in world space
3161
3165
  * @type {Vector2}
@@ -3614,8 +3618,6 @@ function touchGamepadRender()
3614
3618
  * @namespace Audio
3615
3619
  */
3616
3620
 
3617
-
3618
-
3619
3621
  /** Audio context used by the engine
3620
3622
  * @type {AudioContext}
3621
3623
  * @memberof Audio */
@@ -3652,17 +3654,17 @@ class Sound
3652
3654
  {
3653
3655
  /** Create a sound object and cache the zzfx samples for later use
3654
3656
  * @param {Array} zzfxSound - Array of zzfx parameters, ex. [.5,.5]
3655
- * @param {number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
3657
+ * @param {number} [range=soundDefaultRange] - World space max range of sound
3656
3658
  * @param {number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
3657
3659
  */
3658
3660
  constructor(zzfxSound, range=soundDefaultRange, taper=soundDefaultTaper)
3659
3661
  {
3660
3662
  if (!soundEnable || headlessMode) return;
3661
3663
 
3662
- /** @property {number} - World space max range of sound, will not play if camera is farther away */
3664
+ /** @property {number} - World space max range of sound */
3663
3665
  this.range = range;
3664
3666
 
3665
- /** @property {number} - At what percentage of range should it start tapering off */
3667
+ /** @property {number} - At what percentage of range should it start tapering */
3666
3668
  this.taper = taper;
3667
3669
 
3668
3670
  /** @property {number} - How much to randomize frequency each time sound plays */
@@ -3778,8 +3780,8 @@ class SoundWave extends Sound
3778
3780
  /** Create a sound object and cache the wave file for later use
3779
3781
  * @param {string} filename - Filename of audio file to load
3780
3782
  * @param {number} [randomness] - How much to randomize frequency each time sound plays
3781
- * @param {number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
3782
- * @param {number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
3783
+ * @param {number} [range=soundDefaultRange] - World space max range of sound
3784
+ * @param {number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
3783
3785
  * @param {Function} [onloadCallback] - callback function to call when sound is loaded
3784
3786
  */
3785
3787
  constructor(filename, randomness=0, range, taper, onloadCallback)
@@ -3787,17 +3789,26 @@ class SoundWave extends Sound
3787
3789
  super(undefined, range, taper);
3788
3790
  if (!soundEnable || headlessMode) return;
3789
3791
 
3792
+ /** @property {Function} - callback function to call when sound is loaded */
3793
+ this.onloadCallback = onloadCallback;
3790
3794
  this.randomness = randomness;
3791
- fetch(filename)
3792
- .then(response => response.arrayBuffer())
3793
- .then(arrayBuffer => audioContext.decodeAudioData(arrayBuffer))
3794
- .then(audioBuffer =>
3795
- {
3796
- this.sampleChannels = [];
3797
- for (let i = audioBuffer.numberOfChannels; i--;)
3798
- this.sampleChannels[i] = Array.from(audioBuffer.getChannelData(i));
3799
- this.sampleRate = audioBuffer.sampleRate;
3800
- }).then(() => onloadCallback && onloadCallback(this));
3795
+ this.loadSound(filename);
3796
+ }
3797
+
3798
+ /** Loads a sound from a URL and decodes it into sample data. Must be used with await!
3799
+ * @param {string} filename
3800
+ * @return {Promise<void>} */
3801
+ async loadSound(filename)
3802
+ {
3803
+ const response = await fetch(filename);
3804
+ const arrayBuffer = await response.arrayBuffer();
3805
+ const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
3806
+ this.sampleChannels = [];
3807
+ for (let i = audioBuffer.numberOfChannels; i--;)
3808
+ this.sampleChannels[i] = Array.from(audioBuffer.getChannelData(i));
3809
+ this.sampleRate = audioBuffer.sampleRate;
3810
+ if (this.onloadCallback)
3811
+ this.onloadCallback();
3801
3812
  }
3802
3813
  }
3803
3814
 
@@ -4077,8 +4088,6 @@ function zzfxG
4077
4088
  * @namespace TileCollision
4078
4089
  */
4079
4090
 
4080
-
4081
-
4082
4091
  ///////////////////////////////////////////////////////////////////////////////
4083
4092
  // Tile Layer System
4084
4093
 
@@ -4542,8 +4551,6 @@ class TileCollisionLayer extends TileLayer
4542
4551
  * LittleJS Particle System
4543
4552
  */
4544
4553
 
4545
-
4546
-
4547
4554
  /**
4548
4555
  * Particle Emitter - Spawns particles with the given settings
4549
4556
  * @extends EngineObject
@@ -4888,8 +4895,6 @@ class Particle extends EngineObject
4888
4895
  * @namespace Medals
4889
4896
  */
4890
4897
 
4891
-
4892
-
4893
4898
  /** List of all medals
4894
4899
  * @type {Object}
4895
4900
  * @memberof Medals */
@@ -5077,8 +5082,6 @@ class Medal
5077
5082
  * @namespace WebGL
5078
5083
  */
5079
5084
 
5080
-
5081
-
5082
5085
  /** The WebGL canvas which appears above the main canvas and below the overlay canvas
5083
5086
  * @type {HTMLCanvasElement}
5084
5087
  * @memberof WebGL */
@@ -5277,7 +5280,19 @@ function glCreateTexture(image)
5277
5280
  const texture = glContext.createTexture();
5278
5281
  glContext.bindTexture(glContext.TEXTURE_2D, texture);
5279
5282
  if (image && image.width)
5283
+ {
5280
5284
  glSetTextureData(texture, image);
5285
+
5286
+ const isPowerOfTwo = (value)=> !(value & (value - 1));
5287
+ if (!tilesPixelated && isPowerOfTwo(image.width) && isPowerOfTwo(image.height))
5288
+ {
5289
+ // use mipmap filtering
5290
+ glContext.generateMipmap(glContext.TEXTURE_2D);
5291
+ glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MIN_FILTER, glContext.LINEAR_MIPMAP_LINEAR);
5292
+ glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MAG_FILTER, glContext.LINEAR);
5293
+ return texture;
5294
+ }
5295
+ }
5281
5296
  else
5282
5297
  {
5283
5298
  // create a white texture
@@ -5285,7 +5300,7 @@ function glCreateTexture(image)
5285
5300
  glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, 1, 1, 0, glContext.RGBA, glContext.UNSIGNED_BYTE, whitePixel);
5286
5301
  }
5287
5302
 
5288
- // use point filtering for pixelated rendering
5303
+ // set texture filtering
5289
5304
  const filter = tilesPixelated ? glContext.NEAREST : glContext.LINEAR;
5290
5305
  glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MIN_FILTER, filter);
5291
5306
  glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MAG_FILTER, filter);
@@ -5401,8 +5416,6 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=-1, rgba
5401
5416
  * @namespace Engine
5402
5417
  */
5403
5418
 
5404
-
5405
-
5406
5419
  /** Name of engine
5407
5420
  * @type {string}
5408
5421
  * @default
@@ -5413,7 +5426,7 @@ const engineName = 'LittleJS';
5413
5426
  * @type {string}
5414
5427
  * @default
5415
5428
  * @memberof Engine */
5416
- const engineVersion = '1.12.4';
5429
+ const engineVersion = '1.12.6';
5417
5430
 
5418
5431
  /** Frames per second to update
5419
5432
  * @type {number}
@@ -5495,7 +5508,7 @@ function engineAddPlugin(updateFunction, renderFunction)
5495
5508
  * @param {Array<string>} [imageSources=[]] - List of images to load
5496
5509
  * @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default
5497
5510
  * @memberof Engine */
5498
- function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
5511
+ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
5499
5512
  {
5500
5513
  ASSERT(!mainContext, 'engine already initialized');
5501
5514
  ASSERT(Array.isArray(imageSources), 'pass in images as array');
@@ -5652,16 +5665,14 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5652
5665
  mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
5653
5666
  }
5654
5667
 
5655
- function startEngine()
5668
+ // wait for gameInit to load
5669
+ async function startEngine()
5656
5670
  {
5657
- new Promise((resolve) => resolve(gameInit())).then(engineUpdate);
5671
+ await gameInit();
5672
+ engineUpdate();
5658
5673
  }
5659
-
5660
5674
  if (headlessMode)
5661
- {
5662
- startEngine();
5663
- return;
5664
- }
5675
+ return startEngine();
5665
5676
 
5666
5677
  // setup html
5667
5678
  const styleRoot =
@@ -5737,8 +5748,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5737
5748
  }));
5738
5749
  }
5739
5750
 
5740
- // load all of the images
5741
- Promise.all(promises).then(startEngine);
5751
+ // wait for all the promises to finish
5752
+ await Promise.all(promises);
5753
+ return startEngine();
5742
5754
  }
5743
5755
 
5744
5756
  /** Update each engine object, remove destroyed objects, and update time
@@ -6017,8 +6029,6 @@ function drawEngineSplashScreen(t)
6017
6029
  * - Functions to unlock medals
6018
6030
  */
6019
6031
 
6020
-
6021
-
6022
6032
  /** Global Newgrounds object
6023
6033
  * @type {NewgroundsPlugin}
6024
6034
  * @memberof Medal */
@@ -6192,8 +6202,6 @@ class NewgroundsPlugin
6192
6202
  * - can be enabled to pass other canvases through a final shader
6193
6203
  */
6194
6204
 
6195
-
6196
-
6197
6205
  ///////////////////////////////////////////////////////////////////////////////
6198
6206
 
6199
6207
  /** Global Post Process plugin object
@@ -6306,8 +6314,6 @@ class PostProcessPlugin
6306
6314
  * LittleJS ZzFXM Plugin
6307
6315
  */
6308
6316
 
6309
-
6310
-
6311
6317
  /**
6312
6318
  * Music Object - Stores a zzfx music track for later use
6313
6319
  *
@@ -6476,8 +6482,6 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
6476
6482
  * - Images
6477
6483
  */
6478
6484
 
6479
-
6480
-
6481
6485
  ///////////////////////////////////////////////////////////////////////////////
6482
6486
 
6483
6487
  /** Global UI system plugin object
@@ -6645,6 +6649,8 @@ class UIObject
6645
6649
  this.lineWidth = uiSystem.defaultLineWidth;
6646
6650
  /** @property {string} */
6647
6651
  this.font = uiSystem.defaultFont;
6652
+ /** @property {number} - override for text height */
6653
+ this.textHeight = undefined;
6648
6654
  /** @property {boolean} */
6649
6655
  this.visible = true;
6650
6656
  /** @property {Array<UIObject>} */
@@ -6755,7 +6761,8 @@ class UIText extends UIObject
6755
6761
  }
6756
6762
  render()
6757
6763
  {
6758
- uiSystem.drawText(this.text, this.pos, this.size, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
6764
+ const textSize = vec2(this.size.x, this.textHeight || this.size.y);
6765
+ uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
6759
6766
  }
6760
6767
  }
6761
6768
 
@@ -6818,7 +6825,9 @@ class UIButton extends UIObject
6818
6825
  const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
6819
6826
  const color = this.mouseIsOver? this.hoverColor : this.color;
6820
6827
  uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor);
6821
- const textSize = vec2(this.size.x, this.size.y*.8);
6828
+
6829
+ const textScale = .8; // scale text to fit in button
6830
+ const textSize = vec2(this.size.x, this.textHeight || this.size.y*textScale);
6822
6831
  uiSystem.drawText(this.text, this.pos, textSize,
6823
6832
  this.textColor, 0, undefined, this.align, this.font);
6824
6833
  }
@@ -6915,7 +6924,8 @@ class UIScrollbar extends UIObject
6915
6924
  const barColor = this.mouseIsHeld ? this.color : this.handleColor;
6916
6925
  uiSystem.drawRect(handlePos, handleSize, barColor, this.lineWidth, this.lineColor);
6917
6926
 
6918
- const textSize = vec2(this.size.x, this.size.y*.8);
6927
+ const textScale = .8; // scale text to fit in scrollbar
6928
+ const textSize = vec2(this.size.x, this.textHeight || this.size.y*textScale);
6919
6929
  uiSystem.drawText(this.text, this.pos, textSize,
6920
6930
  this.textColor, 0, undefined, this.align, this.font);
6921
6931
  }
@@ -6924,31 +6934,33 @@ class UIScrollbar extends UIObject
6924
6934
  /**
6925
6935
  * LittleJS Box2D Physics Plugin
6926
6936
  * - Box2dObject extends EngineObject with Box2D physics
6927
- * - Call box2dEngineInit() to start instead of normal engineInit()
6937
+ * - Call box2dInit() before engineInit() to enable
6928
6938
  * - You will also need to include box2d.wasm.js
6929
- * - Uses box2d.js super fast web assembly port of Box2D
6939
+ * - Uses a super fast web assembly port of Box2D
6930
6940
  * - More info: https://github.com/kripken/box2d.js
6931
- * - Fully wraps everything in Box2d
6932
6941
  * - Functions to create polygon, circle, and edge shapes
6933
- * - Raycasting and querying
6934
- * - Joint creation
6935
6942
  * - Contact begin and end callbacks
6943
+ * - Wraps b2Vec2 type to/from Vector2
6944
+ * - Raycasting and querying
6945
+ * - Every type of joint
6936
6946
  * - Debug physics drawing
6947
+ * @namespace Box2D
6937
6948
  */
6938
6949
 
6939
-
6940
-
6941
6950
  /** Global Box2d Plugin object
6942
- * @type {Box2dPlugin} */
6951
+ * @type {Box2dPlugin}
6952
+ * @memberof Box2D */
6943
6953
  let box2d;
6944
6954
 
6945
6955
  /** Enable Box2D debug drawing
6946
6956
  * @type {boolean}
6947
- * @default */
6957
+ * @default
6958
+ * @memberof Box2D */
6948
6959
  let box2dDebug = false;
6949
6960
 
6950
6961
  /** Enable Box2D debug drawing
6951
- * @param {boolean} enable */
6962
+ * @param {boolean} enable
6963
+ * @memberof Box2D */
6952
6964
  function box2dSetDebug(enable) { box2dDebug = enable; }
6953
6965
 
6954
6966
  ///////////////////////////////////////////////////////////////////////////////
@@ -7050,7 +7062,7 @@ class Box2dObject extends EngineObject
7050
7062
  * @param {number} [friction]
7051
7063
  * @param {number} [restitution]
7052
7064
  * @param {boolean} [isSensor] */
7053
- addShape(shape, density=1, friction=.2, restitution=0, isSensor=false)
7065
+ addShape(shape, density=1, friction=1, restitution=0, isSensor=false)
7054
7066
  {
7055
7067
  const fd = new box2d.instance.b2FixtureDef();
7056
7068
  fd.set_shape(shape);
@@ -8689,28 +8701,18 @@ class Box2dPlugin
8689
8701
  }
8690
8702
 
8691
8703
  ///////////////////////////////////////////////////////////////////////////////
8692
- /** Box2d Init - Startup LittleJS engine with your callback functions
8693
- * @param {Function|function():Promise} gameInit - Called once after the engine starts up
8694
- * @param {Function} gameUpdate - Called every frame before objects are updated
8695
- * @param {Function} gameUpdatePost - Called after physics and objects are updated, even when paused
8696
- * @param {Function} gameRender - Called before objects are rendered, for drawing the background
8697
- * @param {Function} gameRenderPost - Called after objects are rendered, useful for drawing UI
8698
- * @param {Array<string>} [imageSources=[]] - List of images to load
8699
- * @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default */
8700
- function box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources, rootElement)
8704
+ /** Box2d Init - Call with await before starting LittleJS to init box2d
8705
+ * @return {Promise<Box2dPlugin>}
8706
+ * @memberof Box2D */
8707
+ async function box2dInit()
8701
8708
  {
8702
- Box2D().then(box2dInstance=>
8703
- {
8704
- // create box2d object
8705
- new Box2dPlugin(box2dInstance);
8706
- setupDebugDraw();
8707
-
8708
- // start littlejs
8709
- engineAddPlugin(box2dUpdate, box2dRender);
8710
- engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources, rootElement);
8711
- });
8709
+ // load box2d
8710
+ new Box2dPlugin(await Box2D());
8711
+ setupDebugDraw();
8712
+ engineAddPlugin(box2dUpdate, box2dRender);
8713
+ return box2d;
8712
8714
 
8713
- // hook up box2d plugin to update and render
8715
+ // add the box2d plugin to the engine
8714
8716
  function box2dUpdate()
8715
8717
  {
8716
8718
  if (!paused)