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.
- package/FAQ.md +197 -15
- package/README.md +31 -10
- package/dist/littlejs.d.ts +301 -92
- package/dist/littlejs.esm.js +1578 -518
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1554 -517
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1525 -494
- package/package.json +4 -1
- package/plugins/box2d.js +26 -19
- package/plugins/drawUtilities.js +63 -0
- package/plugins/lightSystem.js +330 -0
- package/plugins/medalSystem.js +36 -5
- package/plugins/newgrounds.js +15 -3
- package/plugins/pathFinder.js +5 -3
- package/plugins/pluginExport.js +12 -3
- package/plugins/postProcess.js +8 -1
- package/plugins/tweenSystem.js +25 -20
- package/plugins/uiSystem.js +21 -3
- package/src/engine.js +14 -3
- package/src/engineAudio.js +48 -16
- package/src/engineBuild.mjs +2 -1
- package/src/engineDebug.js +30 -23
- package/src/engineDraw.js +80 -46
- package/src/engineExport.js +16 -2
- package/src/engineInput.js +534 -190
- package/src/engineLogo.js +1 -1
- package/src/engineMath.js +21 -7
- package/src/engineObject.js +24 -13
- package/src/engineParticles.js +45 -42
- package/src/engineRelease.js +1 -0
- package/src/engineSettings.js +112 -10
- package/src/engineTileLayer.js +67 -43
- package/src/engineUtilities.js +24 -11
- package/src/engineWebGL.js +23 -55
package/plugins/pluginExport.js
CHANGED
|
@@ -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,
|
package/plugins/postProcess.js
CHANGED
|
@@ -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
|
}
|
package/plugins/tweenSystem.js
CHANGED
|
@@ -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
|
-
//
|
|
435
|
-
//
|
|
436
|
-
|
|
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 (
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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:
|
|
448
|
-
function pingPongContinuation(
|
|
450
|
+
// Continuation for pingPong: swaps start and end on the same tween each iteration.
|
|
451
|
+
function pingPongContinuation(tween)
|
|
449
452
|
{
|
|
450
|
-
if (
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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.
|
package/plugins/uiSystem.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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(
|
|
436
|
+
promises.push(imageFontInit());
|
|
426
437
|
|
|
427
438
|
if (showSplashScreen)
|
|
428
439
|
{
|
package/src/engineAudio.js
CHANGED
|
@@ -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 (
|
|
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
|
|
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
|
|
169
|
+
// Create sound instance
|
|
170
170
|
const rate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
171
|
-
|
|
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 (
|
|
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.
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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 (
|
|
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()
|
|
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
|
-
//
|
|
517
|
-
|
|
518
|
-
|
|
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
|
|
package/src/engineBuild.mjs
CHANGED
|
@@ -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} --
|
|
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!'); }
|
package/src/engineDebug.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
///////////////////////////////////////////////////////////////////////////////
|