littlejsengine 1.9.2 → 1.9.3

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
@@ -56,7 +56,7 @@ let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParti
56
56
  ///////////////////////////////////////////////////////////////////////////////
57
57
  // Debug helper functions
58
58
 
59
- /** Asserts if the experssion is false, does not do anything in release builds
59
+ /** Asserts if the expression is false, does not do anything in release builds
60
60
  * @param {Boolean} assert
61
61
  * @param {Object} [output]
62
62
  * @memberof Debug */
@@ -453,7 +453,7 @@ function min(valueA, valueB) { return Math.min(valueA, valueB); }
453
453
  * @memberof Utilities */
454
454
  function max(valueA, valueB) { return Math.max(valueA, valueB); }
455
455
 
456
- /** Returns the sign of value passed in (also returns 1 if 0)
456
+ /** Returns the sign of value passed in
457
457
  * @param {Number} value
458
458
  * @return {Number}
459
459
  * @memberof Utilities */
@@ -500,7 +500,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
500
500
  function distanceWrap(valueA, valueB, wrapSize=1)
501
501
  { const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
502
502
 
503
- /** Linearly interpolates between values passed in with wrappping
503
+ /** Linearly interpolates between values passed in with wrapping
504
504
  * @param {Number} percent
505
505
  * @param {Number} valueA
506
506
  * @param {Number} valueB
@@ -517,7 +517,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
517
517
  * @memberof Utilities */
518
518
  function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
519
519
 
520
- /** Linearly interpolates between the angles passed in with wrappping
520
+ /** Linearly interpolates between the angles passed in with wrapping
521
521
  * @param {Number} percent
522
522
  * @param {Number} angleA
523
523
  * @param {Number} angleB
@@ -1259,7 +1259,7 @@ let tileSizeDefault = vec2(16);
1259
1259
  * @type {Number}
1260
1260
  * @default
1261
1261
  * @memberof Settings */
1262
- let tileFixBleedScale = .3;
1262
+ let tileFixBleedScale = .1;
1263
1263
 
1264
1264
  ///////////////////////////////////////////////////////////////////////////////
1265
1265
  // Object settings
@@ -1270,7 +1270,7 @@ let tileFixBleedScale = .3;
1270
1270
  * @memberof Settings */
1271
1271
  let enablePhysicsSolver = true;
1272
1272
 
1273
- /** Default object mass for collison calcuations (how heavy objects are)
1273
+ /** Default object mass for collision calcuations (how heavy objects are)
1274
1274
  * @type {Number}
1275
1275
  * @default
1276
1276
  * @memberof Settings */
@@ -1353,7 +1353,7 @@ let touchGamepadEnable = false;
1353
1353
  * @memberof Settings */
1354
1354
  let touchGamepadAnalog = true;
1355
1355
 
1356
- /** Size of virutal gamepad for touch devices in pixels
1356
+ /** Size of virtual gamepad for touch devices in pixels
1357
1357
  * @type {Number}
1358
1358
  * @default
1359
1359
  * @memberof Settings */
@@ -1640,7 +1640,7 @@ function setDebugKey(key) { debugKey = key; }
1640
1640
  * - Automatically adds self to object list
1641
1641
  * - Will be updated and rendered each frame
1642
1642
  * - Renders as a sprite from a tilesheet by default
1643
- * - Can have color and addtive color applied
1643
+ * - Can have color and additive color applied
1644
1644
  * - 2D Physics and collision system
1645
1645
  * - Sorted by renderOrder
1646
1646
  * - Objects can have children attached
@@ -1919,7 +1919,7 @@ class EngineObject
1919
1919
  }
1920
1920
 
1921
1921
  /** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
1922
- destroy()
1922
+ destroy()
1923
1923
  {
1924
1924
  if (this.destroyed)
1925
1925
  return;
@@ -2108,13 +2108,9 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
2108
2108
  if (typeof pos === 'number')
2109
2109
  {
2110
2110
  const textureInfo = textureInfos[textureIndex];
2111
- if (textureInfo)
2112
- {
2113
- const cols = textureInfo.size.x / size.x |0;
2114
- pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
2115
- }
2116
- else
2117
- pos = vec2();
2111
+ ASSERT(textureInfo, 'Texture not loaded');
2112
+ const cols = textureInfo.size.x / size.x |0;
2113
+ pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
2118
2114
  }
2119
2115
 
2120
2116
  // return a tile info object
@@ -2203,6 +2199,11 @@ function worldToScreen(worldPos)
2203
2199
  );
2204
2200
  }
2205
2201
 
2202
+ /** Get the camera's visible area in world space
2203
+ * @return {Vector2}
2204
+ * @memberof Draw */
2205
+ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
2206
+
2206
2207
  /** Draw textured tile centered in world space, with color applied if using WebGL
2207
2208
  * @param {Vector2} pos - Center of the tile in world space
2208
2209
  * @param {Vector2} [size=(1,1)] - Size of the tile in world space
@@ -2880,7 +2881,7 @@ function createTouchGamepad()
2880
2881
  touchGamepadStick = vec2();
2881
2882
 
2882
2883
  const touchHandler = ontouchstart;
2883
- ontouchstart = ontouchmove = ontouchend = (e)=>
2884
+ ontouchstart = ontouchmove = ontouchend = (e)=>
2884
2885
  {
2885
2886
  // clear touch gamepad input
2886
2887
  touchGamepadStick = vec2();
@@ -3010,7 +3011,7 @@ function touchGamepadRender()
3010
3011
 
3011
3012
 
3012
3013
  /**
3013
- * Sound Object - Stores a zzfx sound for later use and can be played positionally
3014
+ * Sound Object - Stores a sound for later use and can be played positionally
3014
3015
  *
3015
3016
  * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
3016
3017
  * @example
@@ -3025,7 +3026,7 @@ class Sound
3025
3026
  /** Create a sound object and cache the zzfx samples for later use
3026
3027
  * @param {Array} zzfxSound - Array of zzfx parameters, ex. [.5,.5]
3027
3028
  * @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
3028
- * @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
3029
+ * @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
3029
3030
  */
3030
3031
  constructor(zzfxSound, range=soundDefaultRange, taper=soundDefaultTaper)
3031
3032
  {
@@ -3205,24 +3206,24 @@ class Music extends Sound
3205
3206
 
3206
3207
  /** Play the music
3207
3208
  * @param {Number} [volume=1] - How much to scale volume by
3208
- * @param {Boolean} [loop=1] - True if the music should loop
3209
+ * @param {Boolean} [loop] - True if the music should loop
3209
3210
  * @return {AudioBufferSourceNode} - The audio source node
3210
3211
  */
3211
- playMusic(volume, loop = false)
3212
+ playMusic(volume, loop=false)
3212
3213
  { return super.play(undefined, volume, 1, 1, loop); }
3213
3214
  }
3214
3215
 
3215
3216
  /** Play an mp3, ogg, or wav audio from a local file or url
3216
- * @param {String} url - Location of sound file to play
3217
+ * @param {String} filename - Location of sound file to play
3217
3218
  * @param {Number} [volume] - How much to scale volume by
3218
3219
  * @param {Boolean} [loop] - True if the music should loop
3219
3220
  * @return {HTMLAudioElement} - The audio element for this sound
3220
3221
  * @memberof Audio */
3221
- function playAudioFile(url, volume=1, loop=false)
3222
+ function playAudioFile(filename, volume=1, loop=false)
3222
3223
  {
3223
3224
  if (!soundEnable) return;
3224
3225
 
3225
- const audio = new Audio(url);
3226
+ const audio = new Audio(filename);
3226
3227
  audio.volume = soundVolume * volume;
3227
3228
  audio.loop = loop;
3228
3229
  audio.play();
@@ -3274,6 +3275,11 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
3274
3275
  * @memberof Audio */
3275
3276
  let audioContext = new AudioContext;
3276
3277
 
3278
+ /** Keep track if audio was suspended when last sound was played
3279
+ * @type {Boolean}
3280
+ * @memberof Audio */
3281
+ let audioSuspended = false;
3282
+
3277
3283
  /** Play cached audio samples with given settings
3278
3284
  * @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
3279
3285
  * @param {Number} [volume] - How much to scale volume by
@@ -3288,11 +3294,15 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
3288
3294
  if (!soundEnable) return;
3289
3295
 
3290
3296
  // prevent sounds from building up if they can't be played
3291
- if (audioContext.state != 'running')
3297
+ const audioWasSuspended = audioSuspended;
3298
+ if (audioSuspended = audioContext.state != 'running')
3292
3299
  {
3293
3300
  // fix stalled audio
3294
3301
  audioContext.resume();
3295
- return;
3302
+
3303
+ // prevent suspended sounds from building up
3304
+ if (audioWasSuspended)
3305
+ return;
3296
3306
  }
3297
3307
 
3298
3308
  // create buffer and source
@@ -3560,7 +3570,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
3560
3570
  /**
3561
3571
  * LittleJS Tile Layer System
3562
3572
  * - Caches arrays of tiles to off screen canvas for fast rendering
3563
- * - Unlimted numbers of layers, allocates canvases as needed
3573
+ * - Unlimited numbers of layers, allocates canvases as needed
3564
3574
  * - Interfaces with EngineObject for collision
3565
3575
  * - Collision layer is separate from visible layers
3566
3576
  * - It is recommended to have a visible layer that matches the collision
@@ -3612,7 +3622,7 @@ function getTileCollisionData(pos)
3612
3622
 
3613
3623
  /** Check if collision with another object should occur
3614
3624
  * @param {Vector2} pos
3615
- * @param {Vector2} [size=(1,1)]
3625
+ * @param {Vector2} [size=(0,0)]
3616
3626
  * @param {EngineObject} [object]
3617
3627
  * @return {Boolean}
3618
3628
  * @memberof TileCollision */
@@ -4129,16 +4139,17 @@ class ParticleEmitter extends EngineObject
4129
4139
 
4130
4140
  // build particle
4131
4141
  const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
4132
- particle.velocity = vec2().setAngle(velocityAngle, speed);
4133
- particle.fadeRate = this.fadeRate;
4134
- particle.damping = this.damping;
4135
- particle.angleDamping = this.angleDamping;
4136
- particle.elasticity = this.elasticity;
4137
- particle.friction = this.friction;
4138
- particle.gravityScale = this.gravityScale;
4139
- particle.collideTiles = this.collideTiles;
4140
- particle.renderOrder = this.renderOrder;
4141
- particle.mirror = !!randInt(2);
4142
+ particle.velocity = vec2().setAngle(velocityAngle, speed);
4143
+ particle.angleVelocity = angleSpeed;
4144
+ particle.fadeRate = this.fadeRate;
4145
+ particle.damping = this.damping;
4146
+ particle.angleDamping = this.angleDamping;
4147
+ particle.elasticity = this.elasticity;
4148
+ particle.friction = this.friction;
4149
+ particle.gravityScale = this.gravityScale;
4150
+ particle.collideTiles = this.collideTiles;
4151
+ particle.renderOrder = this.renderOrder;
4152
+ particle.mirror = !!randInt(2);
4142
4153
 
4143
4154
  // call particle create callaback
4144
4155
  this.particleCreateCallback && this.particleCreateCallback(particle);
@@ -4529,7 +4540,7 @@ class Newgrounds
4529
4540
  }
4530
4541
 
4531
4542
  // build the input object
4532
- const input =
4543
+ const input =
4533
4544
  {
4534
4545
  'app_id': this.app_id,
4535
4546
  'session_id': this.session_id,
@@ -4744,8 +4755,6 @@ function glCreateTexture(image)
4744
4755
  const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
4745
4756
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
4746
4757
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
4747
- glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
4748
- glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE);
4749
4758
 
4750
4759
  return texture;
4751
4760
  }
@@ -4823,7 +4832,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
4823
4832
  ///////////////////////////////////////////////////////////////////////////////
4824
4833
  // post processing - can be enabled to pass other canvases through a final shader
4825
4834
 
4826
- let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
4835
+ let glPostShader, glPostTexture, glPostIncludeOverlay;
4827
4836
 
4828
4837
  /** Set up a post processing shader
4829
4838
  * @param {String} shaderCode
@@ -4859,7 +4868,6 @@ function glInitPostProcess(shaderCode, includeOverlay=false)
4859
4868
  );
4860
4869
 
4861
4870
  // create buffer and texture
4862
- glPostArrayBuffer = glContext.createBuffer();
4863
4871
  glPostTexture = glCreateTexture(undefined);
4864
4872
  glPostIncludeOverlay = includeOverlay;
4865
4873
 
@@ -4931,10 +4939,7 @@ gl_NEAREST = 9728,
4931
4939
  gl_LINEAR = 9729,
4932
4940
  gl_TEXTURE_MAG_FILTER = 10240,
4933
4941
  gl_TEXTURE_MIN_FILTER = 10241,
4934
- gl_TEXTURE_WRAP_S = 10242,
4935
- gl_TEXTURE_WRAP_T = 10243,
4936
4942
  gl_COLOR_BUFFER_BIT = 16384,
4937
- gl_CLAMP_TO_EDGE = 33071,
4938
4943
  gl_TEXTURE0 = 33984,
4939
4944
  gl_ARRAY_BUFFER = 34962,
4940
4945
  gl_STATIC_DRAW = 35044,
@@ -4951,7 +4956,7 @@ gl_MAX_INSTANCES = 1e4,
4951
4956
  gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
4952
4957
  gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
4953
4958
  /**
4954
- * LittleJS - The Tiny JavaScript Game Engine That Can!
4959
+ * LittleJS - The Tiny Fast JavaScript Game Engine
4955
4960
  * MIT License - Copyright 2021 Frank Force
4956
4961
  *
4957
4962
  * Engine Features
@@ -4982,9 +4987,9 @@ const engineName = 'LittleJS';
4982
4987
  * @type {String}
4983
4988
  * @default
4984
4989
  * @memberof Engine */
4985
- const engineVersion = '1.9.2';
4990
+ const engineVersion = '1.9.3';
4986
4991
 
4987
- /** Frames per second to update objects
4992
+ /** Frames per second to update
4988
4993
  * @type {Number}
4989
4994
  * @default
4990
4995
  * @memberof Engine */
@@ -5001,7 +5006,7 @@ const timeDelta = 1/frameRate;
5001
5006
  * @memberof Engine */
5002
5007
  let engineObjects = [];
5003
5008
 
5004
- /** Array containing only objects that are set to collide with other objects this frame (for optimization)
5009
+ /** Array with only objects set to collide with other objects this frame (for optimization)
5005
5010
  * @type {Array}
5006
5011
  * @memberof Engine */
5007
5012
  let engineObjectsCollide = [];
@@ -5011,7 +5016,7 @@ let engineObjectsCollide = [];
5011
5016
  * @memberof Engine */
5012
5017
  let frame = 0;
5013
5018
 
5014
- /** Current engine time since start in seconds, derived from frame
5019
+ /** Current engine time since start in seconds
5015
5020
  * @type {Number}
5016
5021
  * @memberof Engine */
5017
5022
  let time = 0;
@@ -5037,12 +5042,12 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
5037
5042
 
5038
5043
  ///////////////////////////////////////////////////////////////////////////////
5039
5044
 
5040
- /** Start up LittleJS engine with your callback functions
5041
- * @param {Function} gameInit - Called once after the engine starts up, setup the game
5042
- * @param {Function} gameUpdate - Called every frame at 60 frames per second, handle input and update the game state
5043
- * @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
5044
- * @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
5045
- * @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
5045
+ /** Startup LittleJS engine with your callback functions
5046
+ * @param {Function} gameInit - Called once after the engine starts up, setup the game
5047
+ * @param {Function} gameUpdate - Called every frame at 60 frames per second, handle input and update the game state
5048
+ * @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
5049
+ * @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
5050
+ * @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
5046
5051
  * @param {Array} [imageSources=['tiles.png']] - Image to load
5047
5052
  * @memberof Engine */
5048
5053
  function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
@@ -5065,32 +5070,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5065
5070
  frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
5066
5071
  if (!debugSpeedUp)
5067
5072
  frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp incase of slow framerate
5068
-
5069
- if (canvasFixedSize.x)
5070
- {
5071
- // clear canvas and set fixed size
5072
- mainCanvas.width = canvasFixedSize.x;
5073
- mainCanvas.height = canvasFixedSize.y;
5074
-
5075
- // fit to window by adding space on top or bottom if necessary
5076
- const aspect = innerWidth / innerHeight;
5077
- const fixedAspect = mainCanvas.width / mainCanvas.height;
5078
- (glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
5079
- (glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
5080
- }
5081
- else
5082
- {
5083
- // clear canvas and set size to same as window
5084
- mainCanvas.width = min(innerWidth, canvasMaxSize.x);
5085
- mainCanvas.height = min(innerHeight, canvasMaxSize.y);
5086
- }
5087
-
5088
- // clear overlay canvas and set size
5089
- overlayCanvas.width = mainCanvas.width;
5090
- overlayCanvas.height = mainCanvas.height;
5091
-
5092
- // save canvas size
5093
- mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
5073
+ updateCanvas();
5094
5074
 
5095
5075
  if (paused)
5096
5076
  {
@@ -5164,6 +5144,35 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5164
5144
  requestAnimationFrame(engineUpdate);
5165
5145
  }
5166
5146
 
5147
+ function updateCanvas()
5148
+ {
5149
+ if (canvasFixedSize.x)
5150
+ {
5151
+ // clear canvas and set fixed size
5152
+ mainCanvas.width = canvasFixedSize.x;
5153
+ mainCanvas.height = canvasFixedSize.y;
5154
+
5155
+ // fit to window by adding space on top or bottom if necessary
5156
+ const aspect = innerWidth / innerHeight;
5157
+ const fixedAspect = mainCanvas.width / mainCanvas.height;
5158
+ (glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
5159
+ (glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
5160
+ }
5161
+ else
5162
+ {
5163
+ // clear canvas and set size to same as window
5164
+ mainCanvas.width = min(innerWidth, canvasMaxSize.x);
5165
+ mainCanvas.height = min(innerHeight, canvasMaxSize.y);
5166
+ }
5167
+
5168
+ // clear overlay canvas and set size
5169
+ overlayCanvas.width = mainCanvas.width;
5170
+ overlayCanvas.height = mainCanvas.height;
5171
+
5172
+ // save canvas size
5173
+ mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
5174
+ }
5175
+
5167
5176
  // setup html
5168
5177
  const styleBody =
5169
5178
  'margin:0;overflow:hidden;' + // fill the window
@@ -5188,6 +5197,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5188
5197
  const styleCanvas = 'position:absolute;' + // position
5189
5198
  'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
5190
5199
  (glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
5200
+ updateCanvas();
5191
5201
 
5192
5202
  // create promises for loading images
5193
5203
  const promises = imageSources.map((src, textureIndex)=>
@@ -5384,9 +5394,9 @@ function drawEngineSplashScreen(t)
5384
5394
 
5385
5395
  // big stack
5386
5396
  rect(50,20,10,-10,color(0,1));
5387
- rect(50,20,6,-10,color(0,2));
5388
- rect(50,20,3,-10,color(0,3));
5389
- rect(50,10,10,10);
5397
+ rect(50,20,6.5,-10,color(0,2));
5398
+ rect(50,20,3.5,-10,color(0,3));
5399
+ rect(50,20,10,-10);
5390
5400
  circle(55,2,11.4,.5,PI-.5,color(3,3));
5391
5401
  circle(55,2,11.4,.5,PI/2,color(3,2),1);
5392
5402
  circle(55,2,11.4,.5,PI-.5);
@@ -5405,7 +5415,7 @@ function drawEngineSplashScreen(t)
5405
5415
 
5406
5416
  // engine outline
5407
5417
  circle(36,30,10,PI/2,PI*3/2);
5408
- circle(47,30,10,PI/2,PI*3/2);
5418
+ circle(48,30,10,PI/2,PI*3/2);
5409
5419
  circle(60,30,10);
5410
5420
  line(36,20,60,20);
5411
5421