littlejsengine 1.16.1 → 1.16.2

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.
@@ -478,11 +478,11 @@ export function explosion(pos, radius=3, strength=300)
478
478
 
479
479
  // fire
480
480
  new LJS.ParticleEmitter(
481
- pos, 0, // pos, angle
482
- radius, .1, 100*radius, 3.14, // emitSize, emitTime, rate, cone
483
- Game.spriteAtlas.dot, // tileInfo
484
- hsl(0,1,.5), hsl(.15,1,.5), // colorStartA, colorStartB
485
- hsl(0,1,.5,0), hsl(.1, 1,.5,0), // colorEndA, colorEndB
481
+ pos, 0, // pos, angle
482
+ radius, .1, 100*radius, 3.14, // emitSize, emitTime, rate, cone
483
+ Game.spriteAtlas.dot, // tileInfo
484
+ hsl(0,.8,.5), hsl(.15,.8,.5), // colorStartA, colorStartB
485
+ hsl(0,1,.5,0), hsl(.1, 1,.5,0),// colorEndA, colorEndB
486
486
  .7, 1, .5, .1, .1, // time, sizeStart, sizeEnd, speed, angleSpeed
487
487
  .9, 1, 0, 3.14, .05, // damp, angleDamp, gravity, particleCone, fade
488
488
  1, 0, 1, 0, 1e9 // randomness, collide, additive, colorLinear, renderOrder
@@ -7,7 +7,7 @@
7
7
  </head><body>
8
8
 
9
9
  <!-- LittleJS Engine -->
10
- <script src=../../dist/littlejs.js?1.16.1></script>
10
+ <script src=../../dist/littlejs.js?1.16.2></script>
11
11
 
12
12
  <!-- Add your game scripts here -->
13
- <script src=game.js?1.16.1></script>
13
+ <script src=game.js?1.16.2></script>
package/examples/logo.png CHANGED
Binary file
Binary file
@@ -128,7 +128,7 @@ export function explosion(pos, radius=3)
128
128
  pos, 0, // pos, angle
129
129
  radius/2, .1, 100*radius, 3.14, // emitSize, emitTime, rate, cone
130
130
  0, // tileInfo
131
- rgb(1,.5,.1), rgb(1,.1,.1), // colorStartA, colorStartB
131
+ rgb(1,.5,.1), rgb(1,.1,.1), // colorStartA, colorStartB
132
132
  rgb(1,.5,.1,0), rgb(1,.1,.1,0), // colorEndA, colorEndB
133
133
  .7, .8, .2, .2, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
134
134
  .9, 1, -.2, 3.14, .05, // damp, angleDamp, gravity, particleCone, fade
@@ -1,10 +1,11 @@
1
1
  <!DOCTYPE html><meta charset=utf-8><body>
2
- <script src=../../dist/littlejs.js?1.16.1></script>
2
+ <script src=../../dist/littlejs.js?1.16.2></script>
3
3
  <script src=../../dist/box2d.wasm.js></script>
4
4
 
5
5
  <!-- LittleJS Engine Source
6
6
  <script src=../../src/engine.js></script>
7
7
  <script src=../../src/engineDebug.js></script>
8
+ <script src=../../src/engineMath.js></script>
8
9
  <script src=../../src/engineUtilities.js></script>
9
10
  <script src=../../src/engineSettings.js></script>
10
11
  <script src=../../src/engineObject.js></script>
@@ -7,11 +7,12 @@
7
7
  </head><body>
8
8
 
9
9
  <!-- LittleJS Engine -->
10
- <script src=../../dist/littlejs.js?1.16.1></script>
10
+ <script src=../../dist/littlejs.js?1.16.2></script>
11
11
 
12
12
  <!-- LittleJS Engine Source
13
13
  <script src=../../src/engine.js></script>
14
14
  <script src=../../src/engineDebug.js></script>
15
+ <script src=../../src/engineMath.js></script>
15
16
  <script src=../../src/engineUtilities.js></script>
16
17
  <script src=../../src/engineSettings.js></script>
17
18
  <script src=../../src/engineObject.js></script>
@@ -31,4 +32,4 @@
31
32
  -->
32
33
 
33
34
  <!-- Add your game scripts here -->
34
- <script src=game.js?1.16.1></script>
35
+ <script src=game.js?1.16.2></script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.16.1",
3
+ "version": "1.16.2",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "dist/littlejs.esm.js",
6
6
  "types": "dist/littlejs.d.ts",
@@ -762,7 +762,7 @@ class UIObject
762
762
  this.onUpdate();
763
763
 
764
764
  // unset active if disabled
765
- if (this.disabled && this == uiSystem.activeObject)
765
+ if (this.disabled && this === uiSystem.activeObject)
766
766
  uiSystem.activeObject = undefined;
767
767
 
768
768
  const wasHover = uiSystem.lastHoverObject === this;
package/reference.md CHANGED
@@ -428,11 +428,11 @@ debugCircle(pos, size, color='#fff', time=0, fill) // Draw debug circle
428
428
  debugPoint(pos, color, time, angle) // Draw debug point
429
429
  debugLine(posA, posB, color, width=.1, time) // Draw debug line
430
430
  debugText(text, pos, size=1, color='#fff', time=0, angle=0) // Draw debug text
431
- debugAABB(pA, sA, pB, sB, color) // Draw a debug axis aligned box
432
- debugClear() // Clear all debug primitives
433
- debugSaveCanvas(canvas, filename) // Save canvas to a file
434
- debugSaveText(text, filename) // Save text to a file
435
- debugSaveDataURL(dataURL, filename) // Save url to a file
431
+ debugAABB(pA, sA, pB, sB, color) // Draw a debug axis aligned box
432
+ debugClear() // Clear all debug primitives
433
+ saveCanvas(canvas, filename) // Save canvas to a file
434
+ saveText(text, filename) // Save text to a file
435
+ saveDataURL(dataURL, filename) // Save url to a file
436
436
 
437
437
  // Debug settings
438
438
  debug // Is debug enabled?
package/src/engine.js CHANGED
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
30
30
  * @type {string}
31
31
  * @default
32
32
  * @memberof Engine */
33
- const engineVersion = '1.16.1';
33
+ const engineVersion = '1.16.2';
34
34
 
35
35
  /** Frames per second to update
36
36
  * @type {number}
@@ -269,33 +269,20 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
269
269
  if (!wasUpdated)
270
270
  updateCanvas();
271
271
 
272
- // render sort then render while removing destroyed objects
272
+ // render the game and objects
273
273
  enginePreRender();
274
274
  gameRender();
275
275
  engineObjects.sort((a,b)=> a.renderOrder - b.renderOrder);
276
276
  for (const o of engineObjects)
277
277
  o.destroyed || o.render();
278
+
279
+ // post rendering
278
280
  gameRenderPost();
279
281
  pluginList.forEach(plugin=>plugin.render?.());
280
282
  inputRender();
281
283
  debugRender();
282
284
  glFlush();
283
- debugVideoCaptureUpdate();
284
-
285
- if (debugWatermark && !debugVideoCaptureIsActive())
286
- {
287
- // update fps display
288
- mainContext.textAlign = 'right';
289
- mainContext.textBaseline = 'top';
290
- mainContext.font = '1em monospace';
291
- mainContext.fillStyle = '#000';
292
- const text = engineName + ' ' + 'v' + engineVersion + ' / '
293
- + drawCount + ' / ' + engineObjects.length + ' / ' + averageFPS.toFixed(1)
294
- + (glEnable ? ' GL' : ' 2D') ;
295
- mainContext.fillText(text, mainCanvas.width-3, 3);
296
- mainContext.fillStyle = '#fff';
297
- mainContext.fillText(text, mainCanvas.width-2, 2);
298
- }
285
+ debugRenderPost();
299
286
  drawCount = 0;
300
287
  }
301
288
  }
@@ -361,15 +348,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
361
348
  mainContext.lineJoin = 'round';
362
349
  mainContext.lineCap = 'round';
363
350
  }
364
-
365
- // wait for gameInit to load
366
- async function startEngine()
367
- {
368
- await gameInit();
369
- engineUpdate();
370
- }
371
- if (headlessMode)
372
- return startEngine();
351
+
352
+ // skip setup if headless
353
+ if (headlessMode) return startEngine();
373
354
 
374
355
  // setup webgl
375
356
  glInit(rootElement);
@@ -384,7 +365,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
384
365
  'touch-action:none;' + // prevent mobile pinch to resize
385
366
  '-webkit-touch-callout:none'; // compatibility for ios
386
367
  rootElement.style.cssText = styleRoot;
387
- drawCanvas = mainCanvas = rootElement.appendChild(document.createElement('canvas'));
368
+ mainCanvas = rootElement.appendChild(document.createElement('canvas'));
388
369
  drawContext = mainContext = mainCanvas.getContext('2d');
389
370
 
390
371
  // init stuff and start engine
@@ -459,6 +440,13 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
459
440
  await Promise.all(promises);
460
441
  return startEngine();
461
442
 
443
+ async function startEngine()
444
+ {
445
+ // wait for gameInit to load
446
+ await gameInit();
447
+ engineUpdate();
448
+ }
449
+
462
450
  ///////////////////////////////////////////////////////////////////////////
463
451
  // LittleJS Splash Screen
464
452
  function drawEngineSplashScreen(t)
@@ -383,8 +383,7 @@ class SoundInstance
383
383
  /** Pause this sound instance */
384
384
  pause()
385
385
  {
386
- if (this.isPaused())
387
- return;
386
+ if (this.isPaused()) return;
388
387
 
389
388
  // save current time and stop sound
390
389
  this.pausedTime = this.getCurrentTime();
@@ -396,8 +395,7 @@ class SoundInstance
396
395
  /** Unpauses this sound instance */
397
396
  resume()
398
397
  {
399
- if (!this.isPaused())
400
- return;
398
+ if (!this.isPaused()) return;
401
399
 
402
400
  // restart sound from paused time
403
401
  this.start(this.pausedTime);
@@ -16,6 +16,7 @@ const SOURCE_FOLDER = 'src';
16
16
  const PLUGIN_FOLDER = 'plugins';
17
17
  const engineSourceFiles =
18
18
  [
19
+ `${SOURCE_FOLDER}/engineMath.js`,
19
20
  `${SOURCE_FOLDER}/engineUtilities.js`,
20
21
  `${SOURCE_FOLDER}/engineSettings.js`,
21
22
  `${SOURCE_FOLDER}/engineObject.js`,
@@ -41,7 +41,7 @@ let debugKey = 'Escape';
41
41
  let debugOverlay = false;
42
42
 
43
43
  // Engine internal variables not exposed to documentation
44
- let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugMedals = false, debugTakeScreenshot, downloadLink, debugCanvas;
44
+ let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugMedals = false, debugTakeScreenshot;
45
45
 
46
46
  ///////////////////////////////////////////////////////////////////////////////
47
47
  // Debug helper functions
@@ -228,46 +228,6 @@ function debugClear() { debugPrimitives = []; }
228
228
  * @memberof Debug */
229
229
  function debugScreenshot() { debugTakeScreenshot = 1; }
230
230
 
231
- /** Save a canvas to disk
232
- * @param {HTMLCanvasElement|OffscreenCanvas} canvas
233
- * @param {string} [filename]
234
- * @param {string} [type]
235
- * @memberof Debug */
236
- function debugSaveCanvas(canvas, filename='screenshot', type='image/png')
237
- {
238
- if (canvas instanceof OffscreenCanvas)
239
- {
240
- // copy to temporary canvas and save
241
- if (!debugCanvas)
242
- debugCanvas = document.createElement('canvas');
243
- debugCanvas.width = canvas.width;
244
- debugCanvas.height = canvas.height;
245
- debugCanvas.getContext('2d').drawImage(canvas, 0, 0);
246
- debugSaveDataURL(debugCanvas.toDataURL(type), filename);
247
- }
248
- else
249
- debugSaveDataURL(canvas.toDataURL(type), filename);
250
- }
251
-
252
- /** Save a text file to disk
253
- * @param {string} text
254
- * @param {string} [filename]
255
- * @param {string} [type]
256
- * @memberof Debug */
257
- function debugSaveText(text, filename='text', type='text/plain')
258
- { debugSaveDataURL(URL.createObjectURL(new Blob([text], {'type':type})), filename); }
259
-
260
- /** Save a data url to disk
261
- * @param {string} dataURL
262
- * @param {string} filename
263
- * @memberof Debug */
264
- function debugSaveDataURL(dataURL, filename)
265
- {
266
- downloadLink.download = filename;
267
- downloadLink.href = dataURL;
268
- downloadLink.click();
269
- }
270
-
271
231
  /** Breaks on all asserts/errors, hides the canvas, and shows message in plain text
272
232
  * This is a good function to call at the start of your game to catch all errors
273
233
  * In release builds this function has no effect
@@ -303,21 +263,11 @@ function debugShowErrors()
303
263
 
304
264
  function debugInit()
305
265
  {
306
- // create link for saving screenshots
307
- downloadLink = document.createElement('a');
308
266
  }
309
267
 
310
268
  function debugUpdate()
311
269
  {
312
- if (!debug)
313
- return;
314
-
315
- if (debugVideoCaptureIsActive())
316
- {
317
- // control to stop video capture
318
- if (keyWasPressed('Digit6') || keyWasPressed(debugKey))
319
- debugVideoCaptureStop();
320
- }
270
+ if (!debug) return;
321
271
 
322
272
  if (keyWasPressed(debugKey)) // Esc
323
273
  debugOverlay = !debugOverlay;
@@ -335,9 +285,15 @@ function debugUpdate()
335
285
  debugRaycast = !debugRaycast;
336
286
  if (keyWasPressed('Digit5'))
337
287
  debugScreenshot();
338
- if (keyWasPressed('Digit6') && !debugVideoCaptureIsActive())
339
- debugVideoCaptureStart();
340
288
  }
289
+ if (debugVideoCaptureIsActive())
290
+ {
291
+ // control to stop video capture
292
+ if (!debugOverlay || keyWasPressed('Digit6'))
293
+ debugVideoCaptureStop();
294
+ }
295
+ else if (debugOverlay && keyWasPressed('Digit6'))
296
+ debugVideoCaptureStart();
341
297
  }
342
298
 
343
299
  function debugRender()
@@ -352,7 +308,7 @@ function debugRender()
352
308
  {
353
309
  // combine canvases, remove alpha and save
354
310
  combineCanvases();
355
- debugSaveCanvas(mainCanvas);
311
+ saveCanvas(mainCanvas);
356
312
  debugTakeScreenshot = 0;
357
313
  }
358
314
 
@@ -598,11 +554,34 @@ function debugRender()
598
554
  }
599
555
  }
600
556
 
557
+ function debugRenderPost()
558
+ {
559
+ if (debugVideoCaptureIsActive())
560
+ {
561
+ debugVideoCaptureUpdate();
562
+ return;
563
+ }
564
+
565
+ if (!debugWatermark) return;
566
+
567
+ // update fps display
568
+ mainContext.textAlign = 'right';
569
+ mainContext.textBaseline = 'top';
570
+ mainContext.font = '1em monospace';
571
+ mainContext.fillStyle = '#000';
572
+ const text = engineName + ' v' + engineVersion + ' / '
573
+ + drawCount + ' / ' + engineObjects.length + ' / ' + averageFPS.toFixed(1)
574
+ + (glEnable ? ' GL' : ' 2D') ;
575
+ mainContext.fillText(text, mainCanvas.width-3, 3);
576
+ mainContext.fillStyle = '#fff';
577
+ mainContext.fillText(text, mainCanvas.width-2, 2);
578
+ }
579
+
601
580
  ///////////////////////////////////////////////////////////////////////////////
602
581
  // video capture - records video and audio at 60 fps using MediaRecorder API
603
582
 
604
583
  // internal variables used to capture video
605
- let debugVideoCapture, debugVideoCaptureTrack, debugVideoCaptureIcon, debugVideoCaptureTimer;
584
+ let debugVideoCapture, debugVideoCaptureIcon;
606
585
 
607
586
  /** Check if video capture is active
608
587
  * @memberof Debug */
@@ -612,80 +591,103 @@ function debugVideoCaptureIsActive() { return !!debugVideoCapture; }
612
591
  * @memberof Debug */
613
592
  function debugVideoCaptureStart()
614
593
  {
615
- if (debugVideoCaptureIsActive())
616
- return; // already recording
594
+ ASSERT(!debugVideoCaptureIsActive(), 'Already capturing video!');
617
595
 
618
- // captureStream passing in 0 to only capture when requestFrame() is called
596
+ if (!debugVideoCaptureIcon)
597
+ {
598
+ // create recording icon to show it is capturing video
599
+ debugVideoCaptureIcon = document.createElement('div');
600
+ debugVideoCaptureIcon.style.position = 'absolute';
601
+ debugVideoCaptureIcon.style.padding = '9px';
602
+ debugVideoCaptureIcon.style.color = '#f00';
603
+ debugVideoCaptureIcon.style.font = '50px monospace';
604
+ document.body.appendChild(debugVideoCaptureIcon);
605
+ }
606
+ // show recording icon
607
+ debugVideoCaptureIcon.textContent = '';
608
+ debugVideoCaptureIcon.style.display = '';
609
+
610
+ // setup captureStream to capture manually by passing 0
619
611
  const stream = mainCanvas.captureStream(0);
612
+ const videoTrack = stream.getVideoTracks()[0];
613
+ const captureTimer = new Timer(0, true);
620
614
  const chunks = [];
621
- debugVideoCaptureTrack = stream.getVideoTracks()[0];
622
- if (debugVideoCaptureTrack.applyConstraints)
623
- debugVideoCaptureTrack.applyConstraints({frameRate:60}); // force 60 fps
624
- debugVideoCapture = new MediaRecorder(stream, {mimeType:'video/webm;codecs=vp8'});
625
- debugVideoCapture.ondataavailable = (e)=> chunks.push(e.data);
626
- debugVideoCapture.onstop = ()=>
615
+ videoTrack.applyConstraints({frameRate:frameRate});
616
+
617
+ // set up the media recorder
618
+ const mediaRecorder = new MediaRecorder(stream,
619
+ {mimeType:'video/webm;codecs=vp8'});
620
+ mediaRecorder.ondataavailable = (e)=> chunks.push(e.data);
621
+ mediaRecorder.onstop = ()=>
627
622
  {
628
623
  const blob = new Blob(chunks, {type: 'video/webm'});
629
624
  const url = URL.createObjectURL(blob);
630
- downloadLink.download = 'capture.webm';
631
- downloadLink.href = url;
632
- downloadLink.click();
633
- URL.revokeObjectURL(url);
625
+ saveDataURL(url, 'capture.webm', 1e3);
634
626
  };
635
627
 
636
- if (audioMasterGain)
628
+ let audioStreamDestination, silentAudioSource;
629
+ if (soundEnable)
637
630
  {
631
+ // create silent audio source
632
+ // fixes issue where video can not start recording without audio
633
+ silentAudioSource = new ConstantSourceNode(audioContext, { offset: 0 });
634
+ silentAudioSource.connect(audioMasterGain);
635
+ silentAudioSource.start();
636
+
638
637
  // connect to audio master gain node
639
- const audioStreamDestination = audioContext.createMediaStreamDestination();
638
+ audioStreamDestination = audioContext.createMediaStreamDestination();
640
639
  audioMasterGain.connect(audioStreamDestination);
641
640
  for (const track of audioStreamDestination.stream.getAudioTracks())
642
641
  stream.addTrack(track); // add audio tracks to capture stream
643
642
  }
644
643
 
645
644
  // start recording
645
+ try { mediaRecorder.start(); }
646
+ catch(e)
647
+ {
648
+ LOG('Video capture not supported in this browser!');
649
+ silentAudioSource?.stop();
650
+ return;
651
+ }
652
+
646
653
  LOG('Video capture started.');
647
- debugVideoCapture.start();
648
- debugVideoCaptureTimer = new Timer(0, true);
649
654
 
650
- if (!debugVideoCaptureIcon)
655
+ // save debug video info
656
+ debugVideoCapture =
651
657
  {
652
- // create recording icon to show it is capturing video
653
- debugVideoCaptureIcon = document.createElement('div');
654
- debugVideoCaptureIcon.style.position = 'absolute';
655
- debugVideoCaptureIcon.style.padding = '9px';
656
- debugVideoCaptureIcon.style.color = '#f00';
657
- debugVideoCaptureIcon.style.font = '50px monospace';
658
- document.body.appendChild(debugVideoCaptureIcon);
659
- }
660
- // show recording icon
661
- debugVideoCaptureIcon.textContent = '';
662
- debugVideoCaptureIcon.style.display = '';
658
+ mediaRecorder,
659
+ captureTimer,
660
+ videoTrack,
661
+ silentAudioSource,
662
+ audioStreamDestination
663
+ };
663
664
  }
664
665
 
665
666
  /** Stop capturing video and save to disk
666
667
  * @memberof Debug */
667
668
  function debugVideoCaptureStop()
668
669
  {
669
- if (!debugVideoCaptureIsActive())
670
- return; // not recording
670
+ ASSERT(debugVideoCaptureIsActive(), 'Not capturing video!');
671
671
 
672
672
  // stop recording
673
- LOG(`Video capture ended. ${debugVideoCaptureTimer.get().toFixed(2)} seconds recorded.`);
674
- debugVideoCapture.stop();
675
- debugVideoCapture = 0;
673
+ LOG(`Video capture ended. ${debugVideoCapture.captureTimer.get().toFixed(2)} seconds recorded.`);
676
674
  debugVideoCaptureIcon.style.display = 'none';
675
+ debugVideoCapture.silentAudioSource?.stop();
676
+ debugVideoCapture.mediaRecorder?.stop();
677
+ debugVideoCapture.videoTrack?.stop();
678
+ debugVideoCapture = undefined;
677
679
  }
678
680
 
679
681
  // update video capture, called automatically by engine
680
682
  function debugVideoCaptureUpdate()
681
683
  {
682
- if (!debugVideoCaptureIsActive())
683
- return; // not recording
684
+ ASSERT(debugVideoCaptureIsActive(), 'Not capturing video!');
684
685
 
685
686
  // save the video frame
686
687
  combineCanvases();
687
- debugVideoCaptureTrack.requestFrame();
688
- debugVideoCaptureIcon.textContent = '● REC ' + formatTime(debugVideoCaptureTimer);
688
+ debugVideoCapture.videoTrack.requestFrame();
689
+ debugVideoCaptureIcon.textContent = '● REC '
690
+ + formatTime(debugVideoCapture.captureTimer);
689
691
  }
690
692
 
691
693
  ///////////////////////////////////////////////////////////////////////////////
package/src/engineDraw.js CHANGED
@@ -31,11 +31,6 @@ let mainCanvas;
31
31
  * @memberof Draw */
32
32
  let mainContext;
33
33
 
34
- /** The default canvas to use for drawing, usually mainCanvas
35
- * @type {HTMLCanvasElement|OffscreenCanvas}
36
- * @memberof Draw */
37
- let drawCanvas;
38
-
39
34
  /** The default 2d context to use for drawing, usually mainContext
40
35
  * @type {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D}
41
36
  * @memberof Draw */
@@ -623,7 +618,11 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
623
618
  ASSERT(typeof drawFunction === 'function', 'drawFunction must be a function');
624
619
 
625
620
  if (!screenSpace)
626
- [pos, size, angle] = worldToScreenTransform(pos, size, angle);
621
+ {
622
+ pos = worldToScreen(pos);
623
+ size = size.scale(cameraScale);
624
+ angle -= cameraAngle;
625
+ }
627
626
  context.save();
628
627
  context.translate(pos.x+.5, pos.y+.5);
629
628
  context.rotate(angle);
@@ -675,9 +674,9 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
675
674
  * @param {string} [fontStyle]
676
675
  * @param {number} [maxWidth]
677
676
  * @param {number} [angle]
678
- * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=mainContext]
677
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
679
678
  * @memberof Draw */
680
- function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=mainContext)
679
+ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=drawContext)
681
680
  {
682
681
  ASSERT(isString(text), 'text must be a string');
683
682
  ASSERT(isVector2(pos), 'pos must be a vec2');
@@ -818,25 +817,6 @@ function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
818
817
  ];
819
818
  }
820
819
 
821
- /** Convert world space transform to screen space
822
- * @param {Vector2} worldPos
823
- * @param {Vector2} worldSize
824
- * @param {number} [worldAngle]
825
- * @return {[Vector2, Vector2, number]} - [pos, size, angle]
826
- * @memberof Draw */
827
- function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
828
- {
829
- ASSERT(isVector2(worldPos), 'worldPos must be a vec2');
830
- ASSERT(isVector2(worldSize), 'worldSize must be a vec2');
831
- ASSERT(isNumber(worldAngle), 'worldAngle must be a number');
832
-
833
- return [
834
- worldToScreen(worldPos),
835
- worldSize.scale(cameraScale),
836
- worldAngle - cameraAngle
837
- ];
838
- }
839
-
840
820
  /** Get the size of the camera window in world space
841
821
  * @return {Vector2}
842
822
  * @memberof Draw */
@@ -1059,7 +1039,7 @@ class FontImage
1059
1039
  * @param {boolean} [center]
1060
1040
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
1061
1041
  */
1062
- drawTextScreen(text, pos, scale=4, center=true, context=mainContext)
1042
+ drawTextScreen(text, pos, scale=4, center=true, context=drawContext)
1063
1043
  {
1064
1044
  context.save();
1065
1045
  const size = this.tileSize;
@@ -41,13 +41,10 @@ export
41
41
  debugText,
42
42
  debugClear,
43
43
  debugScreenshot,
44
- debugSaveCanvas,
45
- debugSaveText,
46
- debugSaveDataURL,
47
44
  debugShowErrors,
48
- debugVideoCaptureIsActive,
49
45
  debugVideoCaptureStart,
50
46
  debugVideoCaptureStop,
47
+ debugVideoCaptureIsActive,
51
48
 
52
49
  // Settings
53
50
  cameraPos,
@@ -144,7 +141,7 @@ export
144
141
  setDebugWatermark,
145
142
  setDebugKey,
146
143
 
147
- // Utilities
144
+ // Math
148
145
  PI,
149
146
  abs,
150
147
  floor,
@@ -172,8 +169,13 @@ export
172
169
  isOverlapping,
173
170
  isIntersecting,
174
171
  wave,
172
+
173
+ // Utilities
175
174
  formatTime,
176
175
  fetchJSON,
176
+ saveText,
177
+ saveCanvas,
178
+ saveDataURL,
177
179
 
178
180
  // Random
179
181
  rand,
@@ -219,7 +221,6 @@ export
219
221
  TextureInfo,
220
222
  mainCanvas,
221
223
  mainContext,
222
- drawCanvas,
223
224
  drawContext,
224
225
  workCanvas,
225
226
  workContext,
@@ -232,6 +233,7 @@ export
232
233
  worldToScreen,
233
234
  screenToWorldDelta,
234
235
  worldToScreenDelta,
236
+ screenToWorldTransform,
235
237
  drawTile,
236
238
  drawRect,
237
239
  drawRectGradient,
@@ -365,8 +365,7 @@ function inputInit()
365
365
  }
366
366
  function onMouseDown(e)
367
367
  {
368
- if (isTouchDevice && touchInputEnable)
369
- return;
368
+ if (isTouchDevice && touchInputEnable) return;
370
369
 
371
370
  // fix stalled audio requiring user interaction
372
371
  if (soundEnable && !headlessMode && audioContext && !audioIsRunning())
@@ -417,8 +416,7 @@ function inputInit()
417
416
  let wasTouching;
418
417
  function handleTouch(e)
419
418
  {
420
- if (!touchInputEnable)
421
- return;
419
+ if (!touchInputEnable) return;
422
420
 
423
421
  // route touch to gamepad
424
422
  if (touchGamepadEnable)
@@ -482,8 +480,7 @@ function inputInit()
482
480
  }
483
481
 
484
482
  // don't process touch gamepad if paused
485
- if (paused)
486
- return;
483
+ if (paused) return;
487
484
 
488
485
  // get center of left and right sides
489
486
  const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);