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
@@ -71,7 +71,7 @@ function min(valueA, valueB) { return Math.min(valueA, valueB); }
71
71
  * @memberof Utilities */
72
72
  function max(valueA, valueB) { return Math.max(valueA, valueB); }
73
73
 
74
- /** Returns the sign of value passed in (also returns 1 if 0)
74
+ /** Returns the sign of value passed in
75
75
  * @param {Number} value
76
76
  * @return {Number}
77
77
  * @memberof Utilities */
@@ -118,7 +118,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
118
118
  function distanceWrap(valueA, valueB, wrapSize=1)
119
119
  { const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
120
120
 
121
- /** Linearly interpolates between values passed in with wrappping
121
+ /** Linearly interpolates between values passed in with wrapping
122
122
  * @param {Number} percent
123
123
  * @param {Number} valueA
124
124
  * @param {Number} valueB
@@ -135,7 +135,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
135
135
  * @memberof Utilities */
136
136
  function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
137
137
 
138
- /** Linearly interpolates between the angles passed in with wrappping
138
+ /** Linearly interpolates between the angles passed in with wrapping
139
139
  * @param {Number} percent
140
140
  * @param {Number} angleA
141
141
  * @param {Number} angleB
@@ -665,7 +665,7 @@ class Color
665
665
 
666
666
  /** Returns this color expressed in hsla format
667
667
  * @return {Array} */
668
- getHSLA()
668
+ HSLA()
669
669
  {
670
670
  const r = clamp(this.r);
671
671
  const g = clamp(this.g);
@@ -877,7 +877,7 @@ let tileSizeDefault = vec2(16);
877
877
  * @type {Number}
878
878
  * @default
879
879
  * @memberof Settings */
880
- let tileFixBleedScale = .3;
880
+ let tileFixBleedScale = .1;
881
881
 
882
882
  ///////////////////////////////////////////////////////////////////////////////
883
883
  // Object settings
@@ -888,7 +888,7 @@ let tileFixBleedScale = .3;
888
888
  * @memberof Settings */
889
889
  let enablePhysicsSolver = true;
890
890
 
891
- /** Default object mass for collison calcuations (how heavy objects are)
891
+ /** Default object mass for collision calcuations (how heavy objects are)
892
892
  * @type {Number}
893
893
  * @default
894
894
  * @memberof Settings */
@@ -971,7 +971,7 @@ let touchGamepadEnable = false;
971
971
  * @memberof Settings */
972
972
  let touchGamepadAnalog = true;
973
973
 
974
- /** Size of virutal gamepad for touch devices in pixels
974
+ /** Size of virtual gamepad for touch devices in pixels
975
975
  * @type {Number}
976
976
  * @default
977
977
  * @memberof Settings */
@@ -1258,7 +1258,7 @@ function setDebugKey(key) { debugKey = key; }
1258
1258
  * - Automatically adds self to object list
1259
1259
  * - Will be updated and rendered each frame
1260
1260
  * - Renders as a sprite from a tilesheet by default
1261
- * - Can have color and addtive color applied
1261
+ * - Can have color and additive color applied
1262
1262
  * - 2D Physics and collision system
1263
1263
  * - Sorted by renderOrder
1264
1264
  * - Objects can have children attached
@@ -1537,7 +1537,7 @@ class EngineObject
1537
1537
  }
1538
1538
 
1539
1539
  /** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
1540
- destroy()
1540
+ destroy()
1541
1541
  {
1542
1542
  if (this.destroyed)
1543
1543
  return;
@@ -1726,13 +1726,9 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
1726
1726
  if (typeof pos === 'number')
1727
1727
  {
1728
1728
  const textureInfo = textureInfos[textureIndex];
1729
- if (textureInfo)
1730
- {
1731
- const cols = textureInfo.size.x / size.x |0;
1732
- pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
1733
- }
1734
- else
1735
- pos = vec2();
1729
+ ASSERT(textureInfo, 'Texture not loaded');
1730
+ const cols = textureInfo.size.x / size.x |0;
1731
+ pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
1736
1732
  }
1737
1733
 
1738
1734
  // return a tile info object
@@ -1759,13 +1755,23 @@ class TileInfo
1759
1755
  this.textureIndex = textureIndex;
1760
1756
  }
1761
1757
 
1762
- /** Returns an offset copy of this tile, useful for animation
1758
+ /** Returns a copy of this tile offset by a vector
1763
1759
  * @param {Vector2} offset - Offset to apply in pixels
1764
1760
  * @return {TileInfo}
1765
1761
  */
1766
1762
  offset(offset)
1767
1763
  { return new TileInfo(this.pos.add(offset), this.size, this.textureIndex); }
1768
1764
 
1765
+ /** Returns a copy of this tile offset by a number of animation frames
1766
+ * @param {Number} frame - Offset to apply in animation frames
1767
+ * @return {TileInfo}
1768
+ */
1769
+ frame(frame)
1770
+ {
1771
+ ASSERT(typeof frame == 'number');
1772
+ return this.offset(vec2(frame*this.size.x, 0));
1773
+ }
1774
+
1769
1775
  /** Returns the texture info for this tile
1770
1776
  * @return {TextureInfo}
1771
1777
  */
@@ -1821,6 +1827,11 @@ function worldToScreen(worldPos)
1821
1827
  );
1822
1828
  }
1823
1829
 
1830
+ /** Get the camera's visible area in world space
1831
+ * @return {Vector2}
1832
+ * @memberof Draw */
1833
+ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
1834
+
1824
1835
  /** Draw textured tile centered in world space, with color applied if using WebGL
1825
1836
  * @param {Vector2} pos - Center of the tile in world space
1826
1837
  * @param {Vector2} [size=(1,1)] - Size of the tile in world space
@@ -1911,21 +1922,6 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
1911
1922
  drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
1912
1923
  }
1913
1924
 
1914
- /** Draw colored polygon using passed in points
1915
- * @param {Array} points - Array of Vector2 points
1916
- * @param {Color} [color=(1,1,1,1)]
1917
- * @param {Boolean} [screenSpace=false]
1918
- * @param {CanvasRenderingContext2D} [context=mainContext]
1919
- * @memberof Draw */
1920
- function drawPoly(points, color=new Color, screenSpace, context=mainContext)
1921
- {
1922
- context.fillStyle = color.toString();
1923
- context.beginPath();
1924
- for (const point of screenSpace ? points : points.map(worldToScreen))
1925
- context.lineTo(point.x, point.y);
1926
- context.fill();
1927
- }
1928
-
1929
1925
  /** Draw colored line between two points
1930
1926
  * @param {Vector2} posA
1931
1927
  * @param {Vector2} posB
@@ -2352,9 +2348,21 @@ function mouseToScreen(mousePos)
2352
2348
  ///////////////////////////////////////////////////////////////////////////////
2353
2349
  // Gamepad input
2354
2350
 
2351
+ // gamepad internal variables
2355
2352
  const stickData = [];
2353
+
2354
+ // gamepads are updated by engine every frame automatically
2356
2355
  function gamepadsUpdate()
2357
2356
  {
2357
+ const applyDeadZones = (v)=>
2358
+ {
2359
+ const min=.3, max=.8;
2360
+ const deadZone = (v)=>
2361
+ v > min ? percent( v, min, max) :
2362
+ v < -min ? -percent(-v, min, max) : 0;
2363
+ return vec2(deadZone(v.x), deadZone(-v.y)).clampLength();
2364
+ }
2365
+
2358
2366
  // update touch gamepad if enabled
2359
2367
  if (touchGamepadEnable && isTouchDevice)
2360
2368
  {
@@ -2366,7 +2374,16 @@ function gamepadsUpdate()
2366
2374
  {
2367
2375
  // read virtual analog stick
2368
2376
  const sticks = stickData[0] || (stickData[0] = []);
2369
- sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
2377
+ sticks[0] = vec2();
2378
+ if (touchGamepadAnalog)
2379
+ sticks[0] = applyDeadZones(touchGamepadStick);
2380
+ else if (touchGamepadStick.lengthSquared() > .3)
2381
+ {
2382
+ // convert to 8 way dpad
2383
+ sticks[0].x = Math.round(touchGamepadStick.x);
2384
+ sticks[0].y = -Math.round(touchGamepadStick.y);
2385
+ sticks[0] = sticks[0].clampLength();
2386
+ }
2370
2387
 
2371
2388
  // read virtual gamepad buttons
2372
2389
  const data = inputData[1] || (inputData[1] = []);
@@ -2378,7 +2395,12 @@ function gamepadsUpdate()
2378
2395
  }
2379
2396
  }
2380
2397
 
2381
- if (!gamepadsEnable || !navigator || !navigator.getGamepads || !document.hasFocus() && !debug)
2398
+ // return if gamepads are disabled or not supported
2399
+ if (!gamepadsEnable || !navigator || !navigator.getGamepads)
2400
+ return;
2401
+
2402
+ // only poll gamepads when focused or in debug mode
2403
+ if (!debug && !document.hasFocus())
2382
2404
  return;
2383
2405
 
2384
2406
  // poll gamepads
@@ -2392,14 +2414,9 @@ function gamepadsUpdate()
2392
2414
 
2393
2415
  if (gamepad)
2394
2416
  {
2395
- // read clamp dead zone of analog sticks
2396
- const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
2397
- v > deadZone ? percent( v, deadZone, deadZoneMax) :
2398
- v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
2399
-
2400
2417
  // read analog sticks
2401
2418
  for (let j = 0; j < gamepad.axes.length-1; j+=2)
2402
- sticks[j>>1] = vec2(applyDeadZone(gamepad.axes[j]), applyDeadZone(-gamepad.axes[j+1])).clampLength();
2419
+ sticks[j>>1] = applyDeadZones(vec2(gamepad.axes[j],gamepad.axes[j+1]));
2403
2420
 
2404
2421
  // read buttons
2405
2422
  for (let j = gamepad.buttons.length; j--;)
@@ -2498,7 +2515,7 @@ function createTouchGamepad()
2498
2515
  touchGamepadStick = vec2();
2499
2516
 
2500
2517
  const touchHandler = ontouchstart;
2501
- ontouchstart = ontouchmove = ontouchend = (e)=>
2518
+ ontouchstart = ontouchmove = ontouchend = (e)=>
2502
2519
  {
2503
2520
  // clear touch gamepad input
2504
2521
  touchGamepadStick = vec2();
@@ -2528,14 +2545,7 @@ function createTouchGamepad()
2528
2545
  if (touchPos.distance(stickCenter) < touchGamepadSize)
2529
2546
  {
2530
2547
  // virtual analog stick
2531
- if (touchGamepadAnalog)
2532
- touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
2533
- else
2534
- {
2535
- // 8 way dpad
2536
- const angle = touchPos.subtract(stickCenter).angle();
2537
- touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
2538
- }
2548
+ touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
2539
2549
  }
2540
2550
  else if (touchPos.distance(buttonCenter) < touchGamepadSize)
2541
2551
  {
@@ -2628,7 +2638,7 @@ function touchGamepadRender()
2628
2638
 
2629
2639
 
2630
2640
  /**
2631
- * Sound Object - Stores a zzfx sound for later use and can be played positionally
2641
+ * Sound Object - Stores a sound for later use and can be played positionally
2632
2642
  *
2633
2643
  * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
2634
2644
  * @example
@@ -2643,7 +2653,7 @@ class Sound
2643
2653
  /** Create a sound object and cache the zzfx samples for later use
2644
2654
  * @param {Array} zzfxSound - Array of zzfx parameters, ex. [.5,.5]
2645
2655
  * @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
2646
- * @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
2656
+ * @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
2647
2657
  */
2648
2658
  constructor(zzfxSound, range=soundDefaultRange, taper=soundDefaultTaper)
2649
2659
  {
@@ -2794,7 +2804,7 @@ class SoundWave extends Sound
2794
2804
  * 1, 0, 9, 1 // channel notes
2795
2805
  * ],
2796
2806
  * [ // channel 1
2797
- * 0, 1, // instrument 1, right speaker
2807
+ * 0, 1, // instrument 0, right speaker
2798
2808
  * 0, 12, 17, -1 // channel notes
2799
2809
  * ]
2800
2810
  * ],
@@ -2823,24 +2833,24 @@ class Music extends Sound
2823
2833
 
2824
2834
  /** Play the music
2825
2835
  * @param {Number} [volume=1] - How much to scale volume by
2826
- * @param {Boolean} [loop=1] - True if the music should loop
2836
+ * @param {Boolean} [loop] - True if the music should loop
2827
2837
  * @return {AudioBufferSourceNode} - The audio source node
2828
2838
  */
2829
- playMusic(volume, loop = false)
2839
+ playMusic(volume, loop=false)
2830
2840
  { return super.play(undefined, volume, 1, 1, loop); }
2831
2841
  }
2832
2842
 
2833
2843
  /** Play an mp3, ogg, or wav audio from a local file or url
2834
- * @param {String} url - Location of sound file to play
2844
+ * @param {String} filename - Location of sound file to play
2835
2845
  * @param {Number} [volume] - How much to scale volume by
2836
2846
  * @param {Boolean} [loop] - True if the music should loop
2837
2847
  * @return {HTMLAudioElement} - The audio element for this sound
2838
2848
  * @memberof Audio */
2839
- function playAudioFile(url, volume=1, loop=false)
2849
+ function playAudioFile(filename, volume=1, loop=false)
2840
2850
  {
2841
2851
  if (!soundEnable) return;
2842
2852
 
2843
- const audio = new Audio(url);
2853
+ const audio = new Audio(filename);
2844
2854
  audio.volume = soundVolume * volume;
2845
2855
  audio.loop = loop;
2846
2856
  audio.play();
@@ -2892,6 +2902,11 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
2892
2902
  * @memberof Audio */
2893
2903
  let audioContext = new AudioContext;
2894
2904
 
2905
+ /** Keep track if audio was suspended when last sound was played
2906
+ * @type {Boolean}
2907
+ * @memberof Audio */
2908
+ let audioSuspended = false;
2909
+
2895
2910
  /** Play cached audio samples with given settings
2896
2911
  * @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
2897
2912
  * @param {Number} [volume] - How much to scale volume by
@@ -2906,11 +2921,15 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
2906
2921
  if (!soundEnable) return;
2907
2922
 
2908
2923
  // prevent sounds from building up if they can't be played
2909
- if (audioContext.state != 'running')
2924
+ const audioWasSuspended = audioSuspended;
2925
+ if (audioSuspended = audioContext.state != 'running')
2910
2926
  {
2911
2927
  // fix stalled audio
2912
2928
  audioContext.resume();
2913
- return;
2929
+
2930
+ // prevent suspended sounds from building up
2931
+ if (audioWasSuspended)
2932
+ return;
2914
2933
  }
2915
2934
 
2916
2935
  // create buffer and source
@@ -3178,7 +3197,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
3178
3197
  /**
3179
3198
  * LittleJS Tile Layer System
3180
3199
  * - Caches arrays of tiles to off screen canvas for fast rendering
3181
- * - Unlimted numbers of layers, allocates canvases as needed
3200
+ * - Unlimited numbers of layers, allocates canvases as needed
3182
3201
  * - Interfaces with EngineObject for collision
3183
3202
  * - Collision layer is separate from visible layers
3184
3203
  * - It is recommended to have a visible layer that matches the collision
@@ -3230,7 +3249,7 @@ function getTileCollisionData(pos)
3230
3249
 
3231
3250
  /** Check if collision with another object should occur
3232
3251
  * @param {Vector2} pos
3233
- * @param {Vector2} [size=(1,1)]
3252
+ * @param {Vector2} [size=(0,0)]
3234
3253
  * @param {EngineObject} [object]
3235
3254
  * @return {Boolean}
3236
3255
  * @memberof TileCollision */
@@ -3747,16 +3766,17 @@ class ParticleEmitter extends EngineObject
3747
3766
 
3748
3767
  // build particle
3749
3768
  const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
3750
- particle.velocity = vec2().setAngle(velocityAngle, speed);
3751
- particle.fadeRate = this.fadeRate;
3752
- particle.damping = this.damping;
3753
- particle.angleDamping = this.angleDamping;
3754
- particle.elasticity = this.elasticity;
3755
- particle.friction = this.friction;
3756
- particle.gravityScale = this.gravityScale;
3757
- particle.collideTiles = this.collideTiles;
3758
- particle.renderOrder = this.renderOrder;
3759
- particle.mirror = !!randInt(2);
3769
+ particle.velocity = vec2().setAngle(velocityAngle, speed);
3770
+ particle.angleVelocity = angleSpeed;
3771
+ particle.fadeRate = this.fadeRate;
3772
+ particle.damping = this.damping;
3773
+ particle.angleDamping = this.angleDamping;
3774
+ particle.elasticity = this.elasticity;
3775
+ particle.friction = this.friction;
3776
+ particle.gravityScale = this.gravityScale;
3777
+ particle.collideTiles = this.collideTiles;
3778
+ particle.renderOrder = this.renderOrder;
3779
+ particle.mirror = !!randInt(2);
3760
3780
 
3761
3781
  // call particle create callaback
3762
3782
  this.particleCreateCallback && this.particleCreateCallback(particle);
@@ -4147,7 +4167,7 @@ class Newgrounds
4147
4167
  }
4148
4168
 
4149
4169
  // build the input object
4150
- const input =
4170
+ const input =
4151
4171
  {
4152
4172
  'app_id': this.app_id,
4153
4173
  'session_id': this.session_id,
@@ -4362,8 +4382,6 @@ function glCreateTexture(image)
4362
4382
  const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
4363
4383
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
4364
4384
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
4365
- glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
4366
- glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE);
4367
4385
 
4368
4386
  return texture;
4369
4387
  }
@@ -4441,7 +4459,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
4441
4459
  ///////////////////////////////////////////////////////////////////////////////
4442
4460
  // post processing - can be enabled to pass other canvases through a final shader
4443
4461
 
4444
- let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
4462
+ let glPostShader, glPostTexture, glPostIncludeOverlay;
4445
4463
 
4446
4464
  /** Set up a post processing shader
4447
4465
  * @param {String} shaderCode
@@ -4477,7 +4495,6 @@ function glInitPostProcess(shaderCode, includeOverlay=false)
4477
4495
  );
4478
4496
 
4479
4497
  // create buffer and texture
4480
- glPostArrayBuffer = glContext.createBuffer();
4481
4498
  glPostTexture = glCreateTexture(undefined);
4482
4499
  glPostIncludeOverlay = includeOverlay;
4483
4500
 
@@ -4549,10 +4566,7 @@ gl_NEAREST = 9728,
4549
4566
  gl_LINEAR = 9729,
4550
4567
  gl_TEXTURE_MAG_FILTER = 10240,
4551
4568
  gl_TEXTURE_MIN_FILTER = 10241,
4552
- gl_TEXTURE_WRAP_S = 10242,
4553
- gl_TEXTURE_WRAP_T = 10243,
4554
4569
  gl_COLOR_BUFFER_BIT = 16384,
4555
- gl_CLAMP_TO_EDGE = 33071,
4556
4570
  gl_TEXTURE0 = 33984,
4557
4571
  gl_ARRAY_BUFFER = 34962,
4558
4572
  gl_STATIC_DRAW = 35044,
@@ -4569,7 +4583,7 @@ gl_MAX_INSTANCES = 1e4,
4569
4583
  gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
4570
4584
  gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
4571
4585
  /**
4572
- * LittleJS - The Tiny JavaScript Game Engine That Can!
4586
+ * LittleJS - The Tiny Fast JavaScript Game Engine
4573
4587
  * MIT License - Copyright 2021 Frank Force
4574
4588
  *
4575
4589
  * Engine Features
@@ -4600,9 +4614,9 @@ const engineName = 'LittleJS';
4600
4614
  * @type {String}
4601
4615
  * @default
4602
4616
  * @memberof Engine */
4603
- const engineVersion = '1.9.2';
4617
+ const engineVersion = '1.9.4';
4604
4618
 
4605
- /** Frames per second to update objects
4619
+ /** Frames per second to update
4606
4620
  * @type {Number}
4607
4621
  * @default
4608
4622
  * @memberof Engine */
@@ -4619,7 +4633,7 @@ const timeDelta = 1/frameRate;
4619
4633
  * @memberof Engine */
4620
4634
  let engineObjects = [];
4621
4635
 
4622
- /** Array containing only objects that are set to collide with other objects this frame (for optimization)
4636
+ /** Array with only objects set to collide with other objects this frame (for optimization)
4623
4637
  * @type {Array}
4624
4638
  * @memberof Engine */
4625
4639
  let engineObjectsCollide = [];
@@ -4629,7 +4643,7 @@ let engineObjectsCollide = [];
4629
4643
  * @memberof Engine */
4630
4644
  let frame = 0;
4631
4645
 
4632
- /** Current engine time since start in seconds, derived from frame
4646
+ /** Current engine time since start in seconds
4633
4647
  * @type {Number}
4634
4648
  * @memberof Engine */
4635
4649
  let time = 0;
@@ -4655,12 +4669,12 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
4655
4669
 
4656
4670
  ///////////////////////////////////////////////////////////////////////////////
4657
4671
 
4658
- /** Start up LittleJS engine with your callback functions
4659
- * @param {Function} gameInit - Called once after the engine starts up, setup the game
4660
- * @param {Function} gameUpdate - Called every frame at 60 frames per second, handle input and update the game state
4661
- * @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
4662
- * @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
4663
- * @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
4672
+ /** Startup LittleJS engine with your callback functions
4673
+ * @param {Function} gameInit - Called once after the engine starts up, setup the game
4674
+ * @param {Function} gameUpdate - Called every frame at 60 frames per second, handle input and update the game state
4675
+ * @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
4676
+ * @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
4677
+ * @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
4664
4678
  * @param {Array} [imageSources=['tiles.png']] - Image to load
4665
4679
  * @memberof Engine */
4666
4680
  function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
@@ -4682,33 +4696,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4682
4696
  timeReal += frameTimeDeltaMS / 1e3;
4683
4697
  frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
4684
4698
  if (!debugSpeedUp)
4685
- frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp incase of slow framerate
4686
-
4687
- if (canvasFixedSize.x)
4688
- {
4689
- // clear canvas and set fixed size
4690
- mainCanvas.width = canvasFixedSize.x;
4691
- mainCanvas.height = canvasFixedSize.y;
4692
-
4693
- // fit to window by adding space on top or bottom if necessary
4694
- const aspect = innerWidth / innerHeight;
4695
- const fixedAspect = mainCanvas.width / mainCanvas.height;
4696
- (glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
4697
- (glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
4698
- }
4699
- else
4700
- {
4701
- // clear canvas and set size to same as window
4702
- mainCanvas.width = min(innerWidth, canvasMaxSize.x);
4703
- mainCanvas.height = min(innerHeight, canvasMaxSize.y);
4704
- }
4705
-
4706
- // clear overlay canvas and set size
4707
- overlayCanvas.width = mainCanvas.width;
4708
- overlayCanvas.height = mainCanvas.height;
4709
-
4710
- // save canvas size
4711
- mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
4699
+ frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp in case of slow framerate
4700
+ updateCanvas();
4712
4701
 
4713
4702
  if (paused)
4714
4703
  {
@@ -4782,6 +4771,35 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4782
4771
  requestAnimationFrame(engineUpdate);
4783
4772
  }
4784
4773
 
4774
+ function updateCanvas()
4775
+ {
4776
+ if (canvasFixedSize.x)
4777
+ {
4778
+ // clear canvas and set fixed size
4779
+ mainCanvas.width = canvasFixedSize.x;
4780
+ mainCanvas.height = canvasFixedSize.y;
4781
+
4782
+ // fit to window by adding space on top or bottom if necessary
4783
+ const aspect = innerWidth / innerHeight;
4784
+ const fixedAspect = mainCanvas.width / mainCanvas.height;
4785
+ (glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
4786
+ (glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
4787
+ }
4788
+ else
4789
+ {
4790
+ // clear canvas and set size to same as window
4791
+ mainCanvas.width = min(innerWidth, canvasMaxSize.x);
4792
+ mainCanvas.height = min(innerHeight, canvasMaxSize.y);
4793
+ }
4794
+
4795
+ // clear overlay canvas and set size
4796
+ overlayCanvas.width = mainCanvas.width;
4797
+ overlayCanvas.height = mainCanvas.height;
4798
+
4799
+ // save canvas size
4800
+ mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
4801
+ }
4802
+
4785
4803
  // setup html
4786
4804
  const styleBody =
4787
4805
  'margin:0;overflow:hidden;' + // fill the window
@@ -4806,6 +4824,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4806
4824
  const styleCanvas = 'position:absolute;' + // position
4807
4825
  'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
4808
4826
  (glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
4827
+ updateCanvas();
4809
4828
 
4810
4829
  // create promises for loading images
4811
4830
  const promises = imageSources.map((src, textureIndex)=>
@@ -5002,9 +5021,9 @@ function drawEngineSplashScreen(t)
5002
5021
 
5003
5022
  // big stack
5004
5023
  rect(50,20,10,-10,color(0,1));
5005
- rect(50,20,6,-10,color(0,2));
5006
- rect(50,20,3,-10,color(0,3));
5007
- rect(50,10,10,10);
5024
+ rect(50,20,6.5,-10,color(0,2));
5025
+ rect(50,20,3.5,-10,color(0,3));
5026
+ rect(50,20,10,-10);
5008
5027
  circle(55,2,11.4,.5,PI-.5,color(3,3));
5009
5028
  circle(55,2,11.4,.5,PI/2,color(3,2),1);
5010
5029
  circle(55,2,11.4,.5,PI-.5);
@@ -5023,7 +5042,7 @@ function drawEngineSplashScreen(t)
5023
5042
 
5024
5043
  // engine outline
5025
5044
  circle(36,30,10,PI/2,PI*3/2);
5026
- circle(47,30,10,PI/2,PI*3/2);
5045
+ circle(48,30,10,PI/2,PI*3/2);
5027
5046
  circle(60,30,10);
5028
5047
  line(36,20,60,20);
5029
5048
 
package/examples/logo.png CHANGED
Binary file
@@ -8,7 +8,7 @@
8
8
 
9
9
  // import module
10
10
  import * as LittleJS from '../../dist/littlejs.esm.js';
11
- const {Vector2, Color, Timer, tile, vec2, hsl, rgb} = LittleJS;
11
+ const {tile, vec2, hsl} = LittleJS;
12
12
 
13
13
  // show the LittleJS splash screen
14
14
  LittleJS.setShowSplashScreen(true);
@@ -1,9 +1,9 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="256" height="64" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="1">
3
3
  <editorsettings>
4
- <export target="../gameTileData.js" format="js"/>
4
+ <export target="../gameLevelData.js" format="js"/>
5
5
  </editorsettings>
6
- <tileset firstgid="1" source="gameTileData.tsx"/>
6
+ <tileset firstgid="1" source="gameLevelData.tsx"/>
7
7
  <layer id="2" name="Tile Layer 2" width="256" height="64" tintcolor="#4d7caf">
8
8
  <data encoding="csv">
9
9
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -103,7 +103,7 @@
103
103
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,2,5,2,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,5,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,2,0,0,0,0,0,0,0,0,0,0,0,4,0,0,2,0,0,0,0,0,2,2,0,0,0,0,5,0,0,5,0,0,5,0,0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,2,2,0,0,0,0,0,0,5,0,0,5,0,0,0,0,0,0,0,0,0,0,5,5,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,5,2,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,0,0,0,0,0,0,0,0,4,0,0,0,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
104
104
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,5,5,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,5,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,0,0,5,5,0,0,0,0,0,0,0,0,5,5,5,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,4,0,0,0,20,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
105
105
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,5,5,0,0,0,0,0,0,18,0,5,5,0,0,0,0,0,0,5,5,0,0,18,0,0,0,0,0,0,5,4,5,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,0,0,5,5,5,0,0,0,0,0,0,5,5,5,5,0,0,5,5,5,0,0,0,0,0,0,18,18,0,0,0,0,0,0,0,0,0,0,0,0,0,18,18,0,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,4,0,0,0,20,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
106
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,19,0,4,0,0,0,5,5,0,0,0,0,0,18,18,0,5,5,0,0,19,0,0,0,5,5,0,18,18,18,0,19,19,0,0,5,4,5,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,19,0,19,0,0,0,0,0,0,0,19,0,0,19,0,0,0,0,0,4,0,19,0,0,0,0,0,0,0,0,0,5,5,5,5,0,0,5,5,5,5,0,19,0,0,5,5,5,5,5,0,0,5,5,5,5,0,0,0,0,0,18,18,0,0,0,0,0,19,0,0,0,19,0,0,0,18,18,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,5,0,0,0,20,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
106
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,19,0,4,0,0,0,5,5,0,0,0,0,0,18,18,0,5,5,0,0,19,0,0,0,5,5,0,18,18,18,0,19,0,0,0,5,4,5,0,0,0,0,0,0,0,0,0,0,5,5,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,4,0,19,0,0,0,0,0,0,0,0,0,5,5,5,5,0,0,5,5,5,5,0,19,0,0,5,5,5,5,5,0,0,5,5,5,5,0,0,0,0,0,18,18,0,0,0,0,0,19,0,0,0,19,0,0,0,18,18,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,5,0,0,0,20,20,20,20,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
107
107
  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,3,2,2,2,3,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,3,2,2,2,2,2,2,2,2,4,2,2,2,2,3,2,2,2,2,2,2,5,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
108
108
  2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,3,2,2,3,2,3,2,2,2,4,2,2,2,2,2,2,2,2,3,2,2,5,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,3,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,
109
109
  2,2,3,2,2,3,2,2,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,4,0,0,0,0,0,0,0,0,0,0,0,5,5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,3,2,2,2,2,3,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,0,0,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,