littlejsengine 1.15.9 → 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.
- package/dist/littlejs.d.ts +154 -158
- package/dist/littlejs.esm.js +573 -570
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +553 -553
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +392 -396
- package/examples/box2d/game.js +1 -1
- package/examples/box2d/gameObjects.js +5 -5
- package/examples/breakout/game.js +3 -3
- package/examples/electron/game.js +1 -2
- package/examples/electron/index.html +2 -2
- package/examples/htmlMenu/game.js +0 -1
- package/examples/index.html +1 -1
- package/examples/logo.png +0 -0
- package/examples/logo2.png +0 -0
- package/examples/module/game.js +1 -2
- package/examples/particles/index.html +1 -1
- package/examples/platformer/game.js +4 -4
- package/examples/platformer/gameEffects.js +1 -1
- package/examples/puzzle/game.js +2 -2
- package/examples/shorts/base.html +4 -3
- package/examples/shorts/box2dPool.js +2 -2
- package/examples/shorts/empty.js +1 -1
- package/examples/shorts/{systemFont.js → fontImage.js} +3 -3
- package/examples/shorts/fps.js +1 -1
- package/examples/shorts/helloWorld.js +2 -2
- package/examples/shorts/nineSlice.js +2 -2
- package/examples/shorts/slidingPuzzle.js +1 -1
- package/examples/starter/game.js +1 -2
- package/examples/starter/index.html +3 -2
- package/examples/stress/index.html +1 -1
- package/examples/typescript/game.js +1 -2
- package/examples/typescript/game.ts +1 -2
- package/package.json +1 -1
- package/plugins/box2d.js +8 -8
- package/plugins/drawUtilities.js +6 -6
- package/plugins/postProcess.js +13 -20
- package/plugins/uiSystem.js +16 -4
- package/reference.md +9 -11
- package/src/engine.js +40 -54
- package/src/engineAudio.js +5 -7
- package/src/engineBuild.js +1 -0
- package/src/engineDebug.js +163 -161
- package/src/engineDraw.js +108 -130
- package/src/engineExport.js +20 -17
- package/src/engineInput.js +4 -7
- package/src/engineMath.js +1201 -0
- package/src/engineMedals.js +7 -7
- package/src/engineObject.js +5 -4
- package/src/engineRelease.js +2 -4
- package/src/engineSettings.js +22 -32
- package/src/engineTileLayer.js +9 -10
- package/src/engineUtilities.js +34 -1187
- package/src/engineWebGL.js +13 -15
package/dist/littlejs.esm.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.
|
|
36
|
+
const engineVersion = '1.16.2';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -180,8 +180,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
180
180
|
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
181
181
|
|
|
182
182
|
// disable smoothing for pixel art
|
|
183
|
-
|
|
184
|
-
mainContext.imageSmoothingEnabled = !tilesPixelated;
|
|
183
|
+
mainContext.imageSmoothingEnabled = !tilesPixelated;
|
|
185
184
|
|
|
186
185
|
// setup gl rendering if enabled
|
|
187
186
|
glPreRender();
|
|
@@ -193,7 +192,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
193
192
|
// update time keeping
|
|
194
193
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
195
194
|
frameTimeLastMS = frameTimeMS;
|
|
196
|
-
if (debug ||
|
|
195
|
+
if (debug || debugWatermark)
|
|
197
196
|
averageFPS = lerp(averageFPS, 1e3/(frameTimeDeltaMS||1), .05);
|
|
198
197
|
const debugSpeedUp = debug && keyIsDown('Equal'); // +
|
|
199
198
|
const debugSpeedDown = debug && keyIsDown('Minus'); // -
|
|
@@ -221,6 +220,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
221
220
|
debugUpdate();
|
|
222
221
|
gameUpdatePost();
|
|
223
222
|
inputUpdatePost();
|
|
223
|
+
if (debugVideoCaptureIsActive())
|
|
224
|
+
renderFrame();
|
|
224
225
|
}
|
|
225
226
|
else
|
|
226
227
|
{
|
|
@@ -271,33 +272,20 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
271
272
|
if (!wasUpdated)
|
|
272
273
|
updateCanvas();
|
|
273
274
|
|
|
274
|
-
// render
|
|
275
|
+
// render the game and objects
|
|
275
276
|
enginePreRender();
|
|
276
277
|
gameRender();
|
|
277
278
|
engineObjects.sort((a,b)=> a.renderOrder - b.renderOrder);
|
|
278
279
|
for (const o of engineObjects)
|
|
279
280
|
o.destroyed || o.render();
|
|
281
|
+
|
|
282
|
+
// post rendering
|
|
280
283
|
gameRenderPost();
|
|
281
284
|
pluginList.forEach(plugin=>plugin.render?.());
|
|
282
285
|
inputRender();
|
|
283
286
|
debugRender();
|
|
284
287
|
glFlush();
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (showWatermark && !debugVideoCaptureIsActive())
|
|
288
|
-
{
|
|
289
|
-
// update fps display
|
|
290
|
-
overlayContext.textAlign = 'right';
|
|
291
|
-
overlayContext.textBaseline = 'top';
|
|
292
|
-
overlayContext.font = '1em monospace';
|
|
293
|
-
overlayContext.fillStyle = '#000';
|
|
294
|
-
const text = engineName + ' ' + 'v' + engineVersion + ' / '
|
|
295
|
-
+ drawCount + ' / ' + engineObjects.length + ' / ' + averageFPS.toFixed(1)
|
|
296
|
-
+ (glEnable ? ' GL' : ' 2D') ;
|
|
297
|
-
overlayContext.fillText(text, mainCanvas.width-3, 3);
|
|
298
|
-
overlayContext.fillStyle = '#fff';
|
|
299
|
-
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
300
|
-
}
|
|
288
|
+
debugRenderPost();
|
|
301
289
|
drawCount = 0;
|
|
302
290
|
}
|
|
303
291
|
}
|
|
@@ -316,8 +304,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
316
304
|
const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
|
|
317
305
|
const w = innerAspect < fixedAspect ? '100%' : '';
|
|
318
306
|
const h = innerAspect < fixedAspect ? '' : '100%';
|
|
319
|
-
|
|
320
|
-
|
|
307
|
+
mainCanvas.style.width = w;
|
|
308
|
+
mainCanvas.style.height = h;
|
|
321
309
|
if (glCanvas)
|
|
322
310
|
{
|
|
323
311
|
glCanvas.style.width = w;
|
|
@@ -332,6 +320,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
332
320
|
|
|
333
321
|
// responsive aspect ratio with native resolution
|
|
334
322
|
const innerAspect = innerWidth / innerHeight;
|
|
323
|
+
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
335
324
|
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
336
325
|
{
|
|
337
326
|
// full height
|
|
@@ -346,12 +335,12 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
346
335
|
}
|
|
347
336
|
}
|
|
348
337
|
|
|
349
|
-
// clear main
|
|
350
|
-
|
|
351
|
-
|
|
338
|
+
// clear main canvas and set size
|
|
339
|
+
mainCanvas.width = mainCanvasSize.x;
|
|
340
|
+
mainCanvas.height = mainCanvasSize.y;
|
|
352
341
|
|
|
353
342
|
// apply the clear color to main canvas
|
|
354
|
-
if (canvasClearColor.a > 0)
|
|
343
|
+
if (canvasClearColor.a > 0 && !glEnable)
|
|
355
344
|
{
|
|
356
345
|
mainContext.fillStyle = canvasClearColor.toString();
|
|
357
346
|
mainContext.fillRect(0, 0, mainCanvasSize.x, mainCanvasSize.y);
|
|
@@ -359,19 +348,15 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
359
348
|
}
|
|
360
349
|
|
|
361
350
|
// set default line join and cap
|
|
362
|
-
|
|
363
|
-
mainContext.
|
|
364
|
-
mainContext.lineCap = overlayContext.lineCap = lineCap;
|
|
351
|
+
mainContext.lineJoin = 'round';
|
|
352
|
+
mainContext.lineCap = 'round';
|
|
365
353
|
}
|
|
354
|
+
|
|
355
|
+
// skip setup if headless
|
|
356
|
+
if (headlessMode) return startEngine();
|
|
366
357
|
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
{
|
|
370
|
-
await gameInit();
|
|
371
|
-
engineUpdate();
|
|
372
|
-
}
|
|
373
|
-
if (headlessMode)
|
|
374
|
-
return startEngine();
|
|
358
|
+
// setup webgl
|
|
359
|
+
glInit(rootElement);
|
|
375
360
|
|
|
376
361
|
// setup html
|
|
377
362
|
const styleRoot =
|
|
@@ -383,36 +368,30 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
383
368
|
'touch-action:none;' + // prevent mobile pinch to resize
|
|
384
369
|
'-webkit-touch-callout:none'; // compatibility for ios
|
|
385
370
|
rootElement.style.cssText = styleRoot;
|
|
386
|
-
|
|
387
|
-
rootElement.appendChild(mainCanvas);
|
|
371
|
+
mainCanvas = rootElement.appendChild(document.createElement('canvas'));
|
|
388
372
|
drawContext = mainContext = mainCanvas.getContext('2d');
|
|
389
373
|
|
|
390
374
|
// init stuff and start engine
|
|
391
375
|
inputInit();
|
|
392
376
|
audioInit();
|
|
393
377
|
debugInit();
|
|
394
|
-
glInit();
|
|
395
|
-
|
|
396
|
-
// create overlay canvas for hud to appear above gl canvas
|
|
397
|
-
overlayCanvas = document.createElement('canvas')
|
|
398
|
-
rootElement.appendChild(overlayCanvas);
|
|
399
|
-
overlayContext = overlayCanvas.getContext('2d');
|
|
400
378
|
|
|
401
379
|
// setup canvases
|
|
402
380
|
// transform way is still more reliable then flexbox or grid
|
|
403
381
|
const styleCanvas = 'position:absolute;'+ // allow canvases to overlap
|
|
404
382
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center on screen
|
|
405
|
-
mainCanvas.style.cssText =
|
|
383
|
+
mainCanvas.style.cssText = styleCanvas;
|
|
406
384
|
if (glCanvas)
|
|
407
385
|
glCanvas.style.cssText = styleCanvas;
|
|
408
386
|
setCanvasPixelated(canvasPixelated);
|
|
409
|
-
setOverlayCanvasPixelated(overlayCanvasPixelated);
|
|
410
387
|
updateCanvas();
|
|
411
388
|
glPreRender();
|
|
412
389
|
|
|
413
|
-
// create offscreen
|
|
414
|
-
workCanvas = new OffscreenCanvas(
|
|
415
|
-
workContext = workCanvas.getContext('2d'
|
|
390
|
+
// create offscreen canvases for image processing
|
|
391
|
+
workCanvas = new OffscreenCanvas(64, 64);
|
|
392
|
+
workContext = workCanvas.getContext('2d');
|
|
393
|
+
workReadCanvas = new OffscreenCanvas(64, 64);
|
|
394
|
+
workReadContext = workReadCanvas.getContext('2d', { willReadFrequently: true });
|
|
416
395
|
|
|
417
396
|
// create promises for loading images
|
|
418
397
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -464,13 +443,20 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
464
443
|
await Promise.all(promises);
|
|
465
444
|
return startEngine();
|
|
466
445
|
|
|
446
|
+
async function startEngine()
|
|
447
|
+
{
|
|
448
|
+
// wait for gameInit to load
|
|
449
|
+
await gameInit();
|
|
450
|
+
engineUpdate();
|
|
451
|
+
}
|
|
452
|
+
|
|
467
453
|
///////////////////////////////////////////////////////////////////////////
|
|
468
454
|
// LittleJS Splash Screen
|
|
469
455
|
function drawEngineSplashScreen(t)
|
|
470
456
|
{
|
|
471
|
-
const x =
|
|
472
|
-
const w =
|
|
473
|
-
const h =
|
|
457
|
+
const x = mainContext;
|
|
458
|
+
const w = mainCanvas.width = innerWidth;
|
|
459
|
+
const h = mainCanvas.height = innerHeight;
|
|
474
460
|
|
|
475
461
|
{
|
|
476
462
|
// background
|
|
@@ -768,7 +754,7 @@ const debugPointSize = .5;
|
|
|
768
754
|
* @type {boolean}
|
|
769
755
|
* @default
|
|
770
756
|
* @memberof Debug */
|
|
771
|
-
let
|
|
757
|
+
let debugWatermark = true;
|
|
772
758
|
|
|
773
759
|
/** Key code used to toggle debug mode, Esc by default
|
|
774
760
|
* @type {string}
|
|
@@ -783,7 +769,7 @@ let debugKey = 'Escape';
|
|
|
783
769
|
let debugOverlay = false;
|
|
784
770
|
|
|
785
771
|
// Engine internal variables not exposed to documentation
|
|
786
|
-
let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugMedals = false, debugTakeScreenshot
|
|
772
|
+
let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugMedals = false, debugTakeScreenshot;
|
|
787
773
|
|
|
788
774
|
///////////////////////////////////////////////////////////////////////////////
|
|
789
775
|
// Debug helper functions
|
|
@@ -970,46 +956,6 @@ function debugClear() { debugPrimitives = []; }
|
|
|
970
956
|
* @memberof Debug */
|
|
971
957
|
function debugScreenshot() { debugTakeScreenshot = 1; }
|
|
972
958
|
|
|
973
|
-
/** Save a canvas to disk
|
|
974
|
-
* @param {HTMLCanvasElement|OffscreenCanvas} canvas
|
|
975
|
-
* @param {string} [filename]
|
|
976
|
-
* @param {string} [type]
|
|
977
|
-
* @memberof Debug */
|
|
978
|
-
function debugSaveCanvas(canvas, filename='screenshot', type='image/png')
|
|
979
|
-
{
|
|
980
|
-
if (canvas instanceof OffscreenCanvas)
|
|
981
|
-
{
|
|
982
|
-
// copy to temporary canvas and save
|
|
983
|
-
if (!debugCanvas)
|
|
984
|
-
debugCanvas = document.createElement('canvas');
|
|
985
|
-
debugCanvas.width = canvas.width;
|
|
986
|
-
debugCanvas.height = canvas.height;
|
|
987
|
-
debugCanvas.getContext('2d').drawImage(canvas, 0, 0);
|
|
988
|
-
debugSaveDataURL(debugCanvas.toDataURL(type), filename);
|
|
989
|
-
}
|
|
990
|
-
else
|
|
991
|
-
debugSaveDataURL(canvas.toDataURL(type), filename);
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
/** Save a text file to disk
|
|
995
|
-
* @param {string} text
|
|
996
|
-
* @param {string} [filename]
|
|
997
|
-
* @param {string} [type]
|
|
998
|
-
* @memberof Debug */
|
|
999
|
-
function debugSaveText(text, filename='text', type='text/plain')
|
|
1000
|
-
{ debugSaveDataURL(URL.createObjectURL(new Blob([text], {'type':type})), filename); }
|
|
1001
|
-
|
|
1002
|
-
/** Save a data url to disk
|
|
1003
|
-
* @param {string} dataURL
|
|
1004
|
-
* @param {string} filename
|
|
1005
|
-
* @memberof Debug */
|
|
1006
|
-
function debugSaveDataURL(dataURL, filename)
|
|
1007
|
-
{
|
|
1008
|
-
downloadLink.download = filename;
|
|
1009
|
-
downloadLink.href = dataURL;
|
|
1010
|
-
downloadLink.click();
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
959
|
/** Breaks on all asserts/errors, hides the canvas, and shows message in plain text
|
|
1014
960
|
* This is a good function to call at the start of your game to catch all errors
|
|
1015
961
|
* In release builds this function has no effect
|
|
@@ -1045,21 +991,18 @@ function debugShowErrors()
|
|
|
1045
991
|
|
|
1046
992
|
function debugInit()
|
|
1047
993
|
{
|
|
1048
|
-
// create link for saving screenshots
|
|
1049
|
-
downloadLink = document.createElement('a');
|
|
1050
994
|
}
|
|
1051
995
|
|
|
1052
996
|
function debugUpdate()
|
|
1053
997
|
{
|
|
1054
|
-
if (!debug)
|
|
1055
|
-
return;
|
|
998
|
+
if (!debug) return;
|
|
1056
999
|
|
|
1057
1000
|
if (keyWasPressed(debugKey)) // Esc
|
|
1058
1001
|
debugOverlay = !debugOverlay;
|
|
1059
1002
|
if (debugOverlay)
|
|
1060
1003
|
{
|
|
1061
1004
|
if (keyWasPressed('Digit0'))
|
|
1062
|
-
|
|
1005
|
+
debugWatermark = !debugWatermark;
|
|
1063
1006
|
if (keyWasPressed('Digit1'))
|
|
1064
1007
|
debugPhysics = !debugPhysics, debugParticles = false;
|
|
1065
1008
|
if (keyWasPressed('Digit2'))
|
|
@@ -1070,9 +1013,15 @@ function debugUpdate()
|
|
|
1070
1013
|
debugRaycast = !debugRaycast;
|
|
1071
1014
|
if (keyWasPressed('Digit5'))
|
|
1072
1015
|
debugScreenshot();
|
|
1073
|
-
if (keyWasPressed('Digit6'))
|
|
1074
|
-
debugVideoCaptureIsActive() ? debugVideoCaptureStop() : debugVideoCaptureStart();
|
|
1075
1016
|
}
|
|
1017
|
+
if (debugVideoCaptureIsActive())
|
|
1018
|
+
{
|
|
1019
|
+
// control to stop video capture
|
|
1020
|
+
if (!debugOverlay || keyWasPressed('Digit6'))
|
|
1021
|
+
debugVideoCaptureStop();
|
|
1022
|
+
}
|
|
1023
|
+
else if (debugOverlay && keyWasPressed('Digit6'))
|
|
1024
|
+
debugVideoCaptureStart();
|
|
1076
1025
|
}
|
|
1077
1026
|
|
|
1078
1027
|
function debugRender()
|
|
@@ -1087,13 +1036,11 @@ function debugRender()
|
|
|
1087
1036
|
{
|
|
1088
1037
|
// combine canvases, remove alpha and save
|
|
1089
1038
|
combineCanvases();
|
|
1090
|
-
|
|
1091
|
-
overlayContext.fillRect(0,0,w,h);
|
|
1092
|
-
overlayContext.drawImage(mainCanvas, 0, 0);
|
|
1093
|
-
debugSaveCanvas(overlayCanvas);
|
|
1039
|
+
saveCanvas(mainCanvas);
|
|
1094
1040
|
debugTakeScreenshot = 0;
|
|
1095
1041
|
}
|
|
1096
1042
|
|
|
1043
|
+
const debugContext = mainContext;
|
|
1097
1044
|
if (debugGamepads && gamepadsEnable)
|
|
1098
1045
|
{
|
|
1099
1046
|
// draw gamepads
|
|
@@ -1141,9 +1088,6 @@ function debugRender()
|
|
|
1141
1088
|
let debugObject;
|
|
1142
1089
|
if (debugOverlay)
|
|
1143
1090
|
{
|
|
1144
|
-
const saveContext = mainContext;
|
|
1145
|
-
mainContext = overlayContext;
|
|
1146
|
-
|
|
1147
1091
|
// draw red rectangle around screen
|
|
1148
1092
|
const cameraSize = getCameraSize();
|
|
1149
1093
|
debugRect(cameraPos, cameraSize.subtract(vec2(.1)), '#f008');
|
|
@@ -1175,15 +1119,14 @@ function debugRender()
|
|
|
1175
1119
|
// show floored tile pick for tile collision
|
|
1176
1120
|
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), rgb(1,1,0,.5));
|
|
1177
1121
|
}
|
|
1178
|
-
mainContext = saveContext;
|
|
1179
1122
|
}
|
|
1180
1123
|
|
|
1181
1124
|
{
|
|
1182
1125
|
// draw debug primitives
|
|
1183
|
-
|
|
1126
|
+
debugContext.lineWidth = 2;
|
|
1184
1127
|
debugPrimitives.forEach(p=>
|
|
1185
1128
|
{
|
|
1186
|
-
|
|
1129
|
+
debugContext.save();
|
|
1187
1130
|
|
|
1188
1131
|
// create canvas transform from world space to screen space
|
|
1189
1132
|
// without scaling because we want consistent pixel sizes
|
|
@@ -1194,55 +1137,56 @@ function debugRender()
|
|
|
1194
1137
|
scale = cameraScale;
|
|
1195
1138
|
angle -= cameraAngle;
|
|
1196
1139
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1140
|
+
debugContext.translate(pos.x|0, pos.y|0);
|
|
1141
|
+
debugContext.rotate(angle);
|
|
1142
|
+
debugContext.scale(1, p.text ? 1 : -1);
|
|
1143
|
+
debugContext.fillStyle = p.color;
|
|
1144
|
+
debugContext.strokeStyle = p.color;
|
|
1201
1145
|
if (p.text !== undefined)
|
|
1202
1146
|
{
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1147
|
+
debugContext.font = p.size*scale + 'px '+ p.font;
|
|
1148
|
+
debugContext.textAlign = 'center';
|
|
1149
|
+
debugContext.textBaseline = 'middle';
|
|
1150
|
+
debugContext.fillText(p.text, 0, 0);
|
|
1207
1151
|
}
|
|
1208
1152
|
else if (p.points !== undefined)
|
|
1209
1153
|
{
|
|
1210
1154
|
// poly
|
|
1211
|
-
|
|
1155
|
+
debugContext.beginPath();
|
|
1212
1156
|
for (const point of p.points)
|
|
1213
1157
|
{
|
|
1214
1158
|
const p2 = point.scale(scale).floor();
|
|
1215
|
-
|
|
1159
|
+
debugContext.lineTo(p2.x, p2.y);
|
|
1216
1160
|
}
|
|
1217
|
-
|
|
1218
|
-
p.fill &&
|
|
1219
|
-
|
|
1161
|
+
debugContext.closePath();
|
|
1162
|
+
p.fill && debugContext.fill();
|
|
1163
|
+
debugContext.stroke();
|
|
1220
1164
|
}
|
|
1221
1165
|
else if (p.size === 0 || (p.size.x === 0 && p.size.y === 0))
|
|
1222
1166
|
{
|
|
1223
1167
|
// point
|
|
1224
1168
|
const pointSize = debugPointSize * scale;
|
|
1225
|
-
|
|
1226
|
-
|
|
1169
|
+
debugContext.fillRect(-pointSize/2, -1, pointSize, 3);
|
|
1170
|
+
debugContext.fillRect(-1, -pointSize/2, 3, pointSize);
|
|
1227
1171
|
}
|
|
1228
1172
|
else if (p.size.x !== undefined)
|
|
1229
1173
|
{
|
|
1230
1174
|
// rect
|
|
1231
1175
|
const s = p.size.scale(scale).floor();
|
|
1232
1176
|
const w = s.x, h = s.y;
|
|
1233
|
-
p.fill &&
|
|
1234
|
-
|
|
1177
|
+
p.fill && debugContext.fillRect(-w/2|0, -h/2|0, w, h);
|
|
1178
|
+
debugContext.strokeRect(-w/2|0, -h/2|0, w, h);
|
|
1235
1179
|
}
|
|
1236
1180
|
else
|
|
1237
1181
|
{
|
|
1238
1182
|
// circle
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
p.fill &&
|
|
1242
|
-
|
|
1183
|
+
debugContext.beginPath();
|
|
1184
|
+
debugContext.arc(0, 0, p.size*scale/2, 0, 9);
|
|
1185
|
+
p.fill && debugContext.fill();
|
|
1186
|
+
debugContext.stroke();
|
|
1243
1187
|
}
|
|
1244
1188
|
|
|
1245
|
-
|
|
1189
|
+
debugContext.restore();
|
|
1246
1190
|
});
|
|
1247
1191
|
|
|
1248
1192
|
// remove expired primitives
|
|
@@ -1251,55 +1195,53 @@ function debugRender()
|
|
|
1251
1195
|
|
|
1252
1196
|
if (debugObject)
|
|
1253
1197
|
{
|
|
1254
|
-
const saveContext = mainContext;
|
|
1255
|
-
mainContext = overlayContext;
|
|
1256
1198
|
const raycastHitPos = tileCollisionRaycast(debugObject.pos, mousePos);
|
|
1257
1199
|
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), rgb(0,1,1,.3));
|
|
1258
1200
|
drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5));
|
|
1259
1201
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
'\
|
|
1263
|
-
|
|
1202
|
+
let debugText = 'mouse pos = ' + mousePos;
|
|
1203
|
+
if (tileCollisionLayers.length)
|
|
1204
|
+
debugText += '\nmouse collision = ' + tileCollisionGetData(mousePos);
|
|
1205
|
+
debugText += '\n\n--- object info ---\n';
|
|
1206
|
+
debugText += debugObject.toString();
|
|
1264
1207
|
drawTextScreen(debugText, mousePosScreen, 24, rgb(), .05, undefined, 'center', 'monospace');
|
|
1265
|
-
mainContext = saveContext;
|
|
1266
1208
|
}
|
|
1267
1209
|
|
|
1268
1210
|
{
|
|
1269
1211
|
// draw debug overlay
|
|
1270
1212
|
const fontSize = 20;
|
|
1271
1213
|
const lineHeight = fontSize * 1.2 | 0;
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1214
|
+
debugContext.save();
|
|
1215
|
+
debugContext.fillStyle = '#fff';
|
|
1216
|
+
debugContext.textAlign = 'left';
|
|
1217
|
+
debugContext.textBaseline = 'top';
|
|
1218
|
+
debugContext.font = fontSize + 'px monospace';
|
|
1219
|
+
debugContext.shadowColor = '#000';
|
|
1220
|
+
debugContext.shadowBlur = 9;
|
|
1279
1221
|
|
|
1280
1222
|
let x = 9, y = 0, h = lineHeight;
|
|
1281
1223
|
if (debugOverlay)
|
|
1282
1224
|
{
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1225
|
+
debugContext.fillText(`${engineName} v${engineVersion}`, x, y += h/2 );
|
|
1226
|
+
debugContext.fillText('Time: ' + formatTime(time), x, y += h);
|
|
1227
|
+
debugContext.fillText('FPS: ' + averageFPS.toFixed(1) + (glEnable?' WebGL':' Canvas2D'),
|
|
1286
1228
|
x, y += h);
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1229
|
+
debugContext.fillText('Objects: ' + engineObjects.length, x, y += h);
|
|
1230
|
+
debugContext.fillText('Draw Count: ' + drawCount, x, y += h);
|
|
1231
|
+
debugContext.fillText('---------', x, y += h);
|
|
1232
|
+
debugContext.fillStyle = '#f00';
|
|
1233
|
+
debugContext.fillText('ESC: Debug Overlay', x, y += h);
|
|
1234
|
+
debugContext.fillStyle = debugPhysics ? '#f00' : '#fff';
|
|
1235
|
+
debugContext.fillText('1: Debug Physics', x, y += h);
|
|
1236
|
+
debugContext.fillStyle = debugParticles ? '#f00' : '#fff';
|
|
1237
|
+
debugContext.fillText('2: Debug Particles', x, y += h);
|
|
1238
|
+
debugContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
1239
|
+
debugContext.fillText('3: Debug Gamepads', x, y += h);
|
|
1240
|
+
debugContext.fillStyle = debugRaycast ? '#f00' : '#fff';
|
|
1241
|
+
debugContext.fillText('4: Debug Raycasts', x, y += h);
|
|
1242
|
+
debugContext.fillStyle = '#fff';
|
|
1243
|
+
debugContext.fillText('5: Save Screenshot', x, y += h);
|
|
1244
|
+
debugContext.fillText('6: Toggle Video Capture', x, y += h);
|
|
1303
1245
|
|
|
1304
1246
|
let keysPressed = '';
|
|
1305
1247
|
let mousePressed = '';
|
|
@@ -1312,8 +1254,8 @@ function debugRender()
|
|
|
1312
1254
|
else if (keyIsDown(i, 0))
|
|
1313
1255
|
keysPressed += i + ' ' ;
|
|
1314
1256
|
}
|
|
1315
|
-
mousePressed &&
|
|
1316
|
-
keysPressed &&
|
|
1257
|
+
mousePressed && debugContext.fillText('Mouse: ' + mousePressed, x, y += h);
|
|
1258
|
+
keysPressed && debugContext.fillText('Keys: ' + keysPressed, x, y += h);
|
|
1317
1259
|
|
|
1318
1260
|
// show gamepad buttons
|
|
1319
1261
|
for (let i = 1; i < inputData.length; i++)
|
|
@@ -1325,26 +1267,49 @@ function debugRender()
|
|
|
1325
1267
|
if (keyIsDown(j, i))
|
|
1326
1268
|
buttonsPressed += j + ' ' ;
|
|
1327
1269
|
}
|
|
1328
|
-
buttonsPressed &&
|
|
1270
|
+
buttonsPressed && debugContext.fillText(`Gamepad ${i-1}: ` + buttonsPressed, x, y += h);
|
|
1329
1271
|
}
|
|
1330
1272
|
}
|
|
1331
1273
|
else
|
|
1332
1274
|
{
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1275
|
+
debugContext.fillText(debugPhysics ? 'Debug Physics' : '', x, y += h);
|
|
1276
|
+
debugContext.fillText(debugParticles ? 'Debug Particles' : '', x, y += h);
|
|
1277
|
+
debugContext.fillText(debugRaycast ? 'Debug Raycasts' : '', x, y += h);
|
|
1278
|
+
debugContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
|
|
1337
1279
|
}
|
|
1338
1280
|
|
|
1339
|
-
|
|
1281
|
+
debugContext.restore();
|
|
1340
1282
|
}
|
|
1341
1283
|
}
|
|
1342
1284
|
|
|
1285
|
+
function debugRenderPost()
|
|
1286
|
+
{
|
|
1287
|
+
if (debugVideoCaptureIsActive())
|
|
1288
|
+
{
|
|
1289
|
+
debugVideoCaptureUpdate();
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
if (!debugWatermark) return;
|
|
1294
|
+
|
|
1295
|
+
// update fps display
|
|
1296
|
+
mainContext.textAlign = 'right';
|
|
1297
|
+
mainContext.textBaseline = 'top';
|
|
1298
|
+
mainContext.font = '1em monospace';
|
|
1299
|
+
mainContext.fillStyle = '#000';
|
|
1300
|
+
const text = engineName + ' v' + engineVersion + ' / '
|
|
1301
|
+
+ drawCount + ' / ' + engineObjects.length + ' / ' + averageFPS.toFixed(1)
|
|
1302
|
+
+ (glEnable ? ' GL' : ' 2D') ;
|
|
1303
|
+
mainContext.fillText(text, mainCanvas.width-3, 3);
|
|
1304
|
+
mainContext.fillStyle = '#fff';
|
|
1305
|
+
mainContext.fillText(text, mainCanvas.width-2, 2);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1343
1308
|
///////////////////////////////////////////////////////////////////////////////
|
|
1344
1309
|
// video capture - records video and audio at 60 fps using MediaRecorder API
|
|
1345
1310
|
|
|
1346
1311
|
// internal variables used to capture video
|
|
1347
|
-
let debugVideoCapture,
|
|
1312
|
+
let debugVideoCapture, debugVideoCaptureIcon;
|
|
1348
1313
|
|
|
1349
1314
|
/** Check if video capture is active
|
|
1350
1315
|
* @memberof Debug */
|
|
@@ -1354,80 +1319,103 @@ function debugVideoCaptureIsActive() { return !!debugVideoCapture; }
|
|
|
1354
1319
|
* @memberof Debug */
|
|
1355
1320
|
function debugVideoCaptureStart()
|
|
1356
1321
|
{
|
|
1357
|
-
|
|
1358
|
-
return; // already recording
|
|
1322
|
+
ASSERT(!debugVideoCaptureIsActive(), 'Already capturing video!');
|
|
1359
1323
|
|
|
1360
|
-
|
|
1324
|
+
if (!debugVideoCaptureIcon)
|
|
1325
|
+
{
|
|
1326
|
+
// create recording icon to show it is capturing video
|
|
1327
|
+
debugVideoCaptureIcon = document.createElement('div');
|
|
1328
|
+
debugVideoCaptureIcon.style.position = 'absolute';
|
|
1329
|
+
debugVideoCaptureIcon.style.padding = '9px';
|
|
1330
|
+
debugVideoCaptureIcon.style.color = '#f00';
|
|
1331
|
+
debugVideoCaptureIcon.style.font = '50px monospace';
|
|
1332
|
+
document.body.appendChild(debugVideoCaptureIcon);
|
|
1333
|
+
}
|
|
1334
|
+
// show recording icon
|
|
1335
|
+
debugVideoCaptureIcon.textContent = '';
|
|
1336
|
+
debugVideoCaptureIcon.style.display = '';
|
|
1337
|
+
|
|
1338
|
+
// setup captureStream to capture manually by passing 0
|
|
1361
1339
|
const stream = mainCanvas.captureStream(0);
|
|
1340
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
1341
|
+
const captureTimer = new Timer(0, true);
|
|
1362
1342
|
const chunks = [];
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1343
|
+
videoTrack.applyConstraints({frameRate:frameRate});
|
|
1344
|
+
|
|
1345
|
+
// set up the media recorder
|
|
1346
|
+
const mediaRecorder = new MediaRecorder(stream,
|
|
1347
|
+
{mimeType:'video/webm;codecs=vp8'});
|
|
1348
|
+
mediaRecorder.ondataavailable = (e)=> chunks.push(e.data);
|
|
1349
|
+
mediaRecorder.onstop = ()=>
|
|
1369
1350
|
{
|
|
1370
1351
|
const blob = new Blob(chunks, {type: 'video/webm'});
|
|
1371
1352
|
const url = URL.createObjectURL(blob);
|
|
1372
|
-
|
|
1373
|
-
downloadLink.href = url;
|
|
1374
|
-
downloadLink.click();
|
|
1375
|
-
URL.revokeObjectURL(url);
|
|
1353
|
+
saveDataURL(url, 'capture.webm', 1e3);
|
|
1376
1354
|
};
|
|
1377
1355
|
|
|
1378
|
-
|
|
1356
|
+
let audioStreamDestination, silentAudioSource;
|
|
1357
|
+
if (soundEnable)
|
|
1379
1358
|
{
|
|
1359
|
+
// create silent audio source
|
|
1360
|
+
// fixes issue where video can not start recording without audio
|
|
1361
|
+
silentAudioSource = new ConstantSourceNode(audioContext, { offset: 0 });
|
|
1362
|
+
silentAudioSource.connect(audioMasterGain);
|
|
1363
|
+
silentAudioSource.start();
|
|
1364
|
+
|
|
1380
1365
|
// connect to audio master gain node
|
|
1381
|
-
|
|
1366
|
+
audioStreamDestination = audioContext.createMediaStreamDestination();
|
|
1382
1367
|
audioMasterGain.connect(audioStreamDestination);
|
|
1383
1368
|
for (const track of audioStreamDestination.stream.getAudioTracks())
|
|
1384
1369
|
stream.addTrack(track); // add audio tracks to capture stream
|
|
1385
1370
|
}
|
|
1386
1371
|
|
|
1387
1372
|
// start recording
|
|
1373
|
+
try { mediaRecorder.start(); }
|
|
1374
|
+
catch(e)
|
|
1375
|
+
{
|
|
1376
|
+
LOG('Video capture not supported in this browser!');
|
|
1377
|
+
silentAudioSource?.stop();
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1388
1381
|
LOG('Video capture started.');
|
|
1389
|
-
debugVideoCapture.start();
|
|
1390
|
-
debugVideoCaptureTimer = new Timer(0);
|
|
1391
1382
|
|
|
1392
|
-
|
|
1383
|
+
// save debug video info
|
|
1384
|
+
debugVideoCapture =
|
|
1393
1385
|
{
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
document.body.appendChild(debugVideoCaptureIcon);
|
|
1401
|
-
}
|
|
1402
|
-
// show recording icon
|
|
1403
|
-
debugVideoCaptureIcon.textContent = '';
|
|
1404
|
-
debugVideoCaptureIcon.style.display = '';
|
|
1386
|
+
mediaRecorder,
|
|
1387
|
+
captureTimer,
|
|
1388
|
+
videoTrack,
|
|
1389
|
+
silentAudioSource,
|
|
1390
|
+
audioStreamDestination
|
|
1391
|
+
};
|
|
1405
1392
|
}
|
|
1406
1393
|
|
|
1407
1394
|
/** Stop capturing video and save to disk
|
|
1408
1395
|
* @memberof Debug */
|
|
1409
1396
|
function debugVideoCaptureStop()
|
|
1410
1397
|
{
|
|
1411
|
-
|
|
1412
|
-
return; // not recording
|
|
1398
|
+
ASSERT(debugVideoCaptureIsActive(), 'Not capturing video!');
|
|
1413
1399
|
|
|
1414
1400
|
// stop recording
|
|
1415
|
-
LOG(`Video capture ended. ${
|
|
1416
|
-
debugVideoCapture.stop();
|
|
1417
|
-
debugVideoCapture = 0;
|
|
1401
|
+
LOG(`Video capture ended. ${debugVideoCapture.captureTimer.get().toFixed(2)} seconds recorded.`);
|
|
1418
1402
|
debugVideoCaptureIcon.style.display = 'none';
|
|
1403
|
+
debugVideoCapture.silentAudioSource?.stop();
|
|
1404
|
+
debugVideoCapture.mediaRecorder?.stop();
|
|
1405
|
+
debugVideoCapture.videoTrack?.stop();
|
|
1406
|
+
debugVideoCapture = undefined;
|
|
1419
1407
|
}
|
|
1420
1408
|
|
|
1421
1409
|
// update video capture, called automatically by engine
|
|
1422
1410
|
function debugVideoCaptureUpdate()
|
|
1423
1411
|
{
|
|
1424
|
-
|
|
1425
|
-
return; // not recording
|
|
1412
|
+
ASSERT(debugVideoCaptureIsActive(), 'Not capturing video!');
|
|
1426
1413
|
|
|
1427
1414
|
// save the video frame
|
|
1428
1415
|
combineCanvases();
|
|
1429
|
-
|
|
1430
|
-
debugVideoCaptureIcon.textContent = '● REC '
|
|
1416
|
+
debugVideoCapture.videoTrack.requestFrame();
|
|
1417
|
+
debugVideoCaptureIcon.textContent = '● REC '
|
|
1418
|
+
+ formatTime(debugVideoCapture.captureTimer);
|
|
1431
1419
|
}
|
|
1432
1420
|
|
|
1433
1421
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1466,99 +1454,99 @@ function debugProtectConstant(obj)
|
|
|
1466
1454
|
* - Color - holds a rgba color with some math functions
|
|
1467
1455
|
* - Timer - tracks time automatically
|
|
1468
1456
|
* - RandomGenerator - seeded random number generator
|
|
1469
|
-
* @namespace
|
|
1457
|
+
* @namespace Math
|
|
1470
1458
|
*/
|
|
1471
1459
|
|
|
1472
1460
|
/** The value of PI
|
|
1473
1461
|
* @type {number}
|
|
1474
1462
|
* @default Math.PI
|
|
1475
|
-
* @memberof
|
|
1463
|
+
* @memberof Math */
|
|
1476
1464
|
const PI = Math.PI;
|
|
1477
1465
|
|
|
1478
1466
|
/** Returns absolute value of value passed in
|
|
1479
1467
|
* @param {number} value
|
|
1480
1468
|
* @return {number}
|
|
1481
|
-
* @memberof
|
|
1469
|
+
* @memberof Math */
|
|
1482
1470
|
const abs = Math.abs;
|
|
1483
1471
|
|
|
1484
1472
|
/** Returns floored value of value passed in
|
|
1485
1473
|
* @param {number} value
|
|
1486
1474
|
* @return {number}
|
|
1487
|
-
* @memberof
|
|
1475
|
+
* @memberof Math */
|
|
1488
1476
|
const floor = Math.floor;
|
|
1489
1477
|
|
|
1490
1478
|
/** Returns ceiled value of value passed in
|
|
1491
1479
|
* @param {number} value
|
|
1492
1480
|
* @return {number}
|
|
1493
|
-
* @memberof
|
|
1481
|
+
* @memberof Math */
|
|
1494
1482
|
const ceil = Math.ceil;
|
|
1495
1483
|
|
|
1496
1484
|
/** Returns rounded value passed in
|
|
1497
1485
|
* @param {number} value
|
|
1498
1486
|
* @return {number}
|
|
1499
|
-
* @memberof
|
|
1487
|
+
* @memberof Math */
|
|
1500
1488
|
const round = Math.round;
|
|
1501
1489
|
|
|
1502
1490
|
/** Returns lowest value passed in
|
|
1503
1491
|
* @param {...number} values
|
|
1504
1492
|
* @return {number}
|
|
1505
|
-
* @memberof
|
|
1493
|
+
* @memberof Math */
|
|
1506
1494
|
const min = Math.min;
|
|
1507
1495
|
|
|
1508
1496
|
/** Returns highest value passed in
|
|
1509
1497
|
* @param {...number} values
|
|
1510
1498
|
* @return {number}
|
|
1511
|
-
* @memberof
|
|
1499
|
+
* @memberof Math */
|
|
1512
1500
|
const max = Math.max;
|
|
1513
1501
|
|
|
1514
1502
|
/** Returns the sign of value passed in
|
|
1515
1503
|
* @param {number} value
|
|
1516
1504
|
* @return {number}
|
|
1517
|
-
* @memberof
|
|
1505
|
+
* @memberof Math */
|
|
1518
1506
|
const sign = Math.sign;
|
|
1519
1507
|
|
|
1520
1508
|
/** Returns hypotenuse of values passed in
|
|
1521
1509
|
* @param {...number} values
|
|
1522
1510
|
* @return {number}
|
|
1523
|
-
* @memberof
|
|
1511
|
+
* @memberof Math */
|
|
1524
1512
|
const hypot = Math.hypot;
|
|
1525
1513
|
|
|
1526
1514
|
/** Returns log2 of value passed in
|
|
1527
1515
|
* @param {number} value
|
|
1528
1516
|
* @return {number}
|
|
1529
|
-
* @memberof
|
|
1517
|
+
* @memberof Math */
|
|
1530
1518
|
const log2 = Math.log2;
|
|
1531
1519
|
|
|
1532
1520
|
/** Returns sin of value passed in
|
|
1533
1521
|
* @param {number} value
|
|
1534
1522
|
* @return {number}
|
|
1535
|
-
* @memberof
|
|
1523
|
+
* @memberof Math */
|
|
1536
1524
|
const sin = Math.sin;
|
|
1537
1525
|
|
|
1538
1526
|
/** Returns cos of value passed in
|
|
1539
1527
|
* @param {number} value
|
|
1540
1528
|
* @return {number}
|
|
1541
|
-
* @memberof
|
|
1529
|
+
* @memberof Math */
|
|
1542
1530
|
const cos = Math.cos;
|
|
1543
1531
|
|
|
1544
1532
|
/** Returns tan of value passed in
|
|
1545
1533
|
* @param {number} value
|
|
1546
1534
|
* @return {number}
|
|
1547
|
-
* @memberof
|
|
1535
|
+
* @memberof Math */
|
|
1548
1536
|
const tan = Math.tan;
|
|
1549
1537
|
|
|
1550
1538
|
/** Returns atan2 of values passed in
|
|
1551
1539
|
* @param {number} y
|
|
1552
1540
|
* @param {number} x
|
|
1553
1541
|
* @return {number}
|
|
1554
|
-
* @memberof
|
|
1542
|
+
* @memberof Math */
|
|
1555
1543
|
const atan2 = Math.atan2;
|
|
1556
1544
|
|
|
1557
1545
|
/** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
|
|
1558
1546
|
* @param {number} dividend
|
|
1559
1547
|
* @param {number} [divisor]
|
|
1560
1548
|
* @return {number}
|
|
1561
|
-
* @memberof
|
|
1549
|
+
* @memberof Math */
|
|
1562
1550
|
function mod(dividend, divisor=1) { return ((dividend % divisor) + divisor) % divisor; }
|
|
1563
1551
|
|
|
1564
1552
|
/** Clamps the value between max and min
|
|
@@ -1566,7 +1554,7 @@ function mod(dividend, divisor=1) { return ((dividend % divisor) + divisor) % di
|
|
|
1566
1554
|
* @param {number} [min]
|
|
1567
1555
|
* @param {number} [max]
|
|
1568
1556
|
* @return {number}
|
|
1569
|
-
* @memberof
|
|
1557
|
+
* @memberof Math */
|
|
1570
1558
|
function clamp(value, min=0, max=1) { return value < min ? min : value > max ? max : value; }
|
|
1571
1559
|
|
|
1572
1560
|
/** Returns what percentage the value is between valueA and valueB
|
|
@@ -1574,7 +1562,7 @@ function clamp(value, min=0, max=1) { return value < min ? min : value > max ? m
|
|
|
1574
1562
|
* @param {number} valueA
|
|
1575
1563
|
* @param {number} valueB
|
|
1576
1564
|
* @return {number}
|
|
1577
|
-
* @memberof
|
|
1565
|
+
* @memberof Math */
|
|
1578
1566
|
function percent(value, valueA, valueB)
|
|
1579
1567
|
{ return (valueB-=valueA) ? clamp((value-valueA)/valueB) : 0; }
|
|
1580
1568
|
|
|
@@ -1583,13 +1571,9 @@ function percent(value, valueA, valueB)
|
|
|
1583
1571
|
* @param {number} valueB
|
|
1584
1572
|
* @param {number} percent
|
|
1585
1573
|
* @return {number}
|
|
1586
|
-
* @memberof
|
|
1574
|
+
* @memberof Math */
|
|
1587
1575
|
function lerp(valueA, valueB, percent)
|
|
1588
|
-
{
|
|
1589
|
-
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
1590
|
-
console.warn('lerp() parameter order changed! use lerp(start, end, p)');
|
|
1591
|
-
return valueA + clamp(percent) * (valueB-valueA);
|
|
1592
|
-
}
|
|
1576
|
+
{ return valueA + clamp(percent) * (valueB-valueA); }
|
|
1593
1577
|
|
|
1594
1578
|
/** Gets percent between percentA and percentB and linearly interpolates between lerpA and lerpB
|
|
1595
1579
|
* A shortcut for lerp(lerpA, lerpB, percent(value, percentA, percentB))
|
|
@@ -1599,7 +1583,7 @@ function lerp(valueA, valueB, percent)
|
|
|
1599
1583
|
* @param {number} lerpA
|
|
1600
1584
|
* @param {number} lerpB
|
|
1601
1585
|
* @return {number}
|
|
1602
|
-
* @memberof
|
|
1586
|
+
* @memberof Math */
|
|
1603
1587
|
function percentLerp(value, percentA, percentB, lerpA, lerpB)
|
|
1604
1588
|
{ return lerp(lerpA, lerpB, percent(value, percentA, percentB)); }
|
|
1605
1589
|
|
|
@@ -1608,7 +1592,7 @@ function percentLerp(value, percentA, percentB, lerpA, lerpB)
|
|
|
1608
1592
|
* @param {number} valueB
|
|
1609
1593
|
* @param {number} [wrapSize]
|
|
1610
1594
|
* @return {number}
|
|
1611
|
-
* @memberof
|
|
1595
|
+
* @memberof Math */
|
|
1612
1596
|
function distanceWrap(valueA, valueB, wrapSize=1)
|
|
1613
1597
|
{ const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
|
|
1614
1598
|
|
|
@@ -1618,19 +1602,15 @@ function distanceWrap(valueA, valueB, wrapSize=1)
|
|
|
1618
1602
|
* @param {number} percent
|
|
1619
1603
|
* @param {number} [wrapSize]
|
|
1620
1604
|
* @return {number}
|
|
1621
|
-
* @memberof
|
|
1605
|
+
* @memberof Math */
|
|
1622
1606
|
function lerpWrap(valueA, valueB, percent, wrapSize=1)
|
|
1623
|
-
{
|
|
1624
|
-
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
1625
|
-
console.warn('lerpWrap() parameter order changed! use lerpWrap(start, end, p)');
|
|
1626
|
-
return valueA + clamp(percent) * distanceWrap(valueB, valueA, wrapSize);
|
|
1627
|
-
}
|
|
1607
|
+
{ return valueA + clamp(percent) * distanceWrap(valueB, valueA, wrapSize); }
|
|
1628
1608
|
|
|
1629
1609
|
/** Returns signed wrapped distance between the two angles passed in
|
|
1630
1610
|
* @param {number} angleA
|
|
1631
1611
|
* @param {number} angleB
|
|
1632
1612
|
* @return {number}
|
|
1633
|
-
* @memberof
|
|
1613
|
+
* @memberof Math */
|
|
1634
1614
|
function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
|
|
1635
1615
|
|
|
1636
1616
|
/** Linearly interpolates between the angles passed in with wrapping
|
|
@@ -1638,25 +1618,25 @@ function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*P
|
|
|
1638
1618
|
* @param {number} angleB
|
|
1639
1619
|
* @param {number} percent
|
|
1640
1620
|
* @return {number}
|
|
1641
|
-
* @memberof
|
|
1621
|
+
* @memberof Math */
|
|
1642
1622
|
function lerpAngle(angleA, angleB, percent) { return lerpWrap(angleA, angleB, percent, 2*PI); }
|
|
1643
1623
|
|
|
1644
1624
|
/** Applies smoothstep function to the percentage value
|
|
1645
1625
|
* @param {number} percent
|
|
1646
1626
|
* @return {number}
|
|
1647
|
-
* @memberof
|
|
1627
|
+
* @memberof Math */
|
|
1648
1628
|
function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
|
|
1649
1629
|
|
|
1650
1630
|
/** Checks if the value passed in is a power of two
|
|
1651
1631
|
* @param {number} value
|
|
1652
1632
|
* @return {boolean}
|
|
1653
|
-
* @memberof
|
|
1633
|
+
* @memberof Math */
|
|
1654
1634
|
function isPowerOfTwo(value) { return !(value & (value - 1)); }
|
|
1655
1635
|
|
|
1656
1636
|
/** Returns the nearest power of two not less than the value
|
|
1657
1637
|
* @param {number} value
|
|
1658
1638
|
* @return {number}
|
|
1659
|
-
* @memberof
|
|
1639
|
+
* @memberof Math */
|
|
1660
1640
|
function nearestPowerOfTwo(value) { return 2**ceil(log2(value)); }
|
|
1661
1641
|
|
|
1662
1642
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
@@ -1666,7 +1646,7 @@ function nearestPowerOfTwo(value) { return 2**ceil(log2(value)); }
|
|
|
1666
1646
|
* @param {Vector2} posB - Center of box B
|
|
1667
1647
|
* @param {Vector2} [sizeB=(0,0)] - Size of box B, uses a point if undefined
|
|
1668
1648
|
* @return {boolean} - True if overlapping
|
|
1669
|
-
* @memberof
|
|
1649
|
+
* @memberof Math */
|
|
1670
1650
|
function isOverlapping(posA, sizeA, posB, sizeB=vec2())
|
|
1671
1651
|
{
|
|
1672
1652
|
const dx = (posA.x - posB.x)*2;
|
|
@@ -1682,7 +1662,7 @@ function isOverlapping(posA, sizeA, posB, sizeB=vec2())
|
|
|
1682
1662
|
* @param {Vector2} pos - Center of box
|
|
1683
1663
|
* @param {Vector2} size - Size of box
|
|
1684
1664
|
* @return {boolean} - True if intersecting
|
|
1685
|
-
* @memberof
|
|
1665
|
+
* @memberof Math */
|
|
1686
1666
|
function isIntersecting(start, end, pos, size)
|
|
1687
1667
|
{
|
|
1688
1668
|
// Liang-Barsky algorithm
|
|
@@ -1723,52 +1703,29 @@ function isIntersecting(start, end, pos, size)
|
|
|
1723
1703
|
* @param {number} [t=time] - Value to use for time of the wave
|
|
1724
1704
|
* @param {number} [offset] - Value to use for time offset of the wave
|
|
1725
1705
|
* @return {number} - Value waving between 0 and amplitude
|
|
1726
|
-
* @memberof
|
|
1706
|
+
* @memberof Math */
|
|
1727
1707
|
function wave(frequency=1, amplitude=1, t=time, offset=0)
|
|
1728
1708
|
{ return amplitude/2 * (1 - cos(offset + t*frequency*2*PI)); }
|
|
1729
1709
|
|
|
1730
|
-
/** Formats seconds to mm:ss style for display purposes
|
|
1731
|
-
* @param {number} t - time in seconds
|
|
1732
|
-
* @return {string}
|
|
1733
|
-
* @memberof Utilities */
|
|
1734
|
-
function formatTime(t)
|
|
1735
|
-
{
|
|
1736
|
-
const sign = t < 0 ? '-' : '';
|
|
1737
|
-
t = abs(t)|0;
|
|
1738
|
-
return sign + (t/60|0) + ':' + (t%60<10?'0':'') + t%60;
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
/** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
|
|
1742
|
-
* @param {string} url - URL of JSON file
|
|
1743
|
-
* @return {Promise<object>}
|
|
1744
|
-
* @memberof Utilities */
|
|
1745
|
-
async function fetchJSON(url)
|
|
1746
|
-
{
|
|
1747
|
-
const response = await fetch(url);
|
|
1748
|
-
if (!response.ok)
|
|
1749
|
-
throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
|
|
1750
|
-
return response.json();
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
1710
|
/**
|
|
1754
1711
|
* Check if object is a valid number, not NaN or undefined, but it may be infinite
|
|
1755
1712
|
* @param {any} n
|
|
1756
1713
|
* @return {boolean}
|
|
1757
|
-
* @memberof
|
|
1714
|
+
* @memberof Math */
|
|
1758
1715
|
function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
1759
1716
|
|
|
1760
1717
|
/**
|
|
1761
1718
|
* Check if object is a valid string or can be converted to one
|
|
1762
1719
|
* @param {any} s
|
|
1763
1720
|
* @return {boolean}
|
|
1764
|
-
* @memberof
|
|
1721
|
+
* @memberof Math */
|
|
1765
1722
|
function isString(s) { return s !== undefined && s !== null && typeof s.toString() === 'string'; }
|
|
1766
1723
|
|
|
1767
1724
|
/**
|
|
1768
1725
|
* Check if object is an array
|
|
1769
1726
|
* @param {any} a
|
|
1770
1727
|
* @return {boolean}
|
|
1771
|
-
* @memberof
|
|
1728
|
+
* @memberof Math */
|
|
1772
1729
|
function isArray(a) { return Array.isArray(a); }
|
|
1773
1730
|
|
|
1774
1731
|
/**
|
|
@@ -1784,7 +1741,7 @@ function isArray(a) { return Array.isArray(a); }
|
|
|
1784
1741
|
* @param {LineTestFunction} testFunction - Check if colliding
|
|
1785
1742
|
* @param {Vector2} [normal] - Optional vector to store the normal
|
|
1786
1743
|
* @return {Vector2|undefined} - Position of the collision or undefined if none found
|
|
1787
|
-
* @memberof
|
|
1744
|
+
* @memberof Math */
|
|
1788
1745
|
function lineTest(posStart, posEnd, testFunction, normal)
|
|
1789
1746
|
{
|
|
1790
1747
|
ASSERT(isVector2(posStart), 'posStart must be a vec2');
|
|
@@ -1796,8 +1753,7 @@ function lineTest(posStart, posEnd, testFunction, normal)
|
|
|
1796
1753
|
const dx = posEnd.x - posStart.x;
|
|
1797
1754
|
const dy = posEnd.y - posStart.y;
|
|
1798
1755
|
const totalLength = hypot(dx, dy);
|
|
1799
|
-
if (!totalLength)
|
|
1800
|
-
return;
|
|
1756
|
+
if (!totalLength) return;
|
|
1801
1757
|
|
|
1802
1758
|
// current integer cell we are in
|
|
1803
1759
|
const pos = posStart.floor();
|
|
@@ -2031,14 +1987,14 @@ class RandomGenerator
|
|
|
2031
1987
|
* let a = vec2(0, 1); // vector with coordinates (0, 1)
|
|
2032
1988
|
* a = vec2(5); // set a to (5, 5)
|
|
2033
1989
|
* b = vec2(); // set b to (0, 0)
|
|
2034
|
-
* @memberof
|
|
1990
|
+
* @memberof Math */
|
|
2035
1991
|
function vec2(x=0, y) { return new Vector2(x, y === undefined ? x : y); }
|
|
2036
1992
|
|
|
2037
1993
|
/**
|
|
2038
1994
|
* Check if object is a valid Vector2
|
|
2039
1995
|
* @param {any} v
|
|
2040
1996
|
* @return {boolean}
|
|
2041
|
-
* @memberof
|
|
1997
|
+
* @memberof Math */
|
|
2042
1998
|
function isVector2(v) { return v instanceof Vector2 && v.isValid(); }
|
|
2043
1999
|
|
|
2044
2000
|
// vector2 asserts
|
|
@@ -2288,7 +2244,7 @@ class Vector2
|
|
|
2288
2244
|
* @param {number} [b=1] - blue
|
|
2289
2245
|
* @param {number} [a=1] - alpha
|
|
2290
2246
|
* @return {Color}
|
|
2291
|
-
* @memberof
|
|
2247
|
+
* @memberof Math
|
|
2292
2248
|
*/
|
|
2293
2249
|
function rgb(r, g, b, a) { return new Color(r, g, b, a); }
|
|
2294
2250
|
|
|
@@ -2299,14 +2255,14 @@ function rgb(r, g, b, a) { return new Color(r, g, b, a); }
|
|
|
2299
2255
|
* @param {number} [l=1] - lightness
|
|
2300
2256
|
* @param {number} [a=1] - alpha
|
|
2301
2257
|
* @return {Color}
|
|
2302
|
-
* @memberof
|
|
2258
|
+
* @memberof Math */
|
|
2303
2259
|
function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
|
|
2304
2260
|
|
|
2305
2261
|
/**
|
|
2306
2262
|
* Check if object is a valid Color
|
|
2307
2263
|
* @param {any} c
|
|
2308
2264
|
* @return {boolean}
|
|
2309
|
-
* @memberof
|
|
2265
|
+
* @memberof Math */
|
|
2310
2266
|
function isColor(c) { return c instanceof Color && c.isValid(); }
|
|
2311
2267
|
|
|
2312
2268
|
// color asserts
|
|
@@ -2487,7 +2443,7 @@ class Color
|
|
|
2487
2443
|
toString(useAlpha = true)
|
|
2488
2444
|
{
|
|
2489
2445
|
if (debug && !this.isValid())
|
|
2490
|
-
return
|
|
2446
|
+
return '#000';
|
|
2491
2447
|
const toHex = (c)=> ((c=clamp(c)*255|0)<16 ? '0' : '') + c.toString(16);
|
|
2492
2448
|
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
2493
2449
|
}
|
|
@@ -2544,67 +2500,67 @@ class Color
|
|
|
2544
2500
|
|
|
2545
2501
|
/** Color - White #ffffff
|
|
2546
2502
|
* @type {Color}
|
|
2547
|
-
* @memberof
|
|
2503
|
+
* @memberof Math */
|
|
2548
2504
|
const WHITE = debugProtectConstant(rgb());
|
|
2549
2505
|
|
|
2550
2506
|
/** Color - Clear White #757474ff with 0 alpha
|
|
2551
2507
|
* @type {Color}
|
|
2552
|
-
* @memberof
|
|
2508
|
+
* @memberof Math */
|
|
2553
2509
|
const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
|
|
2554
2510
|
|
|
2555
2511
|
/** Color - Black #000000
|
|
2556
2512
|
* @type {Color}
|
|
2557
|
-
* @memberof
|
|
2513
|
+
* @memberof Math */
|
|
2558
2514
|
const BLACK = debugProtectConstant(rgb(0,0,0));
|
|
2559
2515
|
|
|
2560
2516
|
/** Color - Clear Black #000000 with 0 alpha
|
|
2561
2517
|
* @type {Color}
|
|
2562
|
-
* @memberof
|
|
2518
|
+
* @memberof Math */
|
|
2563
2519
|
const CLEAR_BLACK = debugProtectConstant(rgb(0,0,0,0));
|
|
2564
2520
|
|
|
2565
2521
|
/** Color - Gray #808080
|
|
2566
2522
|
* @type {Color}
|
|
2567
|
-
* @memberof
|
|
2523
|
+
* @memberof Math */
|
|
2568
2524
|
const GRAY = debugProtectConstant(rgb(.5,.5,.5));
|
|
2569
2525
|
|
|
2570
2526
|
/** Color - Red #ff0000
|
|
2571
2527
|
* @type {Color}
|
|
2572
|
-
* @memberof
|
|
2528
|
+
* @memberof Math */
|
|
2573
2529
|
const RED = debugProtectConstant(rgb(1,0,0));
|
|
2574
2530
|
|
|
2575
2531
|
/** Color - Orange #ff8000
|
|
2576
2532
|
* @type {Color}
|
|
2577
|
-
* @memberof
|
|
2533
|
+
* @memberof Math */
|
|
2578
2534
|
const ORANGE = debugProtectConstant(rgb(1,.5,0));
|
|
2579
2535
|
|
|
2580
2536
|
/** Color - Yellow #ffff00
|
|
2581
2537
|
* @type {Color}
|
|
2582
|
-
* @memberof
|
|
2538
|
+
* @memberof Math */
|
|
2583
2539
|
const YELLOW = debugProtectConstant(rgb(1,1,0));
|
|
2584
2540
|
|
|
2585
2541
|
/** Color - Green #00ff00
|
|
2586
2542
|
* @type {Color}
|
|
2587
|
-
* @memberof
|
|
2543
|
+
* @memberof Math */
|
|
2588
2544
|
const GREEN = debugProtectConstant(rgb(0,1,0));
|
|
2589
2545
|
|
|
2590
2546
|
/** Color - Cyan #00ffff
|
|
2591
2547
|
* @type {Color}
|
|
2592
|
-
* @memberof
|
|
2548
|
+
* @memberof Math */
|
|
2593
2549
|
const CYAN = debugProtectConstant(rgb(0,1,1));
|
|
2594
2550
|
|
|
2595
2551
|
/** Color - Blue #0000ff
|
|
2596
2552
|
* @type {Color}
|
|
2597
|
-
* @memberof
|
|
2553
|
+
* @memberof Math */
|
|
2598
2554
|
const BLUE = debugProtectConstant(rgb(0,0,1));
|
|
2599
2555
|
|
|
2600
2556
|
/** Color - Purple #8000ff
|
|
2601
2557
|
* @type {Color}
|
|
2602
|
-
* @memberof
|
|
2558
|
+
* @memberof Math */
|
|
2603
2559
|
const PURPLE = debugProtectConstant(rgb(.5,0,1));
|
|
2604
2560
|
|
|
2605
2561
|
/** Color - Magenta #ff00ff
|
|
2606
2562
|
* @type {Color}
|
|
2607
|
-
* @memberof
|
|
2563
|
+
* @memberof Math */
|
|
2608
2564
|
const MAGENTA = debugProtectConstant(rgb(1,0,1));
|
|
2609
2565
|
|
|
2610
2566
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -2690,6 +2646,84 @@ class Timer
|
|
|
2690
2646
|
* @return {number} */
|
|
2691
2647
|
valueOf() { return this.get(); }
|
|
2692
2648
|
}
|
|
2649
|
+
/**
|
|
2650
|
+
* LittleJS Utility Classes and Functions
|
|
2651
|
+
* - General purpose math library
|
|
2652
|
+
* - Vector2 - fast, simple, easy 2D vector class
|
|
2653
|
+
* - Color - holds a rgba color with some math functions
|
|
2654
|
+
* - Timer - tracks time automatically
|
|
2655
|
+
* - RandomGenerator - seeded random number generator
|
|
2656
|
+
* @namespace Utilities
|
|
2657
|
+
*/
|
|
2658
|
+
|
|
2659
|
+
/** Formats seconds to mm:ss style for display purposes
|
|
2660
|
+
* @param {number} t - time in seconds
|
|
2661
|
+
* @return {string}
|
|
2662
|
+
* @memberof Utilities */
|
|
2663
|
+
function formatTime(t)
|
|
2664
|
+
{
|
|
2665
|
+
const sign = t < 0 ? '-' : '';
|
|
2666
|
+
t = abs(t)|0;
|
|
2667
|
+
return sign + (t/60|0) + ':' + (t%60<10?'0':'') + t%60;
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
/** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
|
|
2671
|
+
* @param {string} url - URL of JSON file
|
|
2672
|
+
* @return {Promise<object>}
|
|
2673
|
+
* @memberof Utilities */
|
|
2674
|
+
async function fetchJSON(url)
|
|
2675
|
+
{
|
|
2676
|
+
const response = await fetch(url);
|
|
2677
|
+
if (!response.ok)
|
|
2678
|
+
throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
|
|
2679
|
+
return response.json();
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2683
|
+
|
|
2684
|
+
/** Save a text file to disk
|
|
2685
|
+
* @param {string} text
|
|
2686
|
+
* @param {string} [filename]
|
|
2687
|
+
* @param {string} [type]
|
|
2688
|
+
* @memberof Utilities */
|
|
2689
|
+
function saveText(text, filename='text', type='text/plain')
|
|
2690
|
+
{ saveDataURL(URL.createObjectURL(new Blob([text], {'type':type})), filename); }
|
|
2691
|
+
|
|
2692
|
+
/** Save a canvas to disk
|
|
2693
|
+
* @param {HTMLCanvasElement|OffscreenCanvas} canvas
|
|
2694
|
+
* @param {string} [filename]
|
|
2695
|
+
* @param {string} [type]
|
|
2696
|
+
* @memberof Utilities */
|
|
2697
|
+
function saveCanvas(canvas, filename='screenshot', type='image/png')
|
|
2698
|
+
{
|
|
2699
|
+
if (canvas instanceof OffscreenCanvas)
|
|
2700
|
+
{
|
|
2701
|
+
// copy to temporary canvas and save
|
|
2702
|
+
const saveCanvas = document.createElement('canvas');
|
|
2703
|
+
saveCanvas.width = canvas.width;
|
|
2704
|
+
saveCanvas.height = canvas.height;
|
|
2705
|
+
saveCanvas.getContext('2d').drawImage(canvas, 0, 0);
|
|
2706
|
+
saveDataURL(saveCanvas.toDataURL(type), filename);
|
|
2707
|
+
}
|
|
2708
|
+
else
|
|
2709
|
+
saveDataURL(canvas.toDataURL(type), filename);
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
/** Save a data url to disk
|
|
2713
|
+
* @param {string} url
|
|
2714
|
+
* @param {string} [filename]
|
|
2715
|
+
* @param {number} [revokeTime] - how long before revoking the url
|
|
2716
|
+
* @memberof Utilities */
|
|
2717
|
+
function saveDataURL(url, filename='download', revokeTime)
|
|
2718
|
+
{
|
|
2719
|
+
// create link for saving screenshots
|
|
2720
|
+
const link = document.createElement('a');
|
|
2721
|
+
link.download = filename;
|
|
2722
|
+
link.href = url;
|
|
2723
|
+
link.click();
|
|
2724
|
+
if (revokeTime !== undefined)
|
|
2725
|
+
setTimeout(()=> URL.revokeObjectURL(url), revokeTime);
|
|
2726
|
+
}
|
|
2693
2727
|
/**
|
|
2694
2728
|
* LittleJS Engine Settings
|
|
2695
2729
|
* - All settings for the engine are here
|
|
@@ -2727,7 +2761,7 @@ let cameraScale = 32;
|
|
|
2727
2761
|
* @memberof Settings */
|
|
2728
2762
|
let canvasColorTiles = true;
|
|
2729
2763
|
|
|
2730
|
-
/** Color to clear the canvas to before render
|
|
2764
|
+
/** Color to clear the canvas to before render, does not clear if alpha is 0
|
|
2731
2765
|
* @type {Color}
|
|
2732
2766
|
* @memberof Draw */
|
|
2733
2767
|
let canvasClearColor = CLEAR_BLACK;
|
|
@@ -2764,15 +2798,7 @@ let canvasFixedSize = vec2();
|
|
|
2764
2798
|
* @type {boolean}
|
|
2765
2799
|
* @default
|
|
2766
2800
|
* @memberof Settings */
|
|
2767
|
-
let canvasPixelated =
|
|
2768
|
-
|
|
2769
|
-
/** Use nearest canvas scaling for more pixelated look
|
|
2770
|
-
* - If enabled sets css image-rendering:pixelated
|
|
2771
|
-
* - This defaults to false because text looks better with smoothing
|
|
2772
|
-
* @type {boolean}
|
|
2773
|
-
* @default
|
|
2774
|
-
* @memberof Settings */
|
|
2775
|
-
let overlayCanvasPixelated = false;
|
|
2801
|
+
let canvasPixelated = false;
|
|
2776
2802
|
|
|
2777
2803
|
/** Disables texture filtering for crisper pixel art
|
|
2778
2804
|
* @type {boolean}
|
|
@@ -2820,13 +2846,19 @@ let glCircleSides = 32;
|
|
|
2820
2846
|
* @type {Vector2}
|
|
2821
2847
|
* @default Vector2(16,16)
|
|
2822
2848
|
* @memberof Settings */
|
|
2823
|
-
let
|
|
2849
|
+
let tileDefaultSize = vec2(16);
|
|
2824
2850
|
|
|
2825
|
-
/**
|
|
2851
|
+
/** Default padding pixels around tiles
|
|
2826
2852
|
* @type {number}
|
|
2827
2853
|
* @default
|
|
2828
2854
|
* @memberof Settings */
|
|
2829
|
-
let
|
|
2855
|
+
let tileDefaultPadding = 0;
|
|
2856
|
+
|
|
2857
|
+
/** Default amount of pixels smaller to draw tiles to prevent neighbor bleeding
|
|
2858
|
+
* @type {number}
|
|
2859
|
+
* @default
|
|
2860
|
+
* @memberof Settings */
|
|
2861
|
+
let tileDefaultBleed = 0;
|
|
2830
2862
|
|
|
2831
2863
|
///////////////////////////////////////////////////////////////////////////////
|
|
2832
2864
|
// Object settings
|
|
@@ -3038,7 +3070,7 @@ function setCameraScale(scale) { cameraScale = scale; }
|
|
|
3038
3070
|
* @memberof Settings */
|
|
3039
3071
|
function setCanvasColorTiles(colorTiles) { canvasColorTiles = colorTiles; }
|
|
3040
3072
|
|
|
3041
|
-
/** Set color to clear the canvas to before render
|
|
3073
|
+
/** Set color to clear the canvas to before render, does not clear if alpha is 0
|
|
3042
3074
|
* @param {Color} color
|
|
3043
3075
|
* @memberof Settings */
|
|
3044
3076
|
function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
|
|
@@ -3064,7 +3096,6 @@ function setCanvasMaxAspect(aspect) { canvasMaxAspect = aspect; }
|
|
|
3064
3096
|
function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
|
|
3065
3097
|
|
|
3066
3098
|
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
3067
|
-
* - If enabled sets css image-rendering:pixelated
|
|
3068
3099
|
* @param {boolean} pixelated
|
|
3069
3100
|
* @memberof Settings */
|
|
3070
3101
|
function setCanvasPixelated(pixelated)
|
|
@@ -3076,18 +3107,6 @@ function setCanvasPixelated(pixelated)
|
|
|
3076
3107
|
glCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
3077
3108
|
}
|
|
3078
3109
|
|
|
3079
|
-
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
3080
|
-
* - If enabled sets css image-rendering:pixelated
|
|
3081
|
-
* - This defaults to false because text looks better with smoothing
|
|
3082
|
-
* @param {boolean} pixelated
|
|
3083
|
-
* @memberof Settings */
|
|
3084
|
-
function setOverlayCanvasPixelated(pixelated)
|
|
3085
|
-
{
|
|
3086
|
-
overlayCanvasPixelated = pixelated;
|
|
3087
|
-
if (overlayCanvas)
|
|
3088
|
-
overlayCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
3089
|
-
}
|
|
3090
|
-
|
|
3091
3110
|
/** Disables texture filtering for crisper pixel art
|
|
3092
3111
|
* @param {boolean} pixelated
|
|
3093
3112
|
* @memberof Settings */
|
|
@@ -3131,12 +3150,17 @@ function setGLCircleSides(sides) { glCircleSides = sides; }
|
|
|
3131
3150
|
/** Set default size of tiles in pixels
|
|
3132
3151
|
* @param {Vector2} size
|
|
3133
3152
|
* @memberof Settings */
|
|
3134
|
-
function
|
|
3153
|
+
function setTileDefaultSize(size) { tileDefaultSize = size.copy(); }
|
|
3135
3154
|
|
|
3136
|
-
/**
|
|
3137
|
-
* @param {number}
|
|
3155
|
+
/** Default padding pixels around tiles
|
|
3156
|
+
* @param {number} padding
|
|
3138
3157
|
* @memberof Settings */
|
|
3139
|
-
function
|
|
3158
|
+
function setTileDefaultPadding(padding) { tileDefaultPadding = padding; }
|
|
3159
|
+
|
|
3160
|
+
/** Default amount of pixels smaller to draw tiles to prevent neighbor bleeding
|
|
3161
|
+
* @param {number} bleed
|
|
3162
|
+
* @memberof Settings */
|
|
3163
|
+
function setTileDefaultBleed(bleed) { tileDefaultBleed = bleed; }
|
|
3140
3164
|
|
|
3141
3165
|
/** Set if collisions between objects are enabled
|
|
3142
3166
|
* @param {boolean} enable
|
|
@@ -3287,7 +3311,7 @@ function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUn
|
|
|
3287
3311
|
/** Set if watermark with FPS should be shown
|
|
3288
3312
|
* @param {boolean} show
|
|
3289
3313
|
* @memberof Debug */
|
|
3290
|
-
function
|
|
3314
|
+
function setDebugWatermark(show) { debugWatermark = show; }
|
|
3291
3315
|
|
|
3292
3316
|
/** Set key code used to toggle debug mode, Esc by default
|
|
3293
3317
|
* @param {string} key
|
|
@@ -3723,10 +3747,11 @@ class EngineObject
|
|
|
3723
3747
|
* @return {number} -1 if this.mirror is true, or 1 if not mirrored */
|
|
3724
3748
|
getMirrorSign() { return this.mirror ? -1 : 1; }
|
|
3725
3749
|
|
|
3726
|
-
/** Attaches a child to this with a
|
|
3750
|
+
/** Attaches a child to this with a local transform, returns child for chaining
|
|
3727
3751
|
* @param {EngineObject} child
|
|
3728
3752
|
* @param {Vector2} [localPos=(0,0)]
|
|
3729
|
-
* @param {number} [localAngle]
|
|
3753
|
+
* @param {number} [localAngle]
|
|
3754
|
+
* @return {EngineObject} The child object added */
|
|
3730
3755
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
3731
3756
|
{
|
|
3732
3757
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
@@ -3736,6 +3761,7 @@ class EngineObject
|
|
|
3736
3761
|
child.parent = this;
|
|
3737
3762
|
child.localPos = localPos.copy();
|
|
3738
3763
|
child.localAngle = localAngle;
|
|
3764
|
+
return child;
|
|
3739
3765
|
}
|
|
3740
3766
|
|
|
3741
3767
|
/** Removes a child from this one
|
|
@@ -3802,8 +3828,7 @@ class EngineObject
|
|
|
3802
3828
|
/** Render debug info for this object */
|
|
3803
3829
|
renderDebugInfo()
|
|
3804
3830
|
{
|
|
3805
|
-
if (!debug)
|
|
3806
|
-
return;
|
|
3831
|
+
if (!debug) return;
|
|
3807
3832
|
|
|
3808
3833
|
// show object info for debugging
|
|
3809
3834
|
const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
|
|
@@ -3826,7 +3851,6 @@ class EngineObject
|
|
|
3826
3851
|
* There are 3 canvas/contexts available to draw to...
|
|
3827
3852
|
* mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
|
|
3828
3853
|
* glCanvas - Used by the accelerated WebGL batch rendering system.
|
|
3829
|
-
* overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
|
|
3830
3854
|
*
|
|
3831
3855
|
* The WebGL rendering system is very fast with some caveats...
|
|
3832
3856
|
* - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
|
|
@@ -3846,21 +3870,6 @@ let mainCanvas;
|
|
|
3846
3870
|
* @memberof Draw */
|
|
3847
3871
|
let mainContext;
|
|
3848
3872
|
|
|
3849
|
-
/** A canvas that appears on top of everything the same size as mainCanvas
|
|
3850
|
-
* @type {HTMLCanvasElement}
|
|
3851
|
-
* @memberof Draw */
|
|
3852
|
-
let overlayCanvas;
|
|
3853
|
-
|
|
3854
|
-
/** 2d context for overlayCanvas
|
|
3855
|
-
* @type {CanvasRenderingContext2D}
|
|
3856
|
-
* @memberof Draw */
|
|
3857
|
-
let overlayContext;
|
|
3858
|
-
|
|
3859
|
-
/** The default canvas to use for drawing, usually mainCanvas
|
|
3860
|
-
* @type {HTMLCanvasElement|OffscreenCanvas}
|
|
3861
|
-
* @memberof Draw */
|
|
3862
|
-
let drawCanvas;
|
|
3863
|
-
|
|
3864
3873
|
/** The default 2d context to use for drawing, usually mainContext
|
|
3865
3874
|
* @type {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D}
|
|
3866
3875
|
* @memberof Draw */
|
|
@@ -3876,6 +3885,16 @@ let workCanvas;
|
|
|
3876
3885
|
* @memberof Draw */
|
|
3877
3886
|
let workContext;
|
|
3878
3887
|
|
|
3888
|
+
/** Offscreen canvas with willReadFrequently that can be used for image processing
|
|
3889
|
+
* @type {OffscreenCanvas}
|
|
3890
|
+
* @memberof Draw */
|
|
3891
|
+
let workReadCanvas;
|
|
3892
|
+
|
|
3893
|
+
/** Offscreen canvas with willReadFrequently that can be used for image processing
|
|
3894
|
+
* @type {OffscreenCanvasRenderingContext2D}
|
|
3895
|
+
* @memberof Draw */
|
|
3896
|
+
let workReadContext;
|
|
3897
|
+
|
|
3879
3898
|
/** The size of the main canvas (and other secondary canvases)
|
|
3880
3899
|
* @type {Vector2}
|
|
3881
3900
|
* @memberof Draw */
|
|
@@ -3898,7 +3917,7 @@ let drawCount;
|
|
|
3898
3917
|
* - This can take vecs or floats for easier use and conversion
|
|
3899
3918
|
* - If an index is passed in, the tile size and index will determine the position
|
|
3900
3919
|
* @param {Vector2|number} [pos=0] - Position of the tile in pixels, or tile index
|
|
3901
|
-
* @param {Vector2|number} [size
|
|
3920
|
+
* @param {Vector2|number} [size] - Size of tile in pixels
|
|
3902
3921
|
* @param {number} [textureIndex] - Texture index to use
|
|
3903
3922
|
* @param {number} [padding] - How many pixels padding around tiles
|
|
3904
3923
|
* @return {TileInfo}
|
|
@@ -3908,7 +3927,7 @@ let drawCount;
|
|
|
3908
3927
|
* tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
|
|
3909
3928
|
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
3910
3929
|
* @memberof Draw */
|
|
3911
|
-
function tile(pos=new Vector2, size=
|
|
3930
|
+
function tile(pos=new Vector2, size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding)
|
|
3912
3931
|
{
|
|
3913
3932
|
if (headlessMode)
|
|
3914
3933
|
return new TileInfo;
|
|
@@ -3947,13 +3966,13 @@ function tile(pos=new Vector2, size=tileSizeDefault, textureIndex=0, padding=0)
|
|
|
3947
3966
|
class TileInfo
|
|
3948
3967
|
{
|
|
3949
3968
|
/** Create a tile info object
|
|
3950
|
-
* @param {Vector2} [pos=(0,0)]
|
|
3951
|
-
* @param {Vector2} [size
|
|
3952
|
-
* @param {number} [textureIndex]
|
|
3953
|
-
* @param {number} [padding]
|
|
3954
|
-
* @param {number} [
|
|
3969
|
+
* @param {Vector2} [pos=(0,0)] - Top left corner of tile in pixels
|
|
3970
|
+
* @param {Vector2} [size] - Size of tile in pixels
|
|
3971
|
+
* @param {number} [textureIndex] - Texture index to use
|
|
3972
|
+
* @param {number} [padding] - How many pixels padding around tiles
|
|
3973
|
+
* @param {number} [bleed] - How many pixels smaller to draw tiles
|
|
3955
3974
|
*/
|
|
3956
|
-
constructor(pos=vec2(), size=
|
|
3975
|
+
constructor(pos=vec2(), size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding, bleed=tileDefaultBleed)
|
|
3957
3976
|
{
|
|
3958
3977
|
/** @property {Vector2} - Top left corner of tile in pixels */
|
|
3959
3978
|
this.pos = pos.copy();
|
|
@@ -3966,7 +3985,7 @@ class TileInfo
|
|
|
3966
3985
|
/** @property {TextureInfo} - The texture info for this tile */
|
|
3967
3986
|
this.textureInfo = textureInfos[this.textureIndex];
|
|
3968
3987
|
/** @property {number} - Shrinks tile by this many pixels to prevent neighbors bleeding */
|
|
3969
|
-
this.
|
|
3988
|
+
this.bleed = bleed;
|
|
3970
3989
|
}
|
|
3971
3990
|
|
|
3972
3991
|
/** Returns a copy of this tile offset by a vector
|
|
@@ -3974,7 +3993,7 @@ class TileInfo
|
|
|
3974
3993
|
* @return {TileInfo}
|
|
3975
3994
|
*/
|
|
3976
3995
|
offset(offset)
|
|
3977
|
-
{ return new TileInfo(this.pos.add(offset), this.size, this.textureIndex, this.padding, this.
|
|
3996
|
+
{ return new TileInfo(this.pos.add(offset), this.size, this.textureIndex, this.padding, this.bleed); }
|
|
3978
3997
|
|
|
3979
3998
|
/** Returns a copy of this tile offset by a number of animation frames
|
|
3980
3999
|
* @param {number} frame - Offset to apply in animation frames
|
|
@@ -3997,7 +4016,7 @@ class TileInfo
|
|
|
3997
4016
|
this.size = textureInfo.size.copy();
|
|
3998
4017
|
this.textureInfo = textureInfo;
|
|
3999
4018
|
// do not use padding or bleed
|
|
4000
|
-
this.
|
|
4019
|
+
this.bleed = this.padding = 0;
|
|
4001
4020
|
return this;
|
|
4002
4021
|
}
|
|
4003
4022
|
}
|
|
@@ -4063,7 +4082,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4063
4082
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
4064
4083
|
|
|
4065
4084
|
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
4066
|
-
const
|
|
4085
|
+
const bleed = tileInfo?.bleed ?? 0;
|
|
4067
4086
|
if (useWebGL && glEnable)
|
|
4068
4087
|
{
|
|
4069
4088
|
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
@@ -4078,13 +4097,13 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4078
4097
|
const w = tileInfo.size.x * sizeInverse.x;
|
|
4079
4098
|
const h = tileInfo.size.y * sizeInverse.y;
|
|
4080
4099
|
glSetTexture(textureInfo.glTexture);
|
|
4081
|
-
if (
|
|
4100
|
+
if (bleed)
|
|
4082
4101
|
{
|
|
4083
|
-
const
|
|
4084
|
-
const
|
|
4102
|
+
const bleedX = sizeInverse.x*bleed;
|
|
4103
|
+
const bleedY = sizeInverse.y*bleed;
|
|
4085
4104
|
glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
|
|
4086
|
-
x +
|
|
4087
|
-
x -
|
|
4105
|
+
x + bleedX, y + bleedY,
|
|
4106
|
+
x - bleedX + w, y - bleedY + h,
|
|
4088
4107
|
color.rgbaInt(), additiveColor && additiveColor.rgbaInt());
|
|
4089
4108
|
}
|
|
4090
4109
|
else
|
|
@@ -4112,7 +4131,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4112
4131
|
// calculate uvs and render
|
|
4113
4132
|
const x = tileInfo.pos.x, y = tileInfo.pos.y;
|
|
4114
4133
|
const w = tileInfo.size.x, h = tileInfo.size.y;
|
|
4115
|
-
drawImageColor(context, textureInfo.image, x, y, w, h, -.5, -.5, 1, 1, color, additiveColor,
|
|
4134
|
+
drawImageColor(context, textureInfo.image, x, y, w, h, -.5, -.5, 1, 1, color, additiveColor, bleed);
|
|
4116
4135
|
}
|
|
4117
4136
|
else
|
|
4118
4137
|
{
|
|
@@ -4438,7 +4457,11 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
|
|
|
4438
4457
|
ASSERT(typeof drawFunction === 'function', 'drawFunction must be a function');
|
|
4439
4458
|
|
|
4440
4459
|
if (!screenSpace)
|
|
4441
|
-
|
|
4460
|
+
{
|
|
4461
|
+
pos = worldToScreen(pos);
|
|
4462
|
+
size = size.scale(cameraScale);
|
|
4463
|
+
angle -= cameraAngle;
|
|
4464
|
+
}
|
|
4442
4465
|
context.save();
|
|
4443
4466
|
context.translate(pos.x+.5, pos.y+.5);
|
|
4444
4467
|
context.rotate(angle);
|
|
@@ -4477,26 +4500,7 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
4477
4500
|
drawTextScreen(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, angle, context);
|
|
4478
4501
|
}
|
|
4479
4502
|
|
|
4480
|
-
/** Draw text
|
|
4481
|
-
* Automatically splits new lines into rows
|
|
4482
|
-
* @param {string|number} text
|
|
4483
|
-
* @param {Vector2} pos
|
|
4484
|
-
* @param {number} [size]
|
|
4485
|
-
* @param {Color} [color=(1,1,1,1)]
|
|
4486
|
-
* @param {number} [lineWidth]
|
|
4487
|
-
* @param {Color} [lineColor=(0,0,0,1)]
|
|
4488
|
-
* @param {CanvasTextAlign} [textAlign='center']
|
|
4489
|
-
* @param {string} [font=fontDefault]
|
|
4490
|
-
* @param {string} [fontStyle]
|
|
4491
|
-
* @param {number} [maxWidth]
|
|
4492
|
-
* @param {number} [angle]
|
|
4493
|
-
* @memberof Draw */
|
|
4494
|
-
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, angle=0)
|
|
4495
|
-
{
|
|
4496
|
-
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, angle, overlayContext);
|
|
4497
|
-
}
|
|
4498
|
-
|
|
4499
|
-
/** Draw text on overlay canvas in screen space
|
|
4503
|
+
/** Draw text in screen space
|
|
4500
4504
|
* Automatically splits new lines into rows
|
|
4501
4505
|
* @param {string|number} text
|
|
4502
4506
|
* @param {Vector2} pos
|
|
@@ -4509,9 +4513,9 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
4509
4513
|
* @param {string} [fontStyle]
|
|
4510
4514
|
* @param {number} [maxWidth]
|
|
4511
4515
|
* @param {number} [angle]
|
|
4512
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=
|
|
4516
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4513
4517
|
* @memberof Draw */
|
|
4514
|
-
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=
|
|
4518
|
+
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=drawContext)
|
|
4515
4519
|
{
|
|
4516
4520
|
ASSERT(isString(text), 'text must be a string');
|
|
4517
4521
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -4555,16 +4559,16 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
4555
4559
|
* @memberof Draw */
|
|
4556
4560
|
function screenToWorld(screenPos)
|
|
4557
4561
|
{
|
|
4562
|
+
ASSERT(isVector2(screenPos), 'screenPos must be a vec2');
|
|
4563
|
+
|
|
4558
4564
|
let x = (screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale;
|
|
4559
4565
|
let y = (screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale;
|
|
4560
4566
|
if (cameraAngle)
|
|
4561
4567
|
{
|
|
4562
4568
|
// apply camera rotation
|
|
4563
4569
|
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
4564
|
-
const
|
|
4565
|
-
|
|
4566
|
-
x = rotatedX;
|
|
4567
|
-
y = rotatedY;
|
|
4570
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4571
|
+
x = xr; y = yr;
|
|
4568
4572
|
}
|
|
4569
4573
|
return new Vector2(x + cameraPos.x, y + cameraPos.y);
|
|
4570
4574
|
}
|
|
@@ -4575,16 +4579,16 @@ function screenToWorld(screenPos)
|
|
|
4575
4579
|
* @memberof Draw */
|
|
4576
4580
|
function worldToScreen(worldPos)
|
|
4577
4581
|
{
|
|
4582
|
+
ASSERT(isVector2(worldPos), 'worldPos must be a vec2');
|
|
4583
|
+
|
|
4578
4584
|
let x = worldPos.x - cameraPos.x;
|
|
4579
4585
|
let y = worldPos.y - cameraPos.y;
|
|
4580
4586
|
if (cameraAngle)
|
|
4581
4587
|
{
|
|
4582
4588
|
// apply inverse camera rotation
|
|
4583
4589
|
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
4584
|
-
const
|
|
4585
|
-
|
|
4586
|
-
x = rotatedX;
|
|
4587
|
-
y = rotatedY;
|
|
4590
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4591
|
+
x = xr; y = yr;
|
|
4588
4592
|
}
|
|
4589
4593
|
return new Vector2
|
|
4590
4594
|
(
|
|
@@ -4599,16 +4603,16 @@ function worldToScreen(worldPos)
|
|
|
4599
4603
|
* @memberof Draw */
|
|
4600
4604
|
function screenToWorldDelta(screenDelta)
|
|
4601
4605
|
{
|
|
4606
|
+
ASSERT(isVector2(screenDelta), 'screenDelta must be a vec2');
|
|
4607
|
+
|
|
4602
4608
|
let x = screenDelta.x / cameraScale;
|
|
4603
4609
|
let y = screenDelta.y / -cameraScale;
|
|
4604
4610
|
if (cameraAngle)
|
|
4605
4611
|
{
|
|
4606
4612
|
// apply camera rotation
|
|
4607
4613
|
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
4608
|
-
const
|
|
4609
|
-
|
|
4610
|
-
x = rotatedX;
|
|
4611
|
-
y = rotatedY;
|
|
4614
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4615
|
+
x = xr; y = yr;
|
|
4612
4616
|
}
|
|
4613
4617
|
return new Vector2(x, y);
|
|
4614
4618
|
}
|
|
@@ -4619,16 +4623,16 @@ function screenToWorldDelta(screenDelta)
|
|
|
4619
4623
|
* @memberof Draw */
|
|
4620
4624
|
function worldToScreenDelta(worldDelta)
|
|
4621
4625
|
{
|
|
4626
|
+
ASSERT(isVector2(worldDelta), 'worldDelta must be a vec2');
|
|
4627
|
+
|
|
4622
4628
|
let x = worldDelta.x;
|
|
4623
4629
|
let y = worldDelta.y;
|
|
4624
4630
|
if (cameraAngle)
|
|
4625
4631
|
{
|
|
4626
4632
|
// apply inverse camera rotation
|
|
4627
4633
|
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
4628
|
-
const
|
|
4629
|
-
|
|
4630
|
-
x = rotatedX;
|
|
4631
|
-
y = rotatedY;
|
|
4634
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4635
|
+
x = xr; y = yr;
|
|
4632
4636
|
}
|
|
4633
4637
|
return new Vector2(x * cameraScale, y * -cameraScale);
|
|
4634
4638
|
}
|
|
@@ -4641,6 +4645,10 @@ function worldToScreenDelta(worldDelta)
|
|
|
4641
4645
|
* @memberof Draw */
|
|
4642
4646
|
function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
|
|
4643
4647
|
{
|
|
4648
|
+
ASSERT(isVector2(screenPos), 'screenPos must be a vec2');
|
|
4649
|
+
ASSERT(isVector2(screenSize), 'screenSize must be a vec2');
|
|
4650
|
+
ASSERT(isNumber(screenAngle), 'screenAngle must be a number');
|
|
4651
|
+
|
|
4644
4652
|
return [
|
|
4645
4653
|
screenToWorld(screenPos),
|
|
4646
4654
|
screenSize.scale(1/cameraScale),
|
|
@@ -4648,28 +4656,13 @@ function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
|
|
|
4648
4656
|
];
|
|
4649
4657
|
}
|
|
4650
4658
|
|
|
4651
|
-
/** Convert world space transform to screen space
|
|
4652
|
-
* @param {Vector2} worldPos
|
|
4653
|
-
* @param {Vector2} worldSize
|
|
4654
|
-
* @param {number} [worldAngle]
|
|
4655
|
-
* @return {[Vector2, Vector2, number]} - [pos, size, angle]
|
|
4656
|
-
* @memberof Draw */
|
|
4657
|
-
function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
|
|
4658
|
-
{
|
|
4659
|
-
return [
|
|
4660
|
-
worldToScreen(worldPos),
|
|
4661
|
-
worldSize.scale(cameraScale),
|
|
4662
|
-
worldAngle - cameraAngle
|
|
4663
|
-
];
|
|
4664
|
-
}
|
|
4665
|
-
|
|
4666
4659
|
/** Get the size of the camera window in world space
|
|
4667
4660
|
* @return {Vector2}
|
|
4668
4661
|
* @memberof Draw */
|
|
4669
4662
|
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
4670
4663
|
|
|
4671
|
-
/** Check if a point or circle is on screen
|
|
4672
|
-
* If size is a Vector2, uses the
|
|
4664
|
+
/** Check if a box, point, or circle is on screen with a circle test
|
|
4665
|
+
* If size is a Vector2, uses the length as diameter
|
|
4673
4666
|
* This can be used to cull offscreen objects from render or update
|
|
4674
4667
|
* @param {Vector2} pos - world space position
|
|
4675
4668
|
* @param {Vector2|number} size - world space size or diameter
|
|
@@ -4677,12 +4670,30 @@ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
|
4677
4670
|
* @memberof Draw */
|
|
4678
4671
|
function isOnScreen(pos, size=0)
|
|
4679
4672
|
{
|
|
4680
|
-
pos
|
|
4673
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
4674
|
+
ASSERT(isVector2(size) || isNumber(size), 'size must be a vec2 or number');
|
|
4675
|
+
|
|
4676
|
+
// optimized circle on screen test
|
|
4677
|
+
// pos = worldToScreen(pos);
|
|
4678
|
+
let x = pos.x - cameraPos.x;
|
|
4679
|
+
let y = pos.y - cameraPos.y;
|
|
4680
|
+
if (cameraAngle)
|
|
4681
|
+
{
|
|
4682
|
+
// apply inverse camera rotation
|
|
4683
|
+
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
4684
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4685
|
+
x = xr; y = yr;
|
|
4686
|
+
}
|
|
4687
|
+
x *= cameraScale*2; y *= -cameraScale*2;
|
|
4688
|
+
|
|
4681
4689
|
if (size instanceof Vector2)
|
|
4682
|
-
size =
|
|
4683
|
-
size *= cameraScale
|
|
4684
|
-
|
|
4685
|
-
|
|
4690
|
+
size = size.length(); // use length of vector as diameter
|
|
4691
|
+
size *= cameraScale;
|
|
4692
|
+
|
|
4693
|
+
// check against screen bounds
|
|
4694
|
+
const w = mainCanvasSize.x, h = mainCanvasSize.y;
|
|
4695
|
+
return x + size > -w && x - size < w &&
|
|
4696
|
+
y + size > -h && y - size < h;
|
|
4686
4697
|
}
|
|
4687
4698
|
|
|
4688
4699
|
/** Enable normal or additive blend mode
|
|
@@ -4696,18 +4707,19 @@ function setBlendMode(additive=false, context)
|
|
|
4696
4707
|
context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
4697
4708
|
}
|
|
4698
4709
|
|
|
4699
|
-
/** Combines
|
|
4700
|
-
* This is necessary for things like
|
|
4710
|
+
/** Combines LittleJS canvases onto the main canvas
|
|
4711
|
+
* This is necessary for things like screenshots and video
|
|
4701
4712
|
* @memberof Draw */
|
|
4702
4713
|
function combineCanvases()
|
|
4703
4714
|
{
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4715
|
+
const w = mainCanvasSize.x, h = mainCanvasSize.y;
|
|
4716
|
+
workCanvas.width = w;
|
|
4717
|
+
workCanvas.height = h;
|
|
4718
|
+
workContext.fillRect(0,0,w,h); // remove background alpha
|
|
4719
|
+
glCopyToContext(workContext);
|
|
4720
|
+
workContext.drawImage(mainCanvas, 0, 0);
|
|
4721
|
+
mainCanvas.width |= 0;
|
|
4722
|
+
mainContext.drawImage(workCanvas, 0, 0);
|
|
4711
4723
|
}
|
|
4712
4724
|
|
|
4713
4725
|
/** Helper function to draw an image with color and additive color applied
|
|
@@ -4724,18 +4736,18 @@ function combineCanvases()
|
|
|
4724
4736
|
* @param {number} dHeight
|
|
4725
4737
|
* @param {Color} color
|
|
4726
4738
|
* @param {Color} [additiveColor]
|
|
4727
|
-
* @param {number} [
|
|
4739
|
+
* @param {number} [bleed] - How many pixels to shrink the source, used to fix bleeding
|
|
4728
4740
|
* @memberof Draw */
|
|
4729
|
-
function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, color, additiveColor,
|
|
4741
|
+
function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, color, additiveColor, bleed=0)
|
|
4730
4742
|
{
|
|
4731
4743
|
function isWhite(c) { return c.r >= 1 && c.g >= 1 && c.b >= 1; }
|
|
4732
4744
|
function isBlack(c) { return c.r <= 0 && c.g <= 0 && c.b <= 0 && c.a <= 0; }
|
|
4733
|
-
const sx2 =
|
|
4734
|
-
const sy2 =
|
|
4745
|
+
const sx2 = bleed;
|
|
4746
|
+
const sy2 = bleed;
|
|
4735
4747
|
sWidth = max(1,sWidth|0);
|
|
4736
4748
|
sHeight = max(1,sHeight|0);
|
|
4737
|
-
const sWidth2 = sWidth - 2*
|
|
4738
|
-
const sHeight2 = sHeight - 2*
|
|
4749
|
+
const sWidth2 = sWidth - 2*bleed;
|
|
4750
|
+
const sHeight2 = sHeight - 2*bleed;
|
|
4739
4751
|
if (!canvasColorTiles || (additiveColor ? isWhite(color.add(additiveColor)) && additiveColor.a <= 0 : isWhite(color)))
|
|
4740
4752
|
{
|
|
4741
4753
|
// white texture with no additive alpha, no need to tint
|
|
@@ -4746,12 +4758,12 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4746
4758
|
else
|
|
4747
4759
|
{
|
|
4748
4760
|
// copy to offscreen canvas
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4761
|
+
workReadCanvas.width = sWidth;
|
|
4762
|
+
workReadCanvas.height = sHeight;
|
|
4763
|
+
workReadContext.drawImage(image, sx|0, sy|0, sWidth, sHeight, 0, 0, sWidth, sHeight);
|
|
4752
4764
|
|
|
4753
4765
|
// tint image using offscreen work context
|
|
4754
|
-
const imageData =
|
|
4766
|
+
const imageData = workReadContext.getImageData(0, 0, sWidth, sHeight);
|
|
4755
4767
|
const data = imageData.data;
|
|
4756
4768
|
if (additiveColor && !isBlack(additiveColor))
|
|
4757
4769
|
{
|
|
@@ -4760,8 +4772,8 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4760
4772
|
const colorAdd = [additiveColor.r * 255, additiveColor.g * 255, additiveColor.b * 255, additiveColor.a * 255];
|
|
4761
4773
|
for (let i = 0; i < data.length; ++i)
|
|
4762
4774
|
data[i] = data[i] * colorMultiply[i&3] + colorAdd[i&3] |0;
|
|
4763
|
-
|
|
4764
|
-
context.drawImage(
|
|
4775
|
+
workReadContext.putImageData(imageData, 0, 0);
|
|
4776
|
+
context.drawImage(workReadCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
4765
4777
|
}
|
|
4766
4778
|
else
|
|
4767
4779
|
{
|
|
@@ -4772,9 +4784,9 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4772
4784
|
data[i+1] *= color.g;
|
|
4773
4785
|
data[i+2] *= color.b;
|
|
4774
4786
|
}
|
|
4775
|
-
|
|
4787
|
+
workReadContext.putImageData(imageData, 0, 0);
|
|
4776
4788
|
context.globalAlpha = color.a;
|
|
4777
|
-
context.drawImage(
|
|
4789
|
+
context.drawImage(workReadCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
4778
4790
|
context.globalAlpha = 1;
|
|
4779
4791
|
}
|
|
4780
4792
|
}
|
|
@@ -4836,7 +4848,7 @@ class FontImage
|
|
|
4836
4848
|
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1))
|
|
4837
4849
|
{
|
|
4838
4850
|
// load default font image
|
|
4839
|
-
if (!engineFontImage)
|
|
4851
|
+
if (!image && !engineFontImage)
|
|
4840
4852
|
{
|
|
4841
4853
|
engineFontImage = new Image;
|
|
4842
4854
|
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
@@ -4859,23 +4871,14 @@ class FontImage
|
|
|
4859
4871
|
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center, context);
|
|
4860
4872
|
}
|
|
4861
4873
|
|
|
4862
|
-
/** Draw text
|
|
4863
|
-
* @param {string|number} text
|
|
4864
|
-
* @param {Vector2} pos
|
|
4865
|
-
* @param {number} [scale]
|
|
4866
|
-
* @param {boolean} [center]
|
|
4867
|
-
*/
|
|
4868
|
-
drawTextOverlay(text, pos, scale=4, center)
|
|
4869
|
-
{ this.drawText(text, pos, scale, center, overlayContext); }
|
|
4870
|
-
|
|
4871
|
-
/** Draw text on overlay canvas in screen space using the image font
|
|
4874
|
+
/** Draw text in screen space using the image font
|
|
4872
4875
|
* @param {string|number} text
|
|
4873
4876
|
* @param {Vector2} pos
|
|
4874
4877
|
* @param {number} [scale]
|
|
4875
4878
|
* @param {boolean} [center]
|
|
4876
4879
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4877
4880
|
*/
|
|
4878
|
-
drawTextScreen(text, pos, scale=4, center=true, context=
|
|
4881
|
+
drawTextScreen(text, pos, scale=4, center=true, context=drawContext)
|
|
4879
4882
|
{
|
|
4880
4883
|
context.save();
|
|
4881
4884
|
const size = this.tileSize;
|
|
@@ -5268,8 +5271,7 @@ function inputInit()
|
|
|
5268
5271
|
}
|
|
5269
5272
|
function onMouseDown(e)
|
|
5270
5273
|
{
|
|
5271
|
-
if (isTouchDevice && touchInputEnable)
|
|
5272
|
-
return;
|
|
5274
|
+
if (isTouchDevice && touchInputEnable) return;
|
|
5273
5275
|
|
|
5274
5276
|
// fix stalled audio requiring user interaction
|
|
5275
5277
|
if (soundEnable && !headlessMode && audioContext && !audioIsRunning())
|
|
@@ -5320,8 +5322,7 @@ function inputInit()
|
|
|
5320
5322
|
let wasTouching;
|
|
5321
5323
|
function handleTouch(e)
|
|
5322
5324
|
{
|
|
5323
|
-
if (!touchInputEnable)
|
|
5324
|
-
return;
|
|
5325
|
+
if (!touchInputEnable) return;
|
|
5325
5326
|
|
|
5326
5327
|
// route touch to gamepad
|
|
5327
5328
|
if (touchGamepadEnable)
|
|
@@ -5385,8 +5386,7 @@ function inputInit()
|
|
|
5385
5386
|
}
|
|
5386
5387
|
|
|
5387
5388
|
// don't process touch gamepad if paused
|
|
5388
|
-
if (paused)
|
|
5389
|
-
return;
|
|
5389
|
+
if (paused) return;
|
|
5390
5390
|
|
|
5391
5391
|
// get center of left and right sides
|
|
5392
5392
|
const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
@@ -5625,7 +5625,7 @@ function inputRender()
|
|
|
5625
5625
|
return;
|
|
5626
5626
|
|
|
5627
5627
|
// setup the canvas
|
|
5628
|
-
const context =
|
|
5628
|
+
const context = mainContext;
|
|
5629
5629
|
context.save();
|
|
5630
5630
|
context.globalAlpha = alpha*touchGamepadAlpha;
|
|
5631
5631
|
context.strokeStyle = '#fff';
|
|
@@ -5867,12 +5867,12 @@ class Sound
|
|
|
5867
5867
|
///////////////////////////////////////////////////////////////////////////////
|
|
5868
5868
|
|
|
5869
5869
|
/**
|
|
5870
|
-
* Sound Wave Object -
|
|
5871
|
-
* - this can be used to play wave, mp3, and ogg files
|
|
5870
|
+
* Sound Wave Object - Loads and stores an audio file for later use
|
|
5871
|
+
* - this can be used to load and play wave, mp3, and ogg files
|
|
5872
5872
|
* @extends Sound
|
|
5873
5873
|
* @memberof Audio
|
|
5874
5874
|
* @example
|
|
5875
|
-
* //
|
|
5875
|
+
* // load an audio asset file
|
|
5876
5876
|
* const sound_example = new SoundWave('sound.mp3');
|
|
5877
5877
|
*
|
|
5878
5878
|
* // play the sound
|
|
@@ -6072,8 +6072,7 @@ class SoundInstance
|
|
|
6072
6072
|
/** Pause this sound instance */
|
|
6073
6073
|
pause()
|
|
6074
6074
|
{
|
|
6075
|
-
if (this.isPaused())
|
|
6076
|
-
return;
|
|
6075
|
+
if (this.isPaused()) return;
|
|
6077
6076
|
|
|
6078
6077
|
// save current time and stop sound
|
|
6079
6078
|
this.pausedTime = this.getCurrentTime();
|
|
@@ -6085,8 +6084,7 @@ class SoundInstance
|
|
|
6085
6084
|
/** Unpauses this sound instance */
|
|
6086
6085
|
resume()
|
|
6087
6086
|
{
|
|
6088
|
-
if (!this.isPaused())
|
|
6089
|
-
return;
|
|
6087
|
+
if (!this.isPaused()) return;
|
|
6090
6088
|
|
|
6091
6089
|
// restart sound from paused time
|
|
6092
6090
|
this.start(this.pausedTime);
|
|
@@ -6824,26 +6822,26 @@ class TileLayer extends CanvasLayer
|
|
|
6824
6822
|
if (!this.context) return;
|
|
6825
6823
|
|
|
6826
6824
|
// save current render settings
|
|
6827
|
-
/** @type {[
|
|
6828
|
-
this.savedRenderSettings = [
|
|
6825
|
+
/** @type {[CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number, Color]} */
|
|
6826
|
+
this.savedRenderSettings = [drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor];
|
|
6829
6827
|
|
|
6830
6828
|
// set the draw canvas and context to this layer
|
|
6831
6829
|
// use camera settings to match this layer's canvas
|
|
6832
|
-
drawCanvas = this.canvas;
|
|
6833
6830
|
drawContext = this.context;
|
|
6834
6831
|
cameraPos = this.size.scale(.5);
|
|
6835
6832
|
const tileSize = this.tileInfo ? this.tileInfo.size : vec2(1);
|
|
6836
6833
|
cameraScale = tileSize.x;
|
|
6834
|
+
canvasClearColor = CLEAR_BLACK;
|
|
6837
6835
|
mainCanvasSize = this.size.multiply(tileSize);
|
|
6838
6836
|
if (clear)
|
|
6839
6837
|
{
|
|
6840
6838
|
// clear and set size
|
|
6841
|
-
|
|
6842
|
-
|
|
6839
|
+
this.canvas.width = mainCanvasSize.x;
|
|
6840
|
+
this.canvas.height = mainCanvasSize.y;
|
|
6843
6841
|
}
|
|
6844
6842
|
|
|
6845
6843
|
// disable smoothing for pixel art
|
|
6846
|
-
|
|
6844
|
+
drawContext.imageSmoothingEnabled = !tilesPixelated;
|
|
6847
6845
|
|
|
6848
6846
|
// setup gl rendering if enabled
|
|
6849
6847
|
glPreRender();
|
|
@@ -6856,10 +6854,10 @@ class TileLayer extends CanvasLayer
|
|
|
6856
6854
|
|
|
6857
6855
|
ASSERT(drawContext === this.context, 'must call redrawStart() before drawing tiles');
|
|
6858
6856
|
glCopyToContext(drawContext);
|
|
6859
|
-
//
|
|
6857
|
+
//saveCanvas(this.canvas);
|
|
6860
6858
|
|
|
6861
6859
|
// set stuff back to normal
|
|
6862
|
-
[
|
|
6860
|
+
[drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor] = this.savedRenderSettings;
|
|
6863
6861
|
}
|
|
6864
6862
|
|
|
6865
6863
|
/** Draw the tile at a given position in the tile grid
|
|
@@ -6927,8 +6925,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6927
6925
|
/** Destroy this tile layer */
|
|
6928
6926
|
destroy()
|
|
6929
6927
|
{
|
|
6930
|
-
if (this.destroyed)
|
|
6931
|
-
return;
|
|
6928
|
+
if (this.destroyed) return;
|
|
6932
6929
|
|
|
6933
6930
|
// remove from collision layers array and destroy
|
|
6934
6931
|
const index = tileCollisionLayers.indexOf(this);
|
|
@@ -7465,10 +7462,11 @@ function medalsInit(saveName)
|
|
|
7465
7462
|
|
|
7466
7463
|
// engine automatically renders medals
|
|
7467
7464
|
engineAddPlugin(undefined, medalsRender);
|
|
7465
|
+
|
|
7466
|
+
// plugin functions
|
|
7468
7467
|
function medalsRender()
|
|
7469
7468
|
{
|
|
7470
|
-
if (!medalsDisplayQueue.length)
|
|
7471
|
-
return;
|
|
7469
|
+
if (!medalsDisplayQueue.length) return;
|
|
7472
7470
|
|
|
7473
7471
|
// update first medal in queue
|
|
7474
7472
|
const medal = medalsDisplayQueue[0];
|
|
@@ -7558,8 +7556,7 @@ class Medal
|
|
|
7558
7556
|
/** Unlocks a medal if not already unlocked */
|
|
7559
7557
|
unlock()
|
|
7560
7558
|
{
|
|
7561
|
-
if (medalsPreventUnlock || this.unlocked)
|
|
7562
|
-
return;
|
|
7559
|
+
if (medalsPreventUnlock || this.unlocked) return;
|
|
7563
7560
|
|
|
7564
7561
|
// save the medal
|
|
7565
7562
|
ASSERT(medalsSaveName, 'save name must be set');
|
|
@@ -7572,10 +7569,10 @@ class Medal
|
|
|
7572
7569
|
*/
|
|
7573
7570
|
render(hidePercent=0)
|
|
7574
7571
|
{
|
|
7575
|
-
const context =
|
|
7572
|
+
const context = mainContext;
|
|
7576
7573
|
const width = min(medalDisplaySize.x, mainCanvas.width);
|
|
7577
7574
|
const height = medalDisplaySize.y;
|
|
7578
|
-
const x =
|
|
7575
|
+
const x = mainCanvas.width - width;
|
|
7579
7576
|
const y = -height*hidePercent;
|
|
7580
7577
|
const backgroundColor = hsl(0,0,.9);
|
|
7581
7578
|
|
|
@@ -7616,7 +7613,7 @@ class Medal
|
|
|
7616
7613
|
{
|
|
7617
7614
|
// draw the image or icon
|
|
7618
7615
|
if (this.image)
|
|
7619
|
-
|
|
7616
|
+
mainContext.drawImage(this.image, pos.x-size/2, pos.y-size/2, size, size);
|
|
7620
7617
|
else
|
|
7621
7618
|
drawTextScreen(this.icon, pos, size*.7, BLACK);
|
|
7622
7619
|
}
|
|
@@ -7637,7 +7634,7 @@ class Medal
|
|
|
7637
7634
|
* @namespace WebGL
|
|
7638
7635
|
*/
|
|
7639
7636
|
|
|
7640
|
-
/** The WebGL canvas which appears
|
|
7637
|
+
/** The WebGL canvas which appears below the main canvas
|
|
7641
7638
|
* @type {HTMLCanvasElement}
|
|
7642
7639
|
* @memberof WebGL */
|
|
7643
7640
|
let glCanvas;
|
|
@@ -7667,7 +7664,7 @@ const gl_MAX_POLY_VERTEXES = gl_ARRAY_BUFFER_SIZE / gl_POLY_VERTEX_BYTE_STRIDE |
|
|
|
7667
7664
|
///////////////////////////////////////////////////////////////////////////////
|
|
7668
7665
|
|
|
7669
7666
|
// Initialize WebGL, called automatically by the engine
|
|
7670
|
-
function glInit()
|
|
7667
|
+
function glInit(rootElement)
|
|
7671
7668
|
{
|
|
7672
7669
|
// keep set of texture infos so they can be restored if context is lost
|
|
7673
7670
|
glTextureInfos = new Set;
|
|
@@ -7691,8 +7688,7 @@ function glInit()
|
|
|
7691
7688
|
return;
|
|
7692
7689
|
}
|
|
7693
7690
|
|
|
7694
|
-
// attach the WebGL canvas
|
|
7695
|
-
const rootElement = mainCanvas.parentElement;
|
|
7691
|
+
// attach the WebGL canvas;
|
|
7696
7692
|
rootElement.appendChild(glCanvas);
|
|
7697
7693
|
|
|
7698
7694
|
// startup webgl
|
|
@@ -7793,8 +7789,7 @@ function glInit()
|
|
|
7793
7789
|
|
|
7794
7790
|
function glSetInstancedMode()
|
|
7795
7791
|
{
|
|
7796
|
-
if (!glPolyMode)
|
|
7797
|
-
return;
|
|
7792
|
+
if (!glPolyMode) return;
|
|
7798
7793
|
|
|
7799
7794
|
// setup instanced mode
|
|
7800
7795
|
glFlush();
|
|
@@ -7827,8 +7822,7 @@ function glSetInstancedMode()
|
|
|
7827
7822
|
|
|
7828
7823
|
function glSetPolyMode()
|
|
7829
7824
|
{
|
|
7830
|
-
if (glPolyMode)
|
|
7831
|
-
return;
|
|
7825
|
+
if (glPolyMode) return;
|
|
7832
7826
|
|
|
7833
7827
|
// setup poly mode
|
|
7834
7828
|
glFlush();
|
|
@@ -7907,9 +7901,12 @@ function glClearCanvas()
|
|
|
7907
7901
|
if (!glContext) return;
|
|
7908
7902
|
|
|
7909
7903
|
// clear and set to same size as main canvas
|
|
7910
|
-
glCanvas.width =
|
|
7911
|
-
glCanvas.height =
|
|
7904
|
+
glCanvas.width = mainCanvasSize.x;
|
|
7905
|
+
glCanvas.height = mainCanvasSize.y;
|
|
7912
7906
|
glContext.viewport(0, 0, glCanvas.width, glCanvas.height);
|
|
7907
|
+
const color = canvasClearColor;
|
|
7908
|
+
if (color.a > 0)
|
|
7909
|
+
glContext.clearColor(color.r, color.g, color.b, color.a);
|
|
7913
7910
|
glContext.clear(glContext.COLOR_BUFFER_BIT);
|
|
7914
7911
|
}
|
|
7915
7912
|
|
|
@@ -7921,8 +7918,7 @@ function glClearCanvas()
|
|
|
7921
7918
|
function glSetTexture(texture, wrap=false)
|
|
7922
7919
|
{
|
|
7923
7920
|
// must flush cache with the old texture to set a new one
|
|
7924
|
-
if (!glContext || texture === glActiveTexture)
|
|
7925
|
-
return;
|
|
7921
|
+
if (!glContext || texture === glActiveTexture) return;
|
|
7926
7922
|
|
|
7927
7923
|
glFlush();
|
|
7928
7924
|
glActiveTexture = texture;
|
|
@@ -8018,6 +8014,7 @@ function glCreateTexture(image)
|
|
|
8018
8014
|
function glDeleteTexture(texture)
|
|
8019
8015
|
{
|
|
8020
8016
|
if (!glContext) return;
|
|
8017
|
+
|
|
8021
8018
|
glContext.deleteTexture(texture);
|
|
8022
8019
|
}
|
|
8023
8020
|
|
|
@@ -8102,8 +8099,7 @@ function glFlush()
|
|
|
8102
8099
|
* @memberof WebGL */
|
|
8103
8100
|
function glCopyToContext(context)
|
|
8104
8101
|
{
|
|
8105
|
-
if (!glEnable || !glContext)
|
|
8106
|
-
return;
|
|
8102
|
+
if (!glEnable || !glContext) return;
|
|
8107
8103
|
|
|
8108
8104
|
glFlush();
|
|
8109
8105
|
context.drawImage(glCanvas, 0, 0);
|
|
@@ -8139,7 +8135,6 @@ function glDraw(x, y, sizeX, sizeY, angle=0, uv0X=0, uv0Y=0, uv1X=1, uv1Y=1, rgb
|
|
|
8139
8135
|
glFlush();
|
|
8140
8136
|
glSetInstancedMode();
|
|
8141
8137
|
|
|
8142
|
-
glPolyMode = false;
|
|
8143
8138
|
let offset = glBatchCount++ * gl_INDICES_PER_INSTANCE;
|
|
8144
8139
|
glPositionData[offset++] = x;
|
|
8145
8140
|
glPositionData[offset++] = y;
|
|
@@ -8660,13 +8655,12 @@ class PostProcessPlugin
|
|
|
8660
8655
|
{
|
|
8661
8656
|
/** Create global post processing shader
|
|
8662
8657
|
* @param {string} shaderCode
|
|
8663
|
-
* @param {boolean} [includeOverlay]
|
|
8664
8658
|
* @param {boolean} [includeMainCanvas]
|
|
8665
8659
|
* @example
|
|
8666
8660
|
* // create the post process plugin object
|
|
8667
8661
|
* new PostProcessPlugin(shaderCode);
|
|
8668
8662
|
*/
|
|
8669
|
-
constructor(shaderCode,
|
|
8663
|
+
constructor(shaderCode, includeMainCanvas=true)
|
|
8670
8664
|
{
|
|
8671
8665
|
ASSERT(!postProcess, 'Post process already initialized');
|
|
8672
8666
|
postProcess = this;
|
|
@@ -8738,17 +8732,19 @@ class PostProcessPlugin
|
|
|
8738
8732
|
// clear out the buffer
|
|
8739
8733
|
glFlush();
|
|
8740
8734
|
|
|
8741
|
-
|
|
8735
|
+
// setup texture
|
|
8736
|
+
glContext.activeTexture(glContext.TEXTURE0);
|
|
8737
|
+
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
8738
|
+
if (includeMainCanvas)
|
|
8742
8739
|
{
|
|
8743
|
-
// copy
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
}
|
|
8740
|
+
// copy main canvas to work canvas
|
|
8741
|
+
workCanvas.width = mainCanvasSize.x;
|
|
8742
|
+
workCanvas.height = mainCanvasSize.y;
|
|
8743
|
+
glCopyToContext(workContext);
|
|
8744
|
+
workContext.drawImage(mainCanvas, 0, 0);
|
|
8745
|
+
|
|
8746
|
+
// copy work canvas to texture
|
|
8747
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, workCanvas);
|
|
8752
8748
|
}
|
|
8753
8749
|
|
|
8754
8750
|
// setup shader program to draw a quad
|
|
@@ -8757,14 +8753,6 @@ class PostProcessPlugin
|
|
|
8757
8753
|
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, 1);
|
|
8758
8754
|
glContext.disable(glContext.BLEND);
|
|
8759
8755
|
|
|
8760
|
-
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
8761
|
-
glContext.activeTexture(glContext.TEXTURE0);
|
|
8762
|
-
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
8763
|
-
if (includeMainCanvas || includeOverlay)
|
|
8764
|
-
{
|
|
8765
|
-
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, mainCanvas);
|
|
8766
|
-
}
|
|
8767
|
-
|
|
8768
8756
|
// set vertex position attribute
|
|
8769
8757
|
const vertexByteStride = 8;
|
|
8770
8758
|
const pLocation = glContext.getAttribLocation(postProcess.shader, 'p');
|
|
@@ -8993,12 +8981,14 @@ class UISystemPlugin
|
|
|
8993
8981
|
* // create the ui plugin object
|
|
8994
8982
|
* new UISystemPlugin;
|
|
8995
8983
|
*/
|
|
8996
|
-
constructor(context=
|
|
8984
|
+
constructor(context=mainContext)
|
|
8997
8985
|
{
|
|
8998
8986
|
ASSERT(!uiSystem, 'UI system already initialized');
|
|
8999
8987
|
uiSystem = this;
|
|
9000
8988
|
|
|
9001
8989
|
// default settings
|
|
8990
|
+
/** @property {boolean} - Activate when mouse is pressed down instead of clicked */
|
|
8991
|
+
this.activateOnPress = false;
|
|
9002
8992
|
/** @property {Color} - Default fill color for UI elements */
|
|
9003
8993
|
this.defaultColor = WHITE;
|
|
9004
8994
|
/** @property {Color} - Default outline color for UI elements */
|
|
@@ -9650,13 +9640,15 @@ class UIObject
|
|
|
9650
9640
|
uiSystem.uiObjects.push(this);
|
|
9651
9641
|
}
|
|
9652
9642
|
|
|
9653
|
-
/** Add a child UIObject to this object
|
|
9654
|
-
* @param {UIObject} child
|
|
9643
|
+
/** Add a child UIObject to this object, returns child for chaining
|
|
9644
|
+
* @param {UIObject} child
|
|
9645
|
+
* @return {UIObject} The child object added */
|
|
9655
9646
|
addChild(child)
|
|
9656
9647
|
{
|
|
9657
9648
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
9658
9649
|
this.children.push(child);
|
|
9659
9650
|
child.parent = this;
|
|
9651
|
+
return child;
|
|
9660
9652
|
}
|
|
9661
9653
|
|
|
9662
9654
|
/** Remove a child UIObject from this object
|
|
@@ -9703,7 +9695,7 @@ class UIObject
|
|
|
9703
9695
|
this.onUpdate();
|
|
9704
9696
|
|
|
9705
9697
|
// unset active if disabled
|
|
9706
|
-
if (this.disabled && this
|
|
9698
|
+
if (this.disabled && this === uiSystem.activeObject)
|
|
9707
9699
|
uiSystem.activeObject = undefined;
|
|
9708
9700
|
|
|
9709
9701
|
const wasHover = uiSystem.lastHoverObject === this;
|
|
@@ -9729,8 +9721,16 @@ class UIObject
|
|
|
9729
9721
|
if (uiSystem.activeObject && !isActive)
|
|
9730
9722
|
uiSystem.activeObject.onRelease();
|
|
9731
9723
|
uiSystem.activeObject = this;
|
|
9724
|
+
|
|
9725
|
+
if (uiSystem.activateOnPress)
|
|
9726
|
+
{
|
|
9727
|
+
this.onClick();
|
|
9728
|
+
if (!this.soundPress && this.soundClick)
|
|
9729
|
+
this.soundClick.play();
|
|
9730
|
+
}
|
|
9732
9731
|
}
|
|
9733
9732
|
}
|
|
9733
|
+
if (!uiSystem.activateOnPress)
|
|
9734
9734
|
if (!mouseDown && this.isActiveObject() && this.interactive)
|
|
9735
9735
|
{
|
|
9736
9736
|
this.onClick();
|
|
@@ -12170,33 +12170,33 @@ async function box2dInit()
|
|
|
12170
12170
|
color = getDebugColor(color);
|
|
12171
12171
|
point1 = box2d.vec2FromPointer(point1);
|
|
12172
12172
|
point2 = box2d.vec2FromPointer(point2);
|
|
12173
|
-
drawLine(point1, point2, debugLineWidth, color, vec2(), 0, false
|
|
12173
|
+
drawLine(point1, point2, debugLineWidth, color, vec2(), 0, false);
|
|
12174
12174
|
};
|
|
12175
12175
|
debugDraw.DrawPolygon = function(vertices, vertexCount, color)
|
|
12176
12176
|
{
|
|
12177
12177
|
color = getDebugColor(color);
|
|
12178
12178
|
const points = getPointsList(vertices, vertexCount);
|
|
12179
|
-
drawPoly(points, CLEAR_WHITE, debugLineWidth, color, vec2(), 0, false
|
|
12179
|
+
drawPoly(points, CLEAR_WHITE, debugLineWidth, color, vec2(), 0, false);
|
|
12180
12180
|
};
|
|
12181
12181
|
debugDraw.DrawSolidPolygon = function(vertices, vertexCount, color)
|
|
12182
12182
|
{
|
|
12183
12183
|
color = getDebugColor(color);
|
|
12184
12184
|
const points = getPointsList(vertices, vertexCount);
|
|
12185
|
-
drawPoly(points, color, 0, color, vec2(), 0, false
|
|
12185
|
+
drawPoly(points, color, 0, color, vec2(), 0, false);
|
|
12186
12186
|
};
|
|
12187
12187
|
debugDraw.DrawCircle = function(center, radius, color)
|
|
12188
12188
|
{
|
|
12189
12189
|
color = getDebugColor(color);
|
|
12190
12190
|
center = box2d.vec2FromPointer(center);
|
|
12191
|
-
drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false
|
|
12191
|
+
drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false);
|
|
12192
12192
|
};
|
|
12193
12193
|
debugDraw.DrawSolidCircle = function(center, radius, axis, color)
|
|
12194
12194
|
{
|
|
12195
12195
|
color = getDebugColor(color);
|
|
12196
12196
|
center = box2d.vec2FromPointer(center);
|
|
12197
12197
|
axis = box2d.vec2FromPointer(axis).scale(radius);
|
|
12198
|
-
drawCircle(center, radius*2, color, debugLineWidth, color, false
|
|
12199
|
-
drawLine(vec2(), axis, debugLineWidth, color, center, 0, false
|
|
12198
|
+
drawCircle(center, radius*2, color, debugLineWidth, color, false);
|
|
12199
|
+
drawLine(vec2(), axis, debugLineWidth, color, center, 0, false);
|
|
12200
12200
|
};
|
|
12201
12201
|
debugDraw.DrawTransform = function(transform)
|
|
12202
12202
|
{
|
|
@@ -12205,8 +12205,8 @@ async function box2dInit()
|
|
|
12205
12205
|
const angle = -transform.get_q().GetAngle();
|
|
12206
12206
|
const p1 = vec2(1,0), c1 = rgb(.75,0,0,.8);
|
|
12207
12207
|
const p2 = vec2(0,1), c2 = rgb(0,.75,0,.8);
|
|
12208
|
-
drawLine(vec2(), p1, debugLineWidth, c1, pos, angle, false
|
|
12209
|
-
drawLine(vec2(), p2, debugLineWidth, c2, pos, angle, false
|
|
12208
|
+
drawLine(vec2(), p1, debugLineWidth, c1, pos, angle, false);
|
|
12209
|
+
drawLine(vec2(), p2, debugLineWidth, c2, pos, angle, false);
|
|
12210
12210
|
}
|
|
12211
12211
|
|
|
12212
12212
|
debugDraw.AppendFlags(box2d.instance.b2Draw.e_shapeBit);
|
|
@@ -12226,8 +12226,8 @@ async function box2dInit()
|
|
|
12226
12226
|
|
|
12227
12227
|
///////////////////////////////////////////////////////////////////////////////
|
|
12228
12228
|
|
|
12229
|
-
/** Draw a scalable nine-slice UI element to the
|
|
12230
|
-
* This function can not apply color because it draws using the
|
|
12229
|
+
/** Draw a scalable nine-slice UI element to the main canvas in screen space
|
|
12230
|
+
* This function can not apply color because it draws using the 2d context
|
|
12231
12231
|
* @param {Vector2} pos - Screen space position
|
|
12232
12232
|
* @param {Vector2} size - Screen space size
|
|
12233
12233
|
* @param {TileInfo} startTile - Starting tile for the nine-slice pattern
|
|
@@ -12237,7 +12237,7 @@ async function box2dInit()
|
|
|
12237
12237
|
* @memberof DrawUtilities */
|
|
12238
12238
|
function drawNineSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
12239
12239
|
{
|
|
12240
|
-
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true
|
|
12240
|
+
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true);
|
|
12241
12241
|
}
|
|
12242
12242
|
|
|
12243
12243
|
/** Draw a scalable nine-slice UI element in world space
|
|
@@ -12286,8 +12286,8 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
12286
12286
|
}
|
|
12287
12287
|
}
|
|
12288
12288
|
|
|
12289
|
-
/** Draw a scalable three-slice UI element to the
|
|
12290
|
-
* This function can not apply color because it draws using the
|
|
12289
|
+
/** Draw a scalable three-slice UI element to the main canvas in screen space
|
|
12290
|
+
* This function can not apply color because it draws using the 2d context
|
|
12291
12291
|
* @param {Vector2} pos - Screen space position
|
|
12292
12292
|
* @param {Vector2} size - Screen space size
|
|
12293
12293
|
* @param {TileInfo} startTile - Starting tile for the three-slice pattern
|
|
@@ -12297,7 +12297,7 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
12297
12297
|
* @memberof DrawUtilities */
|
|
12298
12298
|
function drawThreeSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
12299
12299
|
{
|
|
12300
|
-
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true
|
|
12300
|
+
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true);
|
|
12301
12301
|
}
|
|
12302
12302
|
|
|
12303
12303
|
/** Draw a scalable three-slice UI element in world space
|
|
@@ -12377,7 +12377,7 @@ export
|
|
|
12377
12377
|
// Globals
|
|
12378
12378
|
debug,
|
|
12379
12379
|
debugOverlay,
|
|
12380
|
-
|
|
12380
|
+
debugWatermark,
|
|
12381
12381
|
|
|
12382
12382
|
// Debug
|
|
12383
12383
|
ASSERT,
|
|
@@ -12391,13 +12391,10 @@ export
|
|
|
12391
12391
|
debugText,
|
|
12392
12392
|
debugClear,
|
|
12393
12393
|
debugScreenshot,
|
|
12394
|
-
debugSaveCanvas,
|
|
12395
|
-
debugSaveText,
|
|
12396
|
-
debugSaveDataURL,
|
|
12397
12394
|
debugShowErrors,
|
|
12398
|
-
debugVideoCaptureIsActive,
|
|
12399
12395
|
debugVideoCaptureStart,
|
|
12400
12396
|
debugVideoCaptureStop,
|
|
12397
|
+
debugVideoCaptureIsActive,
|
|
12401
12398
|
|
|
12402
12399
|
// Settings
|
|
12403
12400
|
cameraPos,
|
|
@@ -12410,13 +12407,13 @@ export
|
|
|
12410
12407
|
canvasMaxAspect,
|
|
12411
12408
|
canvasFixedSize,
|
|
12412
12409
|
canvasPixelated,
|
|
12413
|
-
overlayCanvasPixelated,
|
|
12414
12410
|
tilesPixelated,
|
|
12415
12411
|
fontDefault,
|
|
12416
12412
|
showSplashScreen,
|
|
12417
12413
|
headlessMode,
|
|
12418
|
-
|
|
12419
|
-
|
|
12414
|
+
tileDefaultSize,
|
|
12415
|
+
tileDefaultPadding,
|
|
12416
|
+
tileDefaultBleed,
|
|
12420
12417
|
enablePhysicsSolver,
|
|
12421
12418
|
objectDefaultMass,
|
|
12422
12419
|
objectDefaultDamping,
|
|
@@ -12455,14 +12452,14 @@ export
|
|
|
12455
12452
|
setCanvasMaxAspect,
|
|
12456
12453
|
setCanvasFixedSize,
|
|
12457
12454
|
setCanvasPixelated,
|
|
12458
|
-
setOverlayCanvasPixelated,
|
|
12459
12455
|
setTilesPixelated,
|
|
12460
12456
|
setFontDefault,
|
|
12461
12457
|
setShowSplashScreen,
|
|
12462
12458
|
setHeadlessMode,
|
|
12463
12459
|
setGLEnable,
|
|
12464
|
-
|
|
12465
|
-
|
|
12460
|
+
setTileDefaultSize,
|
|
12461
|
+
setTileDefaultPadding,
|
|
12462
|
+
setTileDefaultBleed,
|
|
12466
12463
|
setEnablePhysicsSolver,
|
|
12467
12464
|
setObjectDefaultMass,
|
|
12468
12465
|
setObjectDefaultDamping,
|
|
@@ -12491,10 +12488,10 @@ export
|
|
|
12491
12488
|
setMedalDisplaySlideTime,
|
|
12492
12489
|
setMedalDisplaySize,
|
|
12493
12490
|
setMedalsPreventUnlock,
|
|
12494
|
-
|
|
12491
|
+
setDebugWatermark,
|
|
12495
12492
|
setDebugKey,
|
|
12496
12493
|
|
|
12497
|
-
//
|
|
12494
|
+
// Math
|
|
12498
12495
|
PI,
|
|
12499
12496
|
abs,
|
|
12500
12497
|
floor,
|
|
@@ -12522,8 +12519,13 @@ export
|
|
|
12522
12519
|
isOverlapping,
|
|
12523
12520
|
isIntersecting,
|
|
12524
12521
|
wave,
|
|
12522
|
+
|
|
12523
|
+
// Utilities
|
|
12525
12524
|
formatTime,
|
|
12526
12525
|
fetchJSON,
|
|
12526
|
+
saveText,
|
|
12527
|
+
saveCanvas,
|
|
12528
|
+
saveDataURL,
|
|
12527
12529
|
|
|
12528
12530
|
// Random
|
|
12529
12531
|
rand,
|
|
@@ -12569,10 +12571,11 @@ export
|
|
|
12569
12571
|
TextureInfo,
|
|
12570
12572
|
mainCanvas,
|
|
12571
12573
|
mainContext,
|
|
12572
|
-
drawCanvas,
|
|
12573
12574
|
drawContext,
|
|
12574
|
-
|
|
12575
|
-
|
|
12575
|
+
workCanvas,
|
|
12576
|
+
workContext,
|
|
12577
|
+
workReadCanvas,
|
|
12578
|
+
workReadContext,
|
|
12576
12579
|
mainCanvasSize,
|
|
12577
12580
|
textureInfos,
|
|
12578
12581
|
drawCount,
|
|
@@ -12580,6 +12583,7 @@ export
|
|
|
12580
12583
|
worldToScreen,
|
|
12581
12584
|
screenToWorldDelta,
|
|
12582
12585
|
worldToScreenDelta,
|
|
12586
|
+
screenToWorldTransform,
|
|
12583
12587
|
drawTile,
|
|
12584
12588
|
drawRect,
|
|
12585
12589
|
drawRectGradient,
|
|
@@ -12590,7 +12594,6 @@ export
|
|
|
12590
12594
|
drawCircle,
|
|
12591
12595
|
drawCanvas2D,
|
|
12592
12596
|
drawText,
|
|
12593
|
-
drawTextOverlay,
|
|
12594
12597
|
drawTextScreen,
|
|
12595
12598
|
setBlendMode,
|
|
12596
12599
|
combineCanvases,
|