littlejsengine 1.9.2 → 1.9.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 (37) hide show
  1. package/README.md +4 -2
  2. package/dist/littlejs.d.ts +31 -26
  3. package/dist/littlejs.esm.js +145 -122
  4. package/dist/littlejs.esm.min.js +1 -1
  5. package/dist/littlejs.js +145 -122
  6. package/dist/littlejs.min.js +1 -1
  7. package/dist/littlejs.release.js +140 -121
  8. package/examples/logo.png +0 -0
  9. package/examples/module/game.js +1 -1
  10. package/examples/platformer/data/{gameTileData.tmx → gameLevelData.tmx} +3 -3
  11. package/examples/platformer/game.js +31 -10
  12. package/examples/platformer/gameCharacter.js +7 -6
  13. package/examples/platformer/gameEffects.js +77 -68
  14. package/examples/platformer/gameLevel.js +33 -17
  15. package/examples/platformer/{gameTileData.js → gameLevelData.js} +3 -3
  16. package/examples/platformer/gameObjects.js +14 -14
  17. package/examples/platformer/index.html +1 -1
  18. package/examples/platformer/tiles.png +0 -0
  19. package/examples/screenshot.jpg +0 -0
  20. package/examples/stress/index.html +1 -1
  21. package/examples/typescript/game.js +1 -1
  22. package/examples/typescript/game.ts +1 -1
  23. package/package.json +1 -1
  24. package/reference.md +409 -0
  25. package/src/engine.js +47 -42
  26. package/src/engineAudio.js +19 -10
  27. package/src/engineDebug.js +5 -1
  28. package/src/engineDraw.js +19 -23
  29. package/src/engineInput.js +31 -17
  30. package/src/engineMedals.js +1 -1
  31. package/src/engineObject.js +2 -2
  32. package/src/engineParticles.js +11 -10
  33. package/src/engineSettings.js +3 -3
  34. package/src/engineTileLayer.js +2 -2
  35. package/src/engineUtilities.js +4 -4
  36. package/src/engineWebGL.js +1 -7
  37. /package/examples/platformer/data/{gameTileData.tsx → gameLevelData.tsx} +0 -0
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 */
@@ -264,6 +264,10 @@ function debugRender()
264
264
  {
265
265
  const saveContext = mainContext;
266
266
  mainContext = overlayContext;
267
+
268
+ // draw red rectangle around screen
269
+ const cameraSize = getCameraSize();
270
+ debugRect(cameraPos, cameraSize.subtract(vec2(.1)), '#f008');
267
271
 
268
272
  // mouse pick
269
273
  let bestDistance = Infinity, bestObject;
@@ -453,7 +457,7 @@ function min(valueA, valueB) { return Math.min(valueA, valueB); }
453
457
  * @memberof Utilities */
454
458
  function max(valueA, valueB) { return Math.max(valueA, valueB); }
455
459
 
456
- /** Returns the sign of value passed in (also returns 1 if 0)
460
+ /** Returns the sign of value passed in
457
461
  * @param {Number} value
458
462
  * @return {Number}
459
463
  * @memberof Utilities */
@@ -500,7 +504,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
500
504
  function distanceWrap(valueA, valueB, wrapSize=1)
501
505
  { const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
502
506
 
503
- /** Linearly interpolates between values passed in with wrappping
507
+ /** Linearly interpolates between values passed in with wrapping
504
508
  * @param {Number} percent
505
509
  * @param {Number} valueA
506
510
  * @param {Number} valueB
@@ -517,7 +521,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
517
521
  * @memberof Utilities */
518
522
  function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
519
523
 
520
- /** Linearly interpolates between the angles passed in with wrappping
524
+ /** Linearly interpolates between the angles passed in with wrapping
521
525
  * @param {Number} percent
522
526
  * @param {Number} angleA
523
527
  * @param {Number} angleB
@@ -1047,7 +1051,7 @@ class Color
1047
1051
 
1048
1052
  /** Returns this color expressed in hsla format
1049
1053
  * @return {Array} */
1050
- getHSLA()
1054
+ HSLA()
1051
1055
  {
1052
1056
  const r = clamp(this.r);
1053
1057
  const g = clamp(this.g);
@@ -1259,7 +1263,7 @@ let tileSizeDefault = vec2(16);
1259
1263
  * @type {Number}
1260
1264
  * @default
1261
1265
  * @memberof Settings */
1262
- let tileFixBleedScale = .3;
1266
+ let tileFixBleedScale = .1;
1263
1267
 
1264
1268
  ///////////////////////////////////////////////////////////////////////////////
1265
1269
  // Object settings
@@ -1270,7 +1274,7 @@ let tileFixBleedScale = .3;
1270
1274
  * @memberof Settings */
1271
1275
  let enablePhysicsSolver = true;
1272
1276
 
1273
- /** Default object mass for collison calcuations (how heavy objects are)
1277
+ /** Default object mass for collision calcuations (how heavy objects are)
1274
1278
  * @type {Number}
1275
1279
  * @default
1276
1280
  * @memberof Settings */
@@ -1353,7 +1357,7 @@ let touchGamepadEnable = false;
1353
1357
  * @memberof Settings */
1354
1358
  let touchGamepadAnalog = true;
1355
1359
 
1356
- /** Size of virutal gamepad for touch devices in pixels
1360
+ /** Size of virtual gamepad for touch devices in pixels
1357
1361
  * @type {Number}
1358
1362
  * @default
1359
1363
  * @memberof Settings */
@@ -1640,7 +1644,7 @@ function setDebugKey(key) { debugKey = key; }
1640
1644
  * - Automatically adds self to object list
1641
1645
  * - Will be updated and rendered each frame
1642
1646
  * - Renders as a sprite from a tilesheet by default
1643
- * - Can have color and addtive color applied
1647
+ * - Can have color and additive color applied
1644
1648
  * - 2D Physics and collision system
1645
1649
  * - Sorted by renderOrder
1646
1650
  * - Objects can have children attached
@@ -1919,7 +1923,7 @@ class EngineObject
1919
1923
  }
1920
1924
 
1921
1925
  /** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
1922
- destroy()
1926
+ destroy()
1923
1927
  {
1924
1928
  if (this.destroyed)
1925
1929
  return;
@@ -2108,13 +2112,9 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
2108
2112
  if (typeof pos === 'number')
2109
2113
  {
2110
2114
  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();
2115
+ ASSERT(textureInfo, 'Texture not loaded');
2116
+ const cols = textureInfo.size.x / size.x |0;
2117
+ pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
2118
2118
  }
2119
2119
 
2120
2120
  // return a tile info object
@@ -2141,13 +2141,23 @@ class TileInfo
2141
2141
  this.textureIndex = textureIndex;
2142
2142
  }
2143
2143
 
2144
- /** Returns an offset copy of this tile, useful for animation
2144
+ /** Returns a copy of this tile offset by a vector
2145
2145
  * @param {Vector2} offset - Offset to apply in pixels
2146
2146
  * @return {TileInfo}
2147
2147
  */
2148
2148
  offset(offset)
2149
2149
  { return new TileInfo(this.pos.add(offset), this.size, this.textureIndex); }
2150
2150
 
2151
+ /** Returns a copy of this tile offset by a number of animation frames
2152
+ * @param {Number} frame - Offset to apply in animation frames
2153
+ * @return {TileInfo}
2154
+ */
2155
+ frame(frame)
2156
+ {
2157
+ ASSERT(typeof frame == 'number');
2158
+ return this.offset(vec2(frame*this.size.x, 0));
2159
+ }
2160
+
2151
2161
  /** Returns the texture info for this tile
2152
2162
  * @return {TextureInfo}
2153
2163
  */
@@ -2203,6 +2213,11 @@ function worldToScreen(worldPos)
2203
2213
  );
2204
2214
  }
2205
2215
 
2216
+ /** Get the camera's visible area in world space
2217
+ * @return {Vector2}
2218
+ * @memberof Draw */
2219
+ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
2220
+
2206
2221
  /** Draw textured tile centered in world space, with color applied if using WebGL
2207
2222
  * @param {Vector2} pos - Center of the tile in world space
2208
2223
  * @param {Vector2} [size=(1,1)] - Size of the tile in world space
@@ -2293,21 +2308,6 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
2293
2308
  drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
2294
2309
  }
2295
2310
 
2296
- /** Draw colored polygon using passed in points
2297
- * @param {Array} points - Array of Vector2 points
2298
- * @param {Color} [color=(1,1,1,1)]
2299
- * @param {Boolean} [screenSpace=false]
2300
- * @param {CanvasRenderingContext2D} [context=mainContext]
2301
- * @memberof Draw */
2302
- function drawPoly(points, color=new Color, screenSpace, context=mainContext)
2303
- {
2304
- context.fillStyle = color.toString();
2305
- context.beginPath();
2306
- for (const point of screenSpace ? points : points.map(worldToScreen))
2307
- context.lineTo(point.x, point.y);
2308
- context.fill();
2309
- }
2310
-
2311
2311
  /** Draw colored line between two points
2312
2312
  * @param {Vector2} posA
2313
2313
  * @param {Vector2} posB
@@ -2734,9 +2734,21 @@ function mouseToScreen(mousePos)
2734
2734
  ///////////////////////////////////////////////////////////////////////////////
2735
2735
  // Gamepad input
2736
2736
 
2737
+ // gamepad internal variables
2737
2738
  const stickData = [];
2739
+
2740
+ // gamepads are updated by engine every frame automatically
2738
2741
  function gamepadsUpdate()
2739
2742
  {
2743
+ const applyDeadZones = (v)=>
2744
+ {
2745
+ const min=.3, max=.8;
2746
+ const deadZone = (v)=>
2747
+ v > min ? percent( v, min, max) :
2748
+ v < -min ? -percent(-v, min, max) : 0;
2749
+ return vec2(deadZone(v.x), deadZone(-v.y)).clampLength();
2750
+ }
2751
+
2740
2752
  // update touch gamepad if enabled
2741
2753
  if (touchGamepadEnable && isTouchDevice)
2742
2754
  {
@@ -2748,7 +2760,16 @@ function gamepadsUpdate()
2748
2760
  {
2749
2761
  // read virtual analog stick
2750
2762
  const sticks = stickData[0] || (stickData[0] = []);
2751
- sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
2763
+ sticks[0] = vec2();
2764
+ if (touchGamepadAnalog)
2765
+ sticks[0] = applyDeadZones(touchGamepadStick);
2766
+ else if (touchGamepadStick.lengthSquared() > .3)
2767
+ {
2768
+ // convert to 8 way dpad
2769
+ sticks[0].x = Math.round(touchGamepadStick.x);
2770
+ sticks[0].y = -Math.round(touchGamepadStick.y);
2771
+ sticks[0] = sticks[0].clampLength();
2772
+ }
2752
2773
 
2753
2774
  // read virtual gamepad buttons
2754
2775
  const data = inputData[1] || (inputData[1] = []);
@@ -2760,7 +2781,12 @@ function gamepadsUpdate()
2760
2781
  }
2761
2782
  }
2762
2783
 
2763
- if (!gamepadsEnable || !navigator || !navigator.getGamepads || !document.hasFocus() && !debug)
2784
+ // return if gamepads are disabled or not supported
2785
+ if (!gamepadsEnable || !navigator || !navigator.getGamepads)
2786
+ return;
2787
+
2788
+ // only poll gamepads when focused or in debug mode
2789
+ if (!debug && !document.hasFocus())
2764
2790
  return;
2765
2791
 
2766
2792
  // poll gamepads
@@ -2774,14 +2800,9 @@ function gamepadsUpdate()
2774
2800
 
2775
2801
  if (gamepad)
2776
2802
  {
2777
- // read clamp dead zone of analog sticks
2778
- const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
2779
- v > deadZone ? percent( v, deadZone, deadZoneMax) :
2780
- v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
2781
-
2782
2803
  // read analog sticks
2783
2804
  for (let j = 0; j < gamepad.axes.length-1; j+=2)
2784
- sticks[j>>1] = vec2(applyDeadZone(gamepad.axes[j]), applyDeadZone(-gamepad.axes[j+1])).clampLength();
2805
+ sticks[j>>1] = applyDeadZones(vec2(gamepad.axes[j],gamepad.axes[j+1]));
2785
2806
 
2786
2807
  // read buttons
2787
2808
  for (let j = gamepad.buttons.length; j--;)
@@ -2880,7 +2901,7 @@ function createTouchGamepad()
2880
2901
  touchGamepadStick = vec2();
2881
2902
 
2882
2903
  const touchHandler = ontouchstart;
2883
- ontouchstart = ontouchmove = ontouchend = (e)=>
2904
+ ontouchstart = ontouchmove = ontouchend = (e)=>
2884
2905
  {
2885
2906
  // clear touch gamepad input
2886
2907
  touchGamepadStick = vec2();
@@ -2910,14 +2931,7 @@ function createTouchGamepad()
2910
2931
  if (touchPos.distance(stickCenter) < touchGamepadSize)
2911
2932
  {
2912
2933
  // virtual analog stick
2913
- if (touchGamepadAnalog)
2914
- touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
2915
- else
2916
- {
2917
- // 8 way dpad
2918
- const angle = touchPos.subtract(stickCenter).angle();
2919
- touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
2920
- }
2934
+ touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
2921
2935
  }
2922
2936
  else if (touchPos.distance(buttonCenter) < touchGamepadSize)
2923
2937
  {
@@ -3010,7 +3024,7 @@ function touchGamepadRender()
3010
3024
 
3011
3025
 
3012
3026
  /**
3013
- * Sound Object - Stores a zzfx sound for later use and can be played positionally
3027
+ * Sound Object - Stores a sound for later use and can be played positionally
3014
3028
  *
3015
3029
  * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
3016
3030
  * @example
@@ -3025,7 +3039,7 @@ class Sound
3025
3039
  /** Create a sound object and cache the zzfx samples for later use
3026
3040
  * @param {Array} zzfxSound - Array of zzfx parameters, ex. [.5,.5]
3027
3041
  * @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
3042
+ * @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
3029
3043
  */
3030
3044
  constructor(zzfxSound, range=soundDefaultRange, taper=soundDefaultTaper)
3031
3045
  {
@@ -3176,7 +3190,7 @@ class SoundWave extends Sound
3176
3190
  * 1, 0, 9, 1 // channel notes
3177
3191
  * ],
3178
3192
  * [ // channel 1
3179
- * 0, 1, // instrument 1, right speaker
3193
+ * 0, 1, // instrument 0, right speaker
3180
3194
  * 0, 12, 17, -1 // channel notes
3181
3195
  * ]
3182
3196
  * ],
@@ -3205,24 +3219,24 @@ class Music extends Sound
3205
3219
 
3206
3220
  /** Play the music
3207
3221
  * @param {Number} [volume=1] - How much to scale volume by
3208
- * @param {Boolean} [loop=1] - True if the music should loop
3222
+ * @param {Boolean} [loop] - True if the music should loop
3209
3223
  * @return {AudioBufferSourceNode} - The audio source node
3210
3224
  */
3211
- playMusic(volume, loop = false)
3225
+ playMusic(volume, loop=false)
3212
3226
  { return super.play(undefined, volume, 1, 1, loop); }
3213
3227
  }
3214
3228
 
3215
3229
  /** Play an mp3, ogg, or wav audio from a local file or url
3216
- * @param {String} url - Location of sound file to play
3230
+ * @param {String} filename - Location of sound file to play
3217
3231
  * @param {Number} [volume] - How much to scale volume by
3218
3232
  * @param {Boolean} [loop] - True if the music should loop
3219
3233
  * @return {HTMLAudioElement} - The audio element for this sound
3220
3234
  * @memberof Audio */
3221
- function playAudioFile(url, volume=1, loop=false)
3235
+ function playAudioFile(filename, volume=1, loop=false)
3222
3236
  {
3223
3237
  if (!soundEnable) return;
3224
3238
 
3225
- const audio = new Audio(url);
3239
+ const audio = new Audio(filename);
3226
3240
  audio.volume = soundVolume * volume;
3227
3241
  audio.loop = loop;
3228
3242
  audio.play();
@@ -3274,6 +3288,11 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
3274
3288
  * @memberof Audio */
3275
3289
  let audioContext = new AudioContext;
3276
3290
 
3291
+ /** Keep track if audio was suspended when last sound was played
3292
+ * @type {Boolean}
3293
+ * @memberof Audio */
3294
+ let audioSuspended = false;
3295
+
3277
3296
  /** Play cached audio samples with given settings
3278
3297
  * @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
3279
3298
  * @param {Number} [volume] - How much to scale volume by
@@ -3288,11 +3307,15 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
3288
3307
  if (!soundEnable) return;
3289
3308
 
3290
3309
  // prevent sounds from building up if they can't be played
3291
- if (audioContext.state != 'running')
3310
+ const audioWasSuspended = audioSuspended;
3311
+ if (audioSuspended = audioContext.state != 'running')
3292
3312
  {
3293
3313
  // fix stalled audio
3294
3314
  audioContext.resume();
3295
- return;
3315
+
3316
+ // prevent suspended sounds from building up
3317
+ if (audioWasSuspended)
3318
+ return;
3296
3319
  }
3297
3320
 
3298
3321
  // create buffer and source
@@ -3560,7 +3583,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
3560
3583
  /**
3561
3584
  * LittleJS Tile Layer System
3562
3585
  * - Caches arrays of tiles to off screen canvas for fast rendering
3563
- * - Unlimted numbers of layers, allocates canvases as needed
3586
+ * - Unlimited numbers of layers, allocates canvases as needed
3564
3587
  * - Interfaces with EngineObject for collision
3565
3588
  * - Collision layer is separate from visible layers
3566
3589
  * - It is recommended to have a visible layer that matches the collision
@@ -3612,7 +3635,7 @@ function getTileCollisionData(pos)
3612
3635
 
3613
3636
  /** Check if collision with another object should occur
3614
3637
  * @param {Vector2} pos
3615
- * @param {Vector2} [size=(1,1)]
3638
+ * @param {Vector2} [size=(0,0)]
3616
3639
  * @param {EngineObject} [object]
3617
3640
  * @return {Boolean}
3618
3641
  * @memberof TileCollision */
@@ -4129,16 +4152,17 @@ class ParticleEmitter extends EngineObject
4129
4152
 
4130
4153
  // build particle
4131
4154
  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);
4155
+ particle.velocity = vec2().setAngle(velocityAngle, speed);
4156
+ particle.angleVelocity = angleSpeed;
4157
+ particle.fadeRate = this.fadeRate;
4158
+ particle.damping = this.damping;
4159
+ particle.angleDamping = this.angleDamping;
4160
+ particle.elasticity = this.elasticity;
4161
+ particle.friction = this.friction;
4162
+ particle.gravityScale = this.gravityScale;
4163
+ particle.collideTiles = this.collideTiles;
4164
+ particle.renderOrder = this.renderOrder;
4165
+ particle.mirror = !!randInt(2);
4142
4166
 
4143
4167
  // call particle create callaback
4144
4168
  this.particleCreateCallback && this.particleCreateCallback(particle);
@@ -4529,7 +4553,7 @@ class Newgrounds
4529
4553
  }
4530
4554
 
4531
4555
  // build the input object
4532
- const input =
4556
+ const input =
4533
4557
  {
4534
4558
  'app_id': this.app_id,
4535
4559
  'session_id': this.session_id,
@@ -4744,8 +4768,6 @@ function glCreateTexture(image)
4744
4768
  const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
4745
4769
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
4746
4770
  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
4771
 
4750
4772
  return texture;
4751
4773
  }
@@ -4823,7 +4845,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
4823
4845
  ///////////////////////////////////////////////////////////////////////////////
4824
4846
  // post processing - can be enabled to pass other canvases through a final shader
4825
4847
 
4826
- let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
4848
+ let glPostShader, glPostTexture, glPostIncludeOverlay;
4827
4849
 
4828
4850
  /** Set up a post processing shader
4829
4851
  * @param {String} shaderCode
@@ -4859,7 +4881,6 @@ function glInitPostProcess(shaderCode, includeOverlay=false)
4859
4881
  );
4860
4882
 
4861
4883
  // create buffer and texture
4862
- glPostArrayBuffer = glContext.createBuffer();
4863
4884
  glPostTexture = glCreateTexture(undefined);
4864
4885
  glPostIncludeOverlay = includeOverlay;
4865
4886
 
@@ -4931,10 +4952,7 @@ gl_NEAREST = 9728,
4931
4952
  gl_LINEAR = 9729,
4932
4953
  gl_TEXTURE_MAG_FILTER = 10240,
4933
4954
  gl_TEXTURE_MIN_FILTER = 10241,
4934
- gl_TEXTURE_WRAP_S = 10242,
4935
- gl_TEXTURE_WRAP_T = 10243,
4936
4955
  gl_COLOR_BUFFER_BIT = 16384,
4937
- gl_CLAMP_TO_EDGE = 33071,
4938
4956
  gl_TEXTURE0 = 33984,
4939
4957
  gl_ARRAY_BUFFER = 34962,
4940
4958
  gl_STATIC_DRAW = 35044,
@@ -4951,7 +4969,7 @@ gl_MAX_INSTANCES = 1e4,
4951
4969
  gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
4952
4970
  gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
4953
4971
  /**
4954
- * LittleJS - The Tiny JavaScript Game Engine That Can!
4972
+ * LittleJS - The Tiny Fast JavaScript Game Engine
4955
4973
  * MIT License - Copyright 2021 Frank Force
4956
4974
  *
4957
4975
  * Engine Features
@@ -4982,9 +5000,9 @@ const engineName = 'LittleJS';
4982
5000
  * @type {String}
4983
5001
  * @default
4984
5002
  * @memberof Engine */
4985
- const engineVersion = '1.9.2';
5003
+ const engineVersion = '1.9.4';
4986
5004
 
4987
- /** Frames per second to update objects
5005
+ /** Frames per second to update
4988
5006
  * @type {Number}
4989
5007
  * @default
4990
5008
  * @memberof Engine */
@@ -5001,7 +5019,7 @@ const timeDelta = 1/frameRate;
5001
5019
  * @memberof Engine */
5002
5020
  let engineObjects = [];
5003
5021
 
5004
- /** Array containing only objects that are set to collide with other objects this frame (for optimization)
5022
+ /** Array with only objects set to collide with other objects this frame (for optimization)
5005
5023
  * @type {Array}
5006
5024
  * @memberof Engine */
5007
5025
  let engineObjectsCollide = [];
@@ -5011,7 +5029,7 @@ let engineObjectsCollide = [];
5011
5029
  * @memberof Engine */
5012
5030
  let frame = 0;
5013
5031
 
5014
- /** Current engine time since start in seconds, derived from frame
5032
+ /** Current engine time since start in seconds
5015
5033
  * @type {Number}
5016
5034
  * @memberof Engine */
5017
5035
  let time = 0;
@@ -5037,12 +5055,12 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
5037
5055
 
5038
5056
  ///////////////////////////////////////////////////////////////////////////////
5039
5057
 
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
5058
+ /** Startup LittleJS engine with your callback functions
5059
+ * @param {Function} gameInit - Called once after the engine starts up, setup the game
5060
+ * @param {Function} gameUpdate - Called every frame at 60 frames per second, handle input and update the game state
5061
+ * @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
5062
+ * @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
5063
+ * @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
5046
5064
  * @param {Array} [imageSources=['tiles.png']] - Image to load
5047
5065
  * @memberof Engine */
5048
5066
  function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
@@ -5064,33 +5082,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5064
5082
  timeReal += frameTimeDeltaMS / 1e3;
5065
5083
  frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
5066
5084
  if (!debugSpeedUp)
5067
- 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);
5085
+ frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp in case of slow framerate
5086
+ updateCanvas();
5094
5087
 
5095
5088
  if (paused)
5096
5089
  {
@@ -5164,6 +5157,35 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5164
5157
  requestAnimationFrame(engineUpdate);
5165
5158
  }
5166
5159
 
5160
+ function updateCanvas()
5161
+ {
5162
+ if (canvasFixedSize.x)
5163
+ {
5164
+ // clear canvas and set fixed size
5165
+ mainCanvas.width = canvasFixedSize.x;
5166
+ mainCanvas.height = canvasFixedSize.y;
5167
+
5168
+ // fit to window by adding space on top or bottom if necessary
5169
+ const aspect = innerWidth / innerHeight;
5170
+ const fixedAspect = mainCanvas.width / mainCanvas.height;
5171
+ (glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
5172
+ (glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
5173
+ }
5174
+ else
5175
+ {
5176
+ // clear canvas and set size to same as window
5177
+ mainCanvas.width = min(innerWidth, canvasMaxSize.x);
5178
+ mainCanvas.height = min(innerHeight, canvasMaxSize.y);
5179
+ }
5180
+
5181
+ // clear overlay canvas and set size
5182
+ overlayCanvas.width = mainCanvas.width;
5183
+ overlayCanvas.height = mainCanvas.height;
5184
+
5185
+ // save canvas size
5186
+ mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
5187
+ }
5188
+
5167
5189
  // setup html
5168
5190
  const styleBody =
5169
5191
  'margin:0;overflow:hidden;' + // fill the window
@@ -5188,6 +5210,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5188
5210
  const styleCanvas = 'position:absolute;' + // position
5189
5211
  'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
5190
5212
  (glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
5213
+ updateCanvas();
5191
5214
 
5192
5215
  // create promises for loading images
5193
5216
  const promises = imageSources.map((src, textureIndex)=>
@@ -5384,9 +5407,9 @@ function drawEngineSplashScreen(t)
5384
5407
 
5385
5408
  // big stack
5386
5409
  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);
5410
+ rect(50,20,6.5,-10,color(0,2));
5411
+ rect(50,20,3.5,-10,color(0,3));
5412
+ rect(50,20,10,-10);
5390
5413
  circle(55,2,11.4,.5,PI-.5,color(3,3));
5391
5414
  circle(55,2,11.4,.5,PI/2,color(3,2),1);
5392
5415
  circle(55,2,11.4,.5,PI-.5);
@@ -5405,7 +5428,7 @@ function drawEngineSplashScreen(t)
5405
5428
 
5406
5429
  // engine outline
5407
5430
  circle(36,30,10,PI/2,PI*3/2);
5408
- circle(47,30,10,PI/2,PI*3/2);
5431
+ circle(48,30,10,PI/2,PI*3/2);
5409
5432
  circle(60,30,10);
5410
5433
  line(36,20,60,20);
5411
5434