littlejsengine 1.18.12 → 1.18.17

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.
@@ -7,16 +7,17 @@ export
7
7
  // Medals
8
8
  medals,
9
9
  medalsPreventUnlock,
10
- medalsInit,
11
- medalsForEach,
12
- Medal,
13
10
  medalDisplayTime,
14
11
  medalDisplaySlideTime,
15
12
  medalDisplaySize,
13
+ medalsInit,
14
+ medalsForEach,
15
+ medalsReset,
16
16
  setMedalDisplayTime,
17
17
  setMedalDisplaySlideTime,
18
18
  setMedalDisplaySize,
19
19
  setMedalsPreventUnlock,
20
+ Medal,
20
21
 
21
22
  // Newgrounds
22
23
  newgrounds,
@@ -27,8 +28,14 @@ export
27
28
  postProcess,
28
29
  PostProcessPlugin,
29
30
 
31
+ // Light System
32
+ lightSystem,
33
+ LightSystemPlugin,
34
+ Light,
35
+
30
36
  // ZzFXMusic
31
37
  ZzFXMusic,
38
+ zzfxM,
32
39
 
33
40
  // UI System
34
41
  uiSystem,
@@ -75,6 +82,8 @@ export
75
82
  drawNineSliceScreen,
76
83
  drawThreeSlice,
77
84
  drawThreeSliceScreen,
85
+ drawCrescent,
86
+ getCrescentPoints,
78
87
 
79
88
  // Tween System
80
89
  Tween,
@@ -107,10 +107,14 @@ class PostProcessPlugin
107
107
  function postProcessRender()
108
108
  {
109
109
  if (headlessMode || !glEnable) return;
110
-
110
+
111
111
  // clear out the buffer
112
112
  glFlush();
113
113
 
114
+ // ensure we render to the default framebuffer (in case any earlier
115
+ // caller this frame left a render target bound)
116
+ glContext.bindFramebuffer(glContext.FRAMEBUFFER, null);
117
+
114
118
  // setup shader program to draw a quad
115
119
  glContext.useProgram(postProcess.shader);
116
120
  glContext.bindVertexArray(postProcess.vao);
@@ -151,6 +155,9 @@ class PostProcessPlugin
151
155
  glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, glCanvas);
152
156
  }
153
157
 
158
+ // restore default so subsequent dynamic texture uploads aren't flipped
159
+ glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, false);
160
+
154
161
  // force it to set instanced mode
155
162
  glSetInstancedMode(true);
156
163
  }
@@ -258,7 +258,7 @@ const Ease =
258
258
  * @param {number} x
259
259
  * @returns {number}
260
260
  * @memberof TweenSystem */
261
- EXPO: (x) => 2 ** (10 * x - 10),
261
+ EXPO: (x) => x === 0 ? 0 : 2 ** (10 * x - 10),
262
262
 
263
263
  /** Back ease-in: overshoots backward at the start before snapping forward.
264
264
  * @param {number} x
@@ -271,6 +271,8 @@ const Ease =
271
271
  * @returns {number}
272
272
  * @memberof TweenSystem */
273
273
  ELASTIC: (x) =>
274
+ x === 0 ? 0 :
275
+ x === 1 ? 1 :
274
276
  -(2 ** (10 * x - 10)) * sin(((37 - 40 * x) * PI) / 6),
275
277
 
276
278
  /** Spring-like ease-out: oscillates outward after passing the target.
@@ -431,29 +433,32 @@ function tweenProperty(target, propertyPath, start, end, duration = 1, options =
431
433
  }
432
434
 
433
435
  // Continuation that schedules the next loop iteration when one finishes.
434
- // Called from the completed tween's `then` slot. Decrements the counter and
435
- // only spawns a new tween if more iterations remain.
436
- function loopContinuation(prev)
436
+ // Reuses the same Tween object across iterations so the user's handle
437
+ // from `.loop()` keeps working — calling `.stop()` mid-loop now cancels
438
+ // the entire chain instead of just the current iteration.
439
+ function loopContinuation(tween)
437
440
  {
438
- if (prev.loopRemaining !== Infinity && prev.loopRemaining <= 1) return;
439
- const next = new Tween(prev.callback, prev.start, prev.end, prev.duration,
440
- { ease: prev.ease, useRealTime: prev.useRealTime });
441
- next.loopRemaining = prev.loopRemaining === Infinity
442
- ? Infinity
443
- : prev.loopRemaining - 1;
444
- next.thenCallback = () => loopContinuation(next);
441
+ if (tween.loopRemaining !== Infinity && tween.loopRemaining <= 1) return;
442
+ if (tween.loopRemaining !== Infinity) tween.loopRemaining -= 1;
443
+ tween.life = tween.duration;
444
+ tween.thenCallback = () => loopContinuation(tween);
445
+ tweenActive.push(tween);
446
+ // snap to start for the new iteration (matches Tween constructor behavior)
447
+ tween.callback(tween.interp(tween.duration));
445
448
  }
446
449
 
447
- // Continuation for pingPong: spawns a new tween with start and end swapped.
448
- function pingPongContinuation(prev)
450
+ // Continuation for pingPong: swaps start and end on the same tween each iteration.
451
+ function pingPongContinuation(tween)
449
452
  {
450
- if (prev.loopRemaining !== Infinity && prev.loopRemaining <= 1) return;
451
- const next = new Tween(prev.callback, prev.end, prev.start, prev.duration,
452
- { ease: prev.ease, useRealTime: prev.useRealTime });
453
- next.loopRemaining = prev.loopRemaining === Infinity
454
- ? Infinity
455
- : prev.loopRemaining - 1;
456
- next.thenCallback = () => pingPongContinuation(next);
453
+ if (tween.loopRemaining !== Infinity && tween.loopRemaining <= 1) return;
454
+ if (tween.loopRemaining !== Infinity) tween.loopRemaining -= 1;
455
+ const tmp = tween.start;
456
+ tween.start = tween.end;
457
+ tween.end = tmp;
458
+ tween.life = tween.duration;
459
+ tween.thenCallback = () => pingPongContinuation(tween);
460
+ tweenActive.push(tween);
461
+ tween.callback(tween.interp(tween.duration));
457
462
  }
458
463
 
459
464
  /** Engine plugin hook: advance every active tween by the appropriate delta.
@@ -456,11 +456,17 @@ class UISystemPlugin
456
456
  * @param {DragAndDropCallback} [onDragOver] - continuously when dragging over */
457
457
  setupDragAndDrop(onDrop, onDragEnter, onDragLeave, onDragOver)
458
458
  {
459
- function setCallback(callback, listenerType)
459
+ // remove any prior listeners so repeated setup calls don't stack
460
+ if (this._dragListeners)
461
+ for (const [type, listener] of this._dragListeners)
462
+ document.removeEventListener(type, listener);
463
+ this._dragListeners = [];
464
+ const setCallback = (callback, listenerType)=>
460
465
  {
461
- function listener(e) { e.preventDefault(); callback && callback(e); }
466
+ const listener = (e)=> { e.preventDefault(); callback && callback(e); };
462
467
  document.addEventListener(listenerType, listener);
463
- }
468
+ this._dragListeners.push([listenerType, listener]);
469
+ };
464
470
  setCallback(onDrop, 'drop');
465
471
  setCallback(onDragEnter, 'dragenter');
466
472
  setCallback(onDragLeave, 'dragleave');
@@ -784,6 +790,15 @@ class UIObject
784
790
  if (this.destroyed)
785
791
  return;
786
792
 
793
+ // clear ui-system references that point at this object so events
794
+ // don't keep firing against a destroyed target (especially the
795
+ // keydown listener attached for keyInputObject)
796
+ if (uiSystem.activeObject === this) uiSystem.activeObject = undefined;
797
+ if (uiSystem.hoverObject === this) uiSystem.hoverObject = undefined;
798
+ if (uiSystem.lastHoverObject === this) uiSystem.lastHoverObject = undefined;
799
+ if (uiSystem.navigationObject === this) uiSystem.navigationObject = undefined;
800
+ if (uiSystem.keyInputObject === this) uiSystem.keyInputObject = undefined;
801
+
787
802
  // disconnect from parent and destroy children
788
803
  this.destroyed = 1;
789
804
  this.parent?.removeChild(this);
@@ -792,6 +807,8 @@ class UIObject
792
807
  child.parent = undefined;
793
808
  child.destroy();
794
809
  }
810
+ // clear references so destroyed children can be GC'd
811
+ this.children.length = 0;
795
812
  }
796
813
 
797
814
  /** Check if the mouse is overlapping this ui object
@@ -1381,6 +1398,7 @@ class UISlider extends UIObject
1381
1398
  {
1382
1399
  // toggle value between 0 and 1
1383
1400
  this.value = this.value ? 0 : 1;
1401
+ this.onChange();
1384
1402
  this.onRelease();
1385
1403
  super.navigatePressed();
1386
1404
  }
package/src/engine.js CHANGED
@@ -32,7 +32,7 @@ const engineName = 'LittleJS';
32
32
  * @type {string}
33
33
  * @default
34
34
  * @memberof Engine */
35
- const engineVersion = '1.18.12';
35
+ const engineVersion = '1.18.17';
36
36
 
37
37
  /** Frames per second to update
38
38
  * @type {number}
@@ -161,12 +161,20 @@ function engineAddPlugin(update, render, glContextLost, glContextRestored)
161
161
  * ['tiles.png', 'tilesLevel.png'] // images to load
162
162
  * );
163
163
  * @memberof Engine */
164
- async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
164
+ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement)
165
165
  {
166
166
  showEngineVersion && console.log(`${engineName} Engine v${engineVersion}`);
167
167
  ASSERT(!mainContext, 'engine already initialized');
168
+ // runtime guard so release builds (where the assert is stripped) don't
169
+ // double-register listeners / double-add canvases on a second call
170
+ if (mainContext) return;
168
171
  ASSERT(isArray(imageSources), 'pass in images as array');
169
172
 
173
+ // ensure body exists for minimal HTML where the script runs before <body> is parsed
174
+ if (!document.body)
175
+ document.documentElement.appendChild(document.createElement('body'));
176
+ rootElement ||= document.body;
177
+
170
178
  // allow passing in empty functions
171
179
  gameInit ||= ()=>{};
172
180
  gameUpdate ||= ()=>{};
@@ -192,6 +200,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
192
200
  {
193
201
  // update time keeping
194
202
  let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
203
+ // skip delta on the very first frame so timeReal doesn't jump
204
+ // by ~page-load-time when RAF starts handing real timestamps
205
+ if (!frameTimeLastMS) frameTimeDeltaMS = 0;
195
206
  frameTimeLastMS = frameTimeMS;
196
207
  if (debug || debugWatermark)
197
208
  averageFPS = lerp(averageFPS, 1e3/(frameTimeDeltaMS||1), .05);
@@ -422,7 +433,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
422
433
  promises.push(loadTexture(0));
423
434
 
424
435
  // load engine font image
425
- promises.push(fontImageInit());
436
+ promises.push(imageFontInit());
426
437
 
427
438
  if (showSplashScreen)
428
439
  {
@@ -103,7 +103,7 @@ class Sound
103
103
  /** @property {SoundLoadCallback} - function to call when sound is loaded */
104
104
  this.onloadCallback = onloadCallback;
105
105
 
106
- if (Array.isArray(asset))
106
+ if (isArray(asset))
107
107
  {
108
108
  // generate zzfx sound — copy so we don't mutate the caller's array
109
109
  const zzfxSound = asset.slice();
@@ -135,7 +135,7 @@ class Sound
135
135
  * @param {number} [randomnessScale] - How much to scale pitch randomness
136
136
  * @param {boolean} [loop] - Should the sound loop?
137
137
  * @param {boolean} [paused] - Should the sound start paused
138
- * @return {SoundInstance} - The audio source node
138
+ * @return {SoundInstance} - The sound instance, or undefined if sound is disabled, not loaded, or running in headless mode
139
139
  */
140
140
  play(pos, volume=1, pitch=1, randomnessScale=1, loop=false, paused=false)
141
141
  {
@@ -166,9 +166,23 @@ class Sound
166
166
  pan = worldToScreen(pos).x * 2/mainCanvas.width - 1;
167
167
  }
168
168
 
169
- // Create and return sound instance
169
+ // Create sound instance
170
170
  const rate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
171
- return new SoundInstance(this, volume, rate, pan, loop, paused);
171
+ const instance = new SoundInstance(this, volume, rate, pan, loop, paused);
172
+
173
+ if (debug && debugSound && pos)
174
+ {
175
+ // visualize where positioned sounds play and their falloff range
176
+ debugCircle(pos, .5, '#0ff', .5, true);
177
+ if (this.range)
178
+ {
179
+ debugCircle(pos, 2*this.range, '#0ff', .5); // silent radius
180
+ debugCircle(pos, 2*this.range*this.taper, '#0ff', .5); // full volume radius
181
+ }
182
+ debugText('vol '+volume.toFixed(2)+' pitch '+rate.toFixed(2), pos, .5, '#0ff', .5);
183
+ }
184
+
185
+ return instance;
172
186
  }
173
187
 
174
188
  /** Play a music track that loops by default
@@ -195,7 +209,7 @@ class Sound
195
209
  }
196
210
 
197
211
  /** Get how long this sound is in seconds
198
- * @return {number} - How long the sound is in seconds (undefined if loading)
212
+ * @return {number} - How long the sound is in seconds (0 if loading)
199
213
  */
200
214
  getDuration()
201
215
  { return this.sampleChannels?.[0]?.length / this.sampleRate || 0; }
@@ -352,10 +366,14 @@ class SoundInstance
352
366
  {
353
367
  if (fadeTime)
354
368
  {
355
- // ramp off gain
369
+ // ramp off gain from current volume (not 1, or low-volume
370
+ // instances would jump back up before fading);
371
+ // cancel any prior scheduling so stacked stop calls don't
372
+ // re-anchor partway through a previous fade
356
373
  const startFade = audioContext.currentTime;
357
374
  const endFade = startFade + fadeTime;
358
- this.gainNode.gain.linearRampToValueAtTime(1, startFade);
375
+ this.gainNode.gain.cancelScheduledValues(startFade);
376
+ this.gainNode.gain.setValueAtTime(this.volume, startFade);
359
377
  this.gainNode.gain.linearRampToValueAtTime(0, endFade);
360
378
  this.source.stop(endFade);
361
379
  }
@@ -403,13 +421,14 @@ class SoundInstance
403
421
  */
404
422
  getCurrentTime()
405
423
  {
406
- const deltaTime = mod(audioContext.currentTime - this.startTime,
407
- this.getDuration());
408
- return this.isPlaying() ? deltaTime : this.pausedTime;
424
+ if (!this.isPlaying()) return this.pausedTime;
425
+ const duration = this.getDuration();
426
+ // guard mod against 0 duration (rate=0 or sound not loaded)
427
+ return duration ? mod(audioContext.currentTime - this.startTime, duration) : 0;
409
428
  }
410
429
 
411
430
  /** Get the total duration of this sound
412
- * @return {number} - Total duration in seconds
431
+ * @return {number} - Total duration in seconds (0 if loading)
413
432
  */
414
433
  getDuration() { return this.rate ? this.sound.getDuration() / this.rate : 0; }
415
434
 
@@ -433,7 +452,7 @@ function speak(text, volume=1, rate=1, pitch=1, language='')
433
452
  {
434
453
  ASSERT(typeof volume !== 'string', 'speak() signature changed: language is now the last parameter, after pitch');
435
454
  if (!soundEnable || headlessMode) return;
436
- if (!speechSynthesis) return;
455
+ if (typeof speechSynthesis === 'undefined') return;
437
456
 
438
457
  // common languages (not supported by all browsers)
439
458
  // en - english, it - italian, fr - french, de - german, es - spanish
@@ -451,7 +470,11 @@ function speak(text, volume=1, rate=1, pitch=1, language='')
451
470
 
452
471
  /** Stop all queued speech
453
472
  * @memberof Audio */
454
- function speakStop() {speechSynthesis?.cancel();}
473
+ function speakStop()
474
+ {
475
+ if (typeof speechSynthesis !== 'undefined')
476
+ speechSynthesis.cancel();
477
+ }
455
478
 
456
479
  /** Get frequency of a note on a musical scale
457
480
  * @param {number} semitoneOffset - How many semitones away from the root note
@@ -513,13 +536,22 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
513
536
  const pannerNode = new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)});
514
537
  source.connect(pannerNode).connect(gainNode);
515
538
 
516
- // callback when the sound ends
517
- if (onended)
518
- source.addEventListener('ended', ()=> onended(source));
539
+ // disconnect nodes when the sound ends so the audio graph doesn't grow
540
+ // unbounded across many play() calls (source.stop() also fires 'ended')
541
+ source.addEventListener('ended', ()=>
542
+ {
543
+ gainNode.disconnect();
544
+ pannerNode.disconnect();
545
+ if (onended) onended(source);
546
+ });
519
547
 
520
548
  // play and return sound
521
549
  const startOffset = offset * rate;
522
550
  source.start(0, startOffset);
551
+
552
+ if (debug && debugSound)
553
+ LOG('sound', 'vol', volume.toFixed(2), 'rate', rate.toFixed(2), 'pan', pan.toFixed(2), loop ? 'loop' : '');
554
+
523
555
  return source;
524
556
  }
525
557
 
@@ -41,6 +41,7 @@ const enginePluginFiles =
41
41
  `${PLUGIN_FOLDER}/medalSystem.js`,
42
42
  `${PLUGIN_FOLDER}/newgrounds.js`,
43
43
  `${PLUGIN_FOLDER}/postProcess.js`,
44
+ `${PLUGIN_FOLDER}/lightSystem.js`,
44
45
  `${PLUGIN_FOLDER}/zzfxm.js`,
45
46
  `${PLUGIN_FOLDER}/uiSystem.js`,
46
47
  `${PLUGIN_FOLDER}/box2d.js`,
@@ -196,7 +197,7 @@ function closureCompilerStep(filename)
196
197
  fs.copyFileSync(filename, filenameTemp);
197
198
  try
198
199
  {
199
- execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --warning_level=VERBOSE --jscomp_off=*`);
200
+ execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --jscomp_off=*`);
200
201
  fs.rmSync(filenameTemp);
201
202
  }
202
203
  catch (e) { handleError(e, 'Failed to run Closure Compiler step!'); }
@@ -44,7 +44,7 @@ let debugKey = 'Escape';
44
44
  let debugOverlay = false;
45
45
 
46
46
  // Engine internal variables not exposed to documentation
47
- let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugTakeScreenshot;
47
+ let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugSound = false, debugTakeScreenshot;
48
48
 
49
49
  ///////////////////////////////////////////////////////////////////////////////
50
50
  // Debug helper functions
@@ -83,12 +83,8 @@ function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false, s
83
83
  ASSERT(isNumber(time), 'time must be a number');
84
84
  ASSERT(isNumber(angle), 'angle must be a number');
85
85
 
86
- if (typeof size === 'number')
87
- size = vec2(size); // allow passing in floats
88
86
  if (isColor(color))
89
87
  color = color.toString();
90
- pos = pos.copy();
91
- size = size.copy();
92
88
  const timer = new Timer(time);
93
89
  debugPrimitives.push({pos:pos.copy(), size:size.copy(), color, timer, angle, fill, screenSpace});
94
90
  }
@@ -158,7 +154,7 @@ function debugPoint(pos, color, time, angle, screenSpace=false)
158
154
  * @param {number} [time]
159
155
  * @param {boolean} [screenSpace]
160
156
  * @memberof Debug */
161
- function debugLine(posA, posB, color, width=.1, time, screenSpace=false)
157
+ function debugLine(posA, posB, color, width=.1, time=0, screenSpace=false)
162
158
  {
163
159
  ASSERT(isVector2(posA), 'posA must be a vec2');
164
160
  ASSERT(isVector2(posB), 'posB must be a vec2');
@@ -196,7 +192,7 @@ function debugOverlap(posA, sizeA, posB, sizeB, color, time, screenSpace=false)
196
192
  debugRect(minPos.lerp(maxPos,.5), maxPos.subtract(minPos), color, time, 0, false, screenSpace);
197
193
  }
198
194
 
199
- /** Draw a debug axis aligned bounding box in world space
195
+ /** Draw debug text in world space
200
196
  * @param {string|number} text
201
197
  * @param {Vector2} pos
202
198
  * @param {number} [size]
@@ -288,6 +284,8 @@ function debugUpdate()
288
284
  debugRaycast = !debugRaycast;
289
285
  if (keyWasPressed('Digit5'))
290
286
  debugScreenshot();
287
+ if (keyWasPressed('Digit7'))
288
+ debugSound = !debugSound;
291
289
  }
292
290
  if (debugVideoCaptureIsActive())
293
291
  {
@@ -307,6 +305,9 @@ function debugRender()
307
305
  // flush any gl sprites before drawing debug info
308
306
  glFlush();
309
307
 
308
+ const savedDrawCount = drawCount;
309
+ const savedPrimitiveCount = primitiveCount;
310
+
310
311
  if (debugTakeScreenshot)
311
312
  {
312
313
  // combine canvases, remove alpha and save
@@ -340,6 +341,8 @@ function debugRender()
340
341
  const stickCount = gamepadStickData[i].length;
341
342
  for (let j = 0; j < stickCount; j++)
342
343
  {
344
+ if (!(j in gamepadStickData[i]))
345
+ continue; // skip sticks that are not present (eg a disabled touch left stick)
343
346
  const stick = gamepadStick(j, i);
344
347
  const drawPos = cornerPos.add(vec2(j*stickScale*2, 0));
345
348
  const stickPos = drawPos.add(stick.scale(stickScale));
@@ -518,6 +521,8 @@ function debugRender()
518
521
  debugContext.fillStyle = '#fff';
519
522
  debugContext.fillText('5: Save Screenshot', x, y += h);
520
523
  debugContext.fillText('6: Toggle Video Capture', x, y += h);
524
+ debugContext.fillStyle = debugSound ? '#f00' : '#fff';
525
+ debugContext.fillText('7: Debug Sound', x, y += h);
521
526
 
522
527
  let keysPressed = '';
523
528
  let mousePressed = '';
@@ -527,7 +532,7 @@ function debugRender()
527
532
  continue;
528
533
  if (parseInt(i) < 3)
529
534
  mousePressed += i + ' ' ;
530
- else if (keyIsDown(i, 0))
535
+ else
531
536
  keysPressed += i + ' ' ;
532
537
  }
533
538
  mousePressed && debugContext.fillText('Mouse: ' + mousePressed, x, y += h);
@@ -552,10 +557,27 @@ function debugRender()
552
557
  debugContext.fillText(debugParticles ? 'Debug Particles' : '', x, y += h);
553
558
  debugContext.fillText(debugRaycast ? 'Debug Raycasts' : '', x, y += h);
554
559
  debugContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
560
+ debugContext.fillText(debugSound ? 'Debug Sound' : '', x, y += h);
555
561
  }
556
562
 
557
563
  debugContext.restore();
558
564
  }
565
+
566
+ if (debugWatermark || debugOverlay)
567
+ {
568
+ // show fps stats display
569
+ mainContext.textAlign = 'right';
570
+ mainContext.textBaseline = 'top';
571
+ mainContext.font = '1em monospace';
572
+ mainContext.fillStyle = '#000';
573
+ const text = engineName + ' v' + engineVersion + ' / '
574
+ + savedDrawCount + ' / ' + savedPrimitiveCount + ' / '
575
+ + engineObjects.length + ' / ' + averageFPS.toFixed(1)
576
+ + (glEnable ? ' GL' : ' 2D') ;
577
+ mainContext.fillText(text, mainCanvas.width-3, 3);
578
+ mainContext.fillStyle = '#fff';
579
+ mainContext.fillText(text, mainCanvas.width-2, 2);
580
+ }
559
581
  }
560
582
 
561
583
  function debugRenderPost()
@@ -565,21 +587,6 @@ function debugRenderPost()
565
587
  debugVideoCaptureUpdate();
566
588
  return;
567
589
  }
568
-
569
- if (!debugWatermark && !debugOverlay) return;
570
-
571
- // update fps display
572
- mainContext.textAlign = 'right';
573
- mainContext.textBaseline = 'top';
574
- mainContext.font = '1em monospace';
575
- mainContext.fillStyle = '#000';
576
- const text = engineName + ' v' + engineVersion + ' / '
577
- + drawCount + ' / ' + primitiveCount + ' / '
578
- + engineObjects.length + ' / ' + averageFPS.toFixed(1)
579
- + (glEnable ? ' GL' : ' 2D') ;
580
- mainContext.fillText(text, mainCanvas.width-3, 3);
581
- mainContext.fillStyle = '#fff';
582
- mainContext.fillText(text, mainCanvas.width-2, 2);
583
590
  }
584
591
 
585
592
  ///////////////////////////////////////////////////////////////////////////////