littlejsengine 1.9.8 → 1.9.11

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 (42) hide show
  1. package/README.md +2 -0
  2. package/dist/littlejs.d.ts +38 -5
  3. package/dist/littlejs.esm.js +106 -58
  4. package/dist/littlejs.esm.min.js +1 -1
  5. package/dist/littlejs.js +98 -56
  6. package/dist/littlejs.min.js +1 -1
  7. package/dist/littlejs.release.js +81 -54
  8. package/examples/box2d/game.js +34 -16
  9. package/examples/box2d/gameObjects.js +485 -0
  10. package/examples/box2d/index.html +6 -18
  11. package/examples/box2d/scenes.js +32 -251
  12. package/examples/box2d/tiles.png +0 -0
  13. package/examples/breakout/index.html +3 -3
  14. package/examples/breakoutTutorial/index.html +2 -2
  15. package/examples/electron/build.js +1 -1
  16. package/examples/electron/index.html +2 -2
  17. package/examples/js13k/build.js +19 -1
  18. package/examples/js13k/index.html +13 -13
  19. package/examples/module/index.html +1 -1
  20. package/examples/particles/index.html +1 -1
  21. package/examples/platformer/gameCharacter.js +1 -0
  22. package/examples/platformer/gameEffects.js +0 -2
  23. package/examples/platformer/gameObjects.js +1 -1
  24. package/examples/platformer/index.html +8 -8
  25. package/examples/puzzle/index.html +2 -2
  26. package/examples/starter/build.js +1 -1
  27. package/examples/starter/game.js +1 -1
  28. package/examples/starter/index.html +13 -13
  29. package/examples/stress/index.html +1 -1
  30. package/examples/typescript/index.html +1 -1
  31. package/package.json +1 -1
  32. package/plugins/box2d.js +126 -47
  33. package/plugins/postProcess.js +3 -2
  34. package/src/engine.js +12 -11
  35. package/src/engineAudio.js +16 -16
  36. package/src/engineBuild.js +1 -1
  37. package/src/engineDebug.js +17 -2
  38. package/src/engineExport.js +7 -2
  39. package/src/engineInput.js +13 -6
  40. package/src/engineMedals.js +3 -2
  41. package/src/engineObject.js +25 -19
  42. package/src/engineSettings.js +12 -0
package/README.md CHANGED
@@ -56,6 +56,7 @@ LittleJS is a small but powerful game engine with many features and no dependenc
56
56
 
57
57
  - Robust arcade physics system with collision handling
58
58
  - Very fast collision and raycasting for tile maps
59
+ - Full Box2d support using [super fast wasm build of box2d.js](https://github.com/kripken/box2d.js/)
59
60
 
60
61
  ### 🚀 Flexibility
61
62
 
@@ -209,6 +210,7 @@ Here are a few of the amazing games people made using LittleJS...
209
210
  - [Data Warrior: 13kb limit](https://js13kgames.com/2024/games/data-warrior-13kb-limit) - Vampire Survivors-like game by [rndD](https://github.com/rndD)
210
211
  - [The Way of the Dodo](https://js13kgames.com/2024/games/the-way-of-the-dodo) - Single button platformer by [repsej](https://github.com/repsej)
211
212
  - [Wendol Village](https://js13kgames.com/2024/games/wendol-village) - Warcraft-like RTS game by [sanojian](https://github.com/sanojian)
213
+ - [Unblock](https://js13kgames.com/games/unblock) - Retro style space shooter by [Isaac Benitez](https://github.com/isacben)
212
214
 
213
215
  ![LittleJS Logo](examples/favicon.png)
214
216
 
@@ -85,6 +85,11 @@ declare module "littlejsengine" {
85
85
  * @param {Array} [objects=engineObjects] - List of objects to check
86
86
  * @memberof Engine */
87
87
  export function engineObjectsCallback(pos?: Vector2, size?: number | Vector2, callbackFunction?: Function, objects?: any[]): void;
88
+ /** Add a new update function for a plugin
89
+ * @param {Function} [updateFunction]
90
+ * @param {Function} [renderFunction]
91
+ * @memberof Engine */
92
+ export function engineAddPlugin(updateFunction?: Function, renderFunction?: Function): void;
88
93
  /**
89
94
  * LittleJS Debug System
90
95
  * - Press Esc to show debug overlay with mouse pick
@@ -470,6 +475,10 @@ declare module "littlejsengine" {
470
475
  * @param {Number} scale
471
476
  * @memberof Settings */
472
477
  export function setParticleEmitRateScale(scale: number): void;
478
+ /** Set if touch input is allowed
479
+ * @param {Boolean} enable
480
+ * @memberof Settings */
481
+ export function setTouchInputEnable(enable: boolean): void;
473
482
  /** Set if gamepads are enabled
474
483
  * @param {Boolean} enable
475
484
  * @memberof Settings */
@@ -1279,17 +1288,17 @@ declare module "littlejsengine" {
1279
1288
  * @type {WebGL2RenderingContext}
1280
1289
  * @memberof WebGL */
1281
1290
  export let glContext: WebGL2RenderingContext;
1282
- /** Set the WebGl texture, called automatically if using multiple textures
1283
- * - This may also flush the gl buffer resulting in more draw calls and worse performance
1284
- * @param {WebGLTexture} texture
1285
- * @memberof WebGL */
1286
- export function glSetTexture(texture: WebGLTexture): void;
1287
1291
  /** Compile WebGL shader of the given type, will throw errors if in debug mode
1288
1292
  * @param {String} source
1289
1293
  * @param {Number} type
1290
1294
  * @return {WebGLShader}
1291
1295
  * @memberof WebGL */
1292
1296
  export function glCompileShader(source: string, type: number): WebGLShader;
1297
+ /** Draw any sprites still in the buffer, copy to main canvas and clear
1298
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
1299
+ * @param {Boolean} [forceDraw]
1300
+ * @memberof WebGL */
1301
+ export function glCopyToContext(context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, forceDraw?: boolean): void;
1293
1302
  /** Create WebGL program with given shaders
1294
1303
  * @param {String} vsSource
1295
1304
  * @param {String} fsSource
@@ -1301,11 +1310,34 @@ declare module "littlejsengine" {
1301
1310
  * @return {WebGLTexture}
1302
1311
  * @memberof WebGL */
1303
1312
  export function glCreateTexture(image: HTMLImageElement): WebGLTexture;
1313
+ /** Add a sprite to the gl draw list, used by all gl draw functions
1314
+ * @param {Number} x
1315
+ * @param {Number} y
1316
+ * @param {Number} sizeX
1317
+ * @param {Number} sizeY
1318
+ * @param {Number} angle
1319
+ * @param {Number} uv0X
1320
+ * @param {Number} uv0Y
1321
+ * @param {Number} uv1X
1322
+ * @param {Number} uv1Y
1323
+ * @param {Number} rgba
1324
+ * @param {Number} [rgbaAdditive=0]
1325
+ * @memberof WebGL */
1326
+ export function glDraw(x: number, y: number, sizeX: number, sizeY: number, angle: number, uv0X: number, uv0Y: number, uv1X: number, uv1Y: number, rgba: number, rgbaAdditive?: number): void;
1327
+ /** Draw all sprites and clear out the buffer, called automatically by the system whenever necessary
1328
+ * @memberof WebGL */
1329
+ export function glFlush(): void;
1330
+ /** Set the WebGl texture, called automatically if using multiple textures
1331
+ * - This may also flush the gl buffer resulting in more draw calls and worse performance
1332
+ * @param {WebGLTexture} texture
1333
+ * @memberof WebGL */
1334
+ export function glSetTexture(texture: WebGLTexture): void;
1304
1335
  /**
1305
1336
  * LittleJS Input System
1306
1337
  * - Tracks keyboard down, pressed, and released
1307
1338
  * - Tracks mouse buttons, position, and wheel
1308
1339
  * - Tracks multiple analog gamepads
1340
+ * - Touch input is handled as mouse input
1309
1341
  * - Virtual gamepad for touch devices
1310
1342
  * @namespace Input
1311
1343
  */
@@ -1335,6 +1367,7 @@ declare module "littlejsengine" {
1335
1367
  * - Tracks keyboard down, pressed, and released
1336
1368
  * - Tracks mouse buttons, position, and wheel
1337
1369
  * - Tracks multiple analog gamepads
1370
+ * - Touch input is handled as mouse input
1338
1371
  * - Virtual gamepad for touch devices
1339
1372
  * @namespace Input
1340
1373
  */
@@ -194,8 +194,23 @@ function debugSaveDataURL(dataURL, filename)
194
194
  downloadLink.click();
195
195
  }
196
196
 
197
+ /** Show error as full page of red text
198
+ * @memberof Debug */
199
+ function debugShowErrors()
200
+ {
201
+ onunhandledrejection = (event)=>showError(event.reason);
202
+ onerror = (event, source, lineno, colno)=>
203
+ showError(`${event}\n${source}\nLn ${lineno}, Col ${colno}`);
204
+
205
+ const showError = (message)=>
206
+ {
207
+ document.body.style.backgroundColor = '#111';
208
+ document.body.innerHTML = `<pre style=color:#f00;font-size:50px>` + message;
209
+ }
210
+ }
211
+
197
212
  ///////////////////////////////////////////////////////////////////////////////
198
- // Engine debug function (called automatically)
213
+ // Engine debug functions (called automatically)
199
214
 
200
215
  function debugInit()
201
216
  {
@@ -326,7 +341,7 @@ function debugRender()
326
341
  const pos = worldToScreen(p.pos);
327
342
  overlayContext.translate(pos.x|0, pos.y|0);
328
343
  overlayContext.rotate(p.angle);
329
- overlayContext.scale(1, -1);
344
+ overlayContext.scale(1, p.text ? 1 : -1);
330
345
  overlayContext.fillStyle = overlayContext.strokeStyle = p.color;
331
346
 
332
347
  if (p.text != undefined)
@@ -1500,6 +1515,13 @@ let gamepadDirectionEmulateStick = true;
1500
1515
  * @memberof Settings */
1501
1516
  let inputWASDEmulateDirection = true;
1502
1517
 
1518
+ /** True if touch input is enabled for mobile devices
1519
+ * - Touch events will be routed to mouse events
1520
+ * @type {Boolean}
1521
+ * @default
1522
+ * @memberof Settings */
1523
+ let touchInputEnable = true;
1524
+
1503
1525
  /** True if touch gamepad should appear on mobile devices
1504
1526
  * - Supports left analog stick, 4 face buttons and start button (button 9)
1505
1527
  * - Must be set by end of gameInit to be activated
@@ -1715,6 +1737,11 @@ function setGamepadDirectionEmulateStick(enable) { gamepadDirectionEmulateStick
1715
1737
  * @memberof Settings */
1716
1738
  function setInputWASDEmulateDirection(enable) { inputWASDEmulateDirection = enable; }
1717
1739
 
1740
+ /** Set if touch input is allowed
1741
+ * @param {Boolean} enable
1742
+ * @memberof Settings */
1743
+ function setTouchInputEnable(enable) { touchInputEnable = enable; }
1744
+
1718
1745
  /** Set if touch gamepad should appear on mobile devices
1719
1746
  * @param {Boolean} enable
1720
1747
  * @memberof Settings */
@@ -1954,9 +1981,9 @@ class EngineObject
1954
1981
 
1955
1982
  // apply physics
1956
1983
  const oldPos = this.pos.copy();
1957
- this.velocity.y += gravity * this.gravityScale;
1958
1984
  this.pos.x += this.velocity.x *= this.damping;
1959
- this.pos.y += this.velocity.y *= this.damping;
1985
+ this.pos.y += this.velocity.y = this.damping * this.velocity.y
1986
+ + gravity * this.gravityScale;
1960
1987
  this.angle += this.angleVelocity *= this.angleDamping;
1961
1988
 
1962
1989
  // physics sanity checks
@@ -2085,19 +2112,22 @@ class EngineObject
2085
2112
  const isBlockedX = tileCollisionTest(vec2(this.pos.x, oldPos.y), this.size, this);
2086
2113
  if (isBlockedY || !isBlockedX)
2087
2114
  {
2088
- // set if landed on ground
2089
- this.groundObject = wasMovingDown;
2090
-
2091
2115
  // bounce velocity
2092
2116
  this.velocity.y *= -this.elasticity;
2093
2117
 
2094
- // adjust next velocity to settle on ground
2095
- const o = (oldPos.y - this.size.y/2|0) - (oldPos.y - this.size.y/2);
2096
- if (o < 0 && o > this.damping * this.velocity.y + gravity * this.gravityScale)
2097
- this.velocity.y = this.damping ? (o - gravity * this.gravityScale) / this.damping : 0;
2098
-
2099
- // move to previous position
2100
- this.pos.y = oldPos.y;
2118
+ // set if landed on ground
2119
+ if (this.groundObject = wasMovingDown)
2120
+ {
2121
+ // adjust position to slightly above nearest tile boundary
2122
+ // this prevents gap between object and ground
2123
+ const epsilon = .0001;
2124
+ this.pos.y = (oldPos.y-this.size.y/2|0)+this.size.y/2+epsilon;
2125
+ }
2126
+ else
2127
+ {
2128
+ // move to previous position
2129
+ this.pos.y = oldPos.y;
2130
+ }
2101
2131
  }
2102
2132
  if (isBlockedX)
2103
2133
  {
@@ -2235,13 +2265,16 @@ class EngineObject
2235
2265
  /** Render debug info for this object */
2236
2266
  renderDebugInfo()
2237
2267
  {
2238
- // show object info for debugging
2239
- const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
2240
- const color1 = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, this.parent?.2:.5);
2241
- const color2 = this.parent ? rgb(1,1,1,.5) : rgb(0,0,0,.8);
2242
- drawRect(this.pos, size, color1, this.angle, false);
2243
- drawRect(this.pos, size.scale(.8), color2, this.angle, false);
2244
- this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(0,0,1,.5), false);
2268
+ if (debug)
2269
+ {
2270
+ // show object info for debugging
2271
+ const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
2272
+ const color1 = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, this.parent?.2:.5);
2273
+ const color2 = this.parent ? rgb(1,1,1,.5) : rgb(0,0,0,.8);
2274
+ drawRect(this.pos, size, color1, this.angle, false);
2275
+ drawRect(this.pos, size.scale(.8), color2, this.angle, false);
2276
+ this.parent && drawLine(this.pos, this.parent.pos, .1, rgb(0,0,1,.5), false);
2277
+ }
2245
2278
  }
2246
2279
  }
2247
2280
  /**
@@ -2748,6 +2781,7 @@ function toggleFullscreen()
2748
2781
  * - Tracks keyboard down, pressed, and released
2749
2782
  * - Tracks mouse buttons, position, and wheel
2750
2783
  * - Tracks multiple analog gamepads
2784
+ * - Touch input is handled as mouse input
2751
2785
  * - Virtual gamepad for touch devices
2752
2786
  * @namespace Input
2753
2787
  */
@@ -2881,7 +2915,8 @@ function inputUpdate()
2881
2915
  if (headlessMode) return;
2882
2916
 
2883
2917
  // clear input when lost focus (prevent stuck keys)
2884
- isTouchDevice || document.hasFocus() || clearInput();
2918
+ if(!(touchInputEnable && isTouchDevice) && !document.hasFocus())
2919
+ clearInput();
2885
2920
 
2886
2921
  // update mouse world space position
2887
2922
  mousePos = screenToWorld(mousePosScreen);
@@ -2942,6 +2977,10 @@ function inputInit()
2942
2977
  // mouse event handlers
2943
2978
  onmousedown = (e)=>
2944
2979
  {
2980
+ // fix stalled audio requiring user interaction
2981
+ if (soundEnable && !headlessMode && audioContext && audioContext.state != 'running')
2982
+ audioContext.resume();
2983
+
2945
2984
  isUsingGamepad = false;
2946
2985
  inputData[0][e.button] = 3;
2947
2986
  mousePosScreen = mouseToScreen(e);
@@ -2953,7 +2992,7 @@ function inputInit()
2953
2992
  oncontextmenu = (e)=> false; // prevent right click menu
2954
2993
 
2955
2994
  // init touch input
2956
- if (isTouchDevice)
2995
+ if (isTouchDevice && touchInputEnable)
2957
2996
  touchInputInit();
2958
2997
  }
2959
2998
 
@@ -3081,12 +3120,12 @@ function vibrateStop() { vibrate(0); }
3081
3120
 
3082
3121
  /** True if a touch device has been detected
3083
3122
  * @memberof Input */
3084
- const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
3123
+ const isTouchDevice = window.ontouchstart !== undefined;
3085
3124
 
3086
3125
  // touch gamepad internal variables
3087
3126
  let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
3088
3127
 
3089
- // try to enable touch mouse
3128
+ // enable touch input mouse passthrough
3090
3129
  function touchInputInit()
3091
3130
  {
3092
3131
  // add non passive touch event listeners
@@ -3110,8 +3149,8 @@ function touchInputInit()
3110
3149
  function handleTouchDefault(e)
3111
3150
  {
3112
3151
  // fix stalled audio requiring user interaction
3113
- if (soundEnable && audioContext && audioContext.state != 'running')
3114
- zzfx(0);
3152
+ if (soundEnable && !headlessMode && audioContext && audioContext.state != 'running')
3153
+ audioContext.resume();
3115
3154
 
3116
3155
  // check if touching and pass to mouse events
3117
3156
  const touching = e.touches.length;
@@ -3195,6 +3234,7 @@ function touchInputInit()
3195
3234
  // render the touch gamepad, called automatically by the engine
3196
3235
  function touchGamepadRender()
3197
3236
  {
3237
+ if (!touchInputEnable || !isTouchDevice || headlessMode) return;
3198
3238
  if (!touchGamepadEnable || !touchGamepadTimer.isSet())
3199
3239
  return;
3200
3240
 
@@ -3323,6 +3363,7 @@ class Sound
3323
3363
  // generate zzfx sound now for fast playback
3324
3364
  const defaultRandomness = .05;
3325
3365
  this.randomness = zzfxSound[1] || defaultRandomness;
3366
+ zzfxSound[1] = 0; // generate without randomness
3326
3367
  this.sampleChannels = [zzfxG(...zzfxSound)];
3327
3368
  this.sampleRate = zzfxR;
3328
3369
  }
@@ -3555,10 +3596,6 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
3555
3596
 
3556
3597
  ///////////////////////////////////////////////////////////////////////////////
3557
3598
 
3558
- // internal tracking if audio was suspended when last sound was played
3559
- // allows first suspended sound to play when audio is resumed
3560
- let audioSuspended = false;
3561
-
3562
3599
  /** Play cached audio samples with given settings
3563
3600
  * @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
3564
3601
  * @param {Number} [volume] - How much to scale volume by
@@ -3574,20 +3611,21 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
3574
3611
  if (!soundEnable || headlessMode) return;
3575
3612
 
3576
3613
  // prevent sounds from building up if they can't be played
3577
- const audioWasSuspended = audioSuspended;
3578
- if (audioSuspended = audioContext.state != 'running')
3614
+ if (audioContext.state != 'running')
3579
3615
  {
3580
3616
  // fix stalled audio
3581
- audioContext.resume();
3617
+ audioContext.resume().then(()=>
3618
+ playSamples(sampleChannels, volume, rate, pan, loop, sampleRate, gainNode));
3582
3619
 
3583
3620
  // prevent suspended sounds from building up
3584
- if (audioWasSuspended)
3585
- return;
3621
+ return;
3586
3622
  }
3587
3623
 
3588
3624
  // create buffer and source
3589
- const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length, sampleRate),
3590
- source = audioContext.createBufferSource();
3625
+ const channelCount = sampleChannels.length;
3626
+ const sampleLength = sampleChannels[0].length;
3627
+ const buffer = audioContext.createBuffer(channelCount, sampleLength, sampleRate);
3628
+ const source = audioContext.createBufferSource();
3591
3629
 
3592
3630
  // copy samples to buffer and setup source
3593
3631
  sampleChannels.forEach((c,i)=> buffer.getChannelData(i).set(c));
@@ -3601,7 +3639,8 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
3601
3639
  gainNode.connect(audioGainNode);
3602
3640
 
3603
3641
  // connect source to stereo panner and gain
3604
- source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)})).connect(gainNode);
3642
+ const pannerNode = new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)});
3643
+ source.connect(pannerNode).connect(gainNode);
3605
3644
 
3606
3645
  // play and return sound
3607
3646
  source.start();
@@ -3617,7 +3656,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
3617
3656
  * @param {Array} zzfxSound - Array of ZzFX parameters, ex. [.5,.5]
3618
3657
  * @return {AudioBufferSourceNode} - The audio node of the sound played
3619
3658
  * @memberof Audio */
3620
- function zzfx(...zzfxSound) { return new Sound(zzfxSound).play(); }
3659
+ function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
3621
3660
 
3622
3661
  /** Sample rate used for all ZzFX sounds
3623
3662
  * @default 44100
@@ -3626,7 +3665,7 @@ const zzfxR = 44100;
3626
3665
 
3627
3666
  /** Generate samples for a ZzFX sound
3628
3667
  * @param {Number} [volume] - Volume scale (percent)
3629
- * @param {Number} [randomness] - Unused in this fuction, handled by Sound class
3668
+ * @param {Number} [randomness] - How much to randomize frequency (percent Hz)
3630
3669
  * @param {Number} [frequency] - Frequency of sound (Hz)
3631
3670
  * @param {Number} [attack] - Attack time, how fast sound starts (seconds)
3632
3671
  * @param {Number} [sustain] - Sustain time, how long sound holds (seconds)
@@ -3652,7 +3691,7 @@ const zzfxR = 44100;
3652
3691
  function zzfxG
3653
3692
  (
3654
3693
  // parameters
3655
- volume = 1, randomness = 0, frequency = 220, attack = 0, sustain = 0,
3694
+ volume = 1, randomness = .05, frequency = 220, attack = 0, sustain = 0,
3656
3695
  release = .1, shape = 0, shapeCurve = 1, slide = 0, deltaSlide = 0,
3657
3696
  pitchJump = 0, pitchJumpTime = 0, repeatTime = 0, noise = 0, modulation = 0,
3658
3697
  bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0, filter = 0
@@ -3663,7 +3702,8 @@ function zzfxG
3663
3702
  // init parameters
3664
3703
  let PI2 = PI*2, sampleRate = zzfxR,
3665
3704
  startSlide = slide *= 500 * PI2 / sampleRate / sampleRate,
3666
- startFrequency = frequency *= PI2 / sampleRate,
3705
+ startFrequency = frequency *=
3706
+ rand(1 + randomness, 1-randomness) * PI2 / sampleRate,
3667
3707
  b = [], t = 0, tm = 0, i = 0, j = 1, r = 0, c = 0, s = 0, f, length,
3668
3708
 
3669
3709
  // biquad LP/HP filter
@@ -4599,7 +4639,8 @@ function medalsInit(saveName)
4599
4639
  medalsForEach(medal=> medal.unlocked = (localStorage[medal.storageKey()] | 0));
4600
4640
 
4601
4641
  // engine automatically renders medals
4602
- addPluginRender(function()
4642
+ engineAddPlugin(undefined, medalsRender);
4643
+ function medalsRender()
4603
4644
  {
4604
4645
  if (!medalsDisplayQueue.length)
4605
4646
  return;
@@ -4623,7 +4664,7 @@ function medalsInit(saveName)
4623
4664
  time > slideOffTime ? (time - slideOffTime) / medalDisplaySlideTime : 0;
4624
4665
  medal.render(hidePercent);
4625
4666
  }
4626
- });
4667
+ }
4627
4668
  }
4628
4669
 
4629
4670
  /** Calls a function for each medal
@@ -5062,7 +5103,7 @@ const engineName = 'LittleJS';
5062
5103
  * @type {String}
5063
5104
  * @default
5064
5105
  * @memberof Engine */
5065
- const engineVersion = '1.9.8';
5106
+ const engineVersion = '1.9.11';
5066
5107
 
5067
5108
  /** Frames per second to update
5068
5109
  * @type {Number}
@@ -5121,14 +5162,14 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
5121
5162
  const pluginUpdateList = [], pluginRenderList = [];
5122
5163
 
5123
5164
  /** Add a new update function for a plugin
5124
- * @param {Function} updateFunction
5165
+ * @param {Function} [updateFunction]
5166
+ * @param {Function} [renderFunction]
5125
5167
  * @memberof Engine */
5126
- function addPluginUpdate(updateFunction) { pluginUpdateList.push(updateFunction); }
5127
-
5128
- /** Add a new render function for a plugin
5129
- * @param {Function} renderFunction
5130
- * @memberof Engine */
5131
- function addPluginRender(renderFunction) { pluginRenderList.push(renderFunction); }
5168
+ function engineAddPlugin(updateFunction, renderFunction)
5169
+ {
5170
+ updateFunction && pluginUpdateList.push(updateFunction);
5171
+ renderFunction && pluginRenderList.push(renderFunction);
5172
+ }
5132
5173
 
5133
5174
  ///////////////////////////////////////////////////////////////////////////////
5134
5175
  // Main engine functions
@@ -5301,10 +5342,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5301
5342
  const styleBody =
5302
5343
  'margin:0;overflow:hidden;' + // fill the window
5303
5344
  'background:#000;' + // set background color
5304
- 'touch-action:none;' + // prevent mobile pinch to resize
5305
- 'user-select:none;' + // prevent mobile hold to select
5345
+ 'user-select:none;' + // prevent hold to select
5306
5346
  '-webkit-user-select:none;' + // compatibility for ios
5307
- '-webkit-touch-callout:none'; // compatibility for ios
5347
+ (!touchInputEnable ? '' : // no touch css setttings
5348
+ 'touch-action:none;' + // prevent mobile pinch to resize
5349
+ '-webkit-touch-callout:none');// compatibility for ios
5308
5350
  document.body.style.cssText = styleBody;
5309
5351
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
5310
5352
  mainContext = mainCanvas.getContext('2d');
@@ -5645,6 +5687,7 @@ export {
5645
5687
  engineObjectsUpdate,
5646
5688
  engineObjectsDestroy,
5647
5689
  engineObjectsCallback,
5690
+ engineAddPlugin,
5648
5691
 
5649
5692
  // Globals
5650
5693
  debug,
@@ -5726,6 +5769,7 @@ export {
5726
5769
  setObjectMaxSpeed,
5727
5770
  setGravity,
5728
5771
  setParticleEmitRateScale,
5772
+ setTouchInputEnable,
5729
5773
  setGamepadsEnable,
5730
5774
  setGamepadDirectionEmulateStick,
5731
5775
  setInputWASDEmulateDirection,
@@ -5811,10 +5855,13 @@ export {
5811
5855
  // WebGL
5812
5856
  glCanvas,
5813
5857
  glContext,
5814
- glSetTexture,
5815
5858
  glCompileShader,
5859
+ glCopyToContext,
5816
5860
  glCreateProgram,
5817
5861
  glCreateTexture,
5862
+ glDraw,
5863
+ glFlush,
5864
+ glSetTexture,
5818
5865
 
5819
5866
  // Input
5820
5867
  keyIsDown,
@@ -5874,4 +5921,5 @@ export {
5874
5921
  medalsPreventUnlock,
5875
5922
  medalsInit,
5876
5923
  Medal,
5877
- };
5924
+ };
5925
+