littlejsengine 1.15.9 → 1.16.1
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 +66 -83
- package/dist/littlejs.esm.js +318 -330
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +306 -319
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +229 -242
- package/examples/box2d/game.js +1 -1
- 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/module/game.js +1 -2
- package/examples/particles/index.html +1 -1
- package/examples/platformer/game.js +4 -4
- package/examples/puzzle/game.js +2 -2
- package/examples/shorts/base.html +3 -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 +2 -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 +15 -3
- package/reference.md +4 -6
- package/src/engine.js +35 -37
- package/src/engineAudio.js +3 -3
- package/src/engineDebug.js +78 -78
- package/src/engineDraw.js +107 -109
- package/src/engineExport.js +12 -11
- package/src/engineInput.js +1 -1
- package/src/engineMedals.js +3 -3
- package/src/engineObject.js +4 -2
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +20 -30
- package/src/engineTileLayer.js +5 -4
- package/src/engineUtilities.js +2 -10
- package/src/engineWebGL.js +6 -5
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.1';
|
|
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
|
{
|
|
@@ -284,19 +285,19 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
284
285
|
glFlush();
|
|
285
286
|
debugVideoCaptureUpdate();
|
|
286
287
|
|
|
287
|
-
if (
|
|
288
|
+
if (debugWatermark && !debugVideoCaptureIsActive())
|
|
288
289
|
{
|
|
289
290
|
// update fps display
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
mainContext.textAlign = 'right';
|
|
292
|
+
mainContext.textBaseline = 'top';
|
|
293
|
+
mainContext.font = '1em monospace';
|
|
294
|
+
mainContext.fillStyle = '#000';
|
|
294
295
|
const text = engineName + ' ' + 'v' + engineVersion + ' / '
|
|
295
296
|
+ drawCount + ' / ' + engineObjects.length + ' / ' + averageFPS.toFixed(1)
|
|
296
297
|
+ (glEnable ? ' GL' : ' 2D') ;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
298
|
+
mainContext.fillText(text, mainCanvas.width-3, 3);
|
|
299
|
+
mainContext.fillStyle = '#fff';
|
|
300
|
+
mainContext.fillText(text, mainCanvas.width-2, 2);
|
|
300
301
|
}
|
|
301
302
|
drawCount = 0;
|
|
302
303
|
}
|
|
@@ -316,8 +317,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
316
317
|
const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
|
|
317
318
|
const w = innerAspect < fixedAspect ? '100%' : '';
|
|
318
319
|
const h = innerAspect < fixedAspect ? '' : '100%';
|
|
319
|
-
|
|
320
|
-
|
|
320
|
+
mainCanvas.style.width = w;
|
|
321
|
+
mainCanvas.style.height = h;
|
|
321
322
|
if (glCanvas)
|
|
322
323
|
{
|
|
323
324
|
glCanvas.style.width = w;
|
|
@@ -332,6 +333,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
332
333
|
|
|
333
334
|
// responsive aspect ratio with native resolution
|
|
334
335
|
const innerAspect = innerWidth / innerHeight;
|
|
336
|
+
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
335
337
|
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
336
338
|
{
|
|
337
339
|
// full height
|
|
@@ -346,12 +348,12 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
346
348
|
}
|
|
347
349
|
}
|
|
348
350
|
|
|
349
|
-
// clear main
|
|
350
|
-
|
|
351
|
-
|
|
351
|
+
// clear main canvas and set size
|
|
352
|
+
mainCanvas.width = mainCanvasSize.x;
|
|
353
|
+
mainCanvas.height = mainCanvasSize.y;
|
|
352
354
|
|
|
353
355
|
// apply the clear color to main canvas
|
|
354
|
-
if (canvasClearColor.a > 0)
|
|
356
|
+
if (canvasClearColor.a > 0 && !glEnable)
|
|
355
357
|
{
|
|
356
358
|
mainContext.fillStyle = canvasClearColor.toString();
|
|
357
359
|
mainContext.fillRect(0, 0, mainCanvasSize.x, mainCanvasSize.y);
|
|
@@ -359,9 +361,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
359
361
|
}
|
|
360
362
|
|
|
361
363
|
// set default line join and cap
|
|
362
|
-
|
|
363
|
-
mainContext.
|
|
364
|
-
mainContext.lineCap = overlayContext.lineCap = lineCap;
|
|
364
|
+
mainContext.lineJoin = 'round';
|
|
365
|
+
mainContext.lineCap = 'round';
|
|
365
366
|
}
|
|
366
367
|
|
|
367
368
|
// wait for gameInit to load
|
|
@@ -373,6 +374,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
373
374
|
if (headlessMode)
|
|
374
375
|
return startEngine();
|
|
375
376
|
|
|
377
|
+
// setup webgl
|
|
378
|
+
glInit(rootElement);
|
|
379
|
+
|
|
376
380
|
// setup html
|
|
377
381
|
const styleRoot =
|
|
378
382
|
'margin:0;' + // fill the window
|
|
@@ -383,36 +387,30 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
383
387
|
'touch-action:none;' + // prevent mobile pinch to resize
|
|
384
388
|
'-webkit-touch-callout:none'; // compatibility for ios
|
|
385
389
|
rootElement.style.cssText = styleRoot;
|
|
386
|
-
drawCanvas = mainCanvas = document.createElement('canvas');
|
|
387
|
-
rootElement.appendChild(mainCanvas);
|
|
390
|
+
drawCanvas = mainCanvas = rootElement.appendChild(document.createElement('canvas'));
|
|
388
391
|
drawContext = mainContext = mainCanvas.getContext('2d');
|
|
389
392
|
|
|
390
393
|
// init stuff and start engine
|
|
391
394
|
inputInit();
|
|
392
395
|
audioInit();
|
|
393
396
|
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
397
|
|
|
401
398
|
// setup canvases
|
|
402
399
|
// transform way is still more reliable then flexbox or grid
|
|
403
400
|
const styleCanvas = 'position:absolute;'+ // allow canvases to overlap
|
|
404
401
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center on screen
|
|
405
|
-
mainCanvas.style.cssText =
|
|
402
|
+
mainCanvas.style.cssText = styleCanvas;
|
|
406
403
|
if (glCanvas)
|
|
407
404
|
glCanvas.style.cssText = styleCanvas;
|
|
408
405
|
setCanvasPixelated(canvasPixelated);
|
|
409
|
-
setOverlayCanvasPixelated(overlayCanvasPixelated);
|
|
410
406
|
updateCanvas();
|
|
411
407
|
glPreRender();
|
|
412
408
|
|
|
413
|
-
// create offscreen
|
|
414
|
-
workCanvas = new OffscreenCanvas(
|
|
415
|
-
workContext = workCanvas.getContext('2d'
|
|
409
|
+
// create offscreen canvases for image processing
|
|
410
|
+
workCanvas = new OffscreenCanvas(64, 64);
|
|
411
|
+
workContext = workCanvas.getContext('2d');
|
|
412
|
+
workReadCanvas = new OffscreenCanvas(64, 64);
|
|
413
|
+
workReadContext = workReadCanvas.getContext('2d', { willReadFrequently: true });
|
|
416
414
|
|
|
417
415
|
// create promises for loading images
|
|
418
416
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -468,9 +466,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
468
466
|
// LittleJS Splash Screen
|
|
469
467
|
function drawEngineSplashScreen(t)
|
|
470
468
|
{
|
|
471
|
-
const x =
|
|
472
|
-
const w =
|
|
473
|
-
const h =
|
|
469
|
+
const x = mainContext;
|
|
470
|
+
const w = mainCanvas.width = innerWidth;
|
|
471
|
+
const h = mainCanvas.height = innerHeight;
|
|
474
472
|
|
|
475
473
|
{
|
|
476
474
|
// background
|
|
@@ -768,7 +766,7 @@ const debugPointSize = .5;
|
|
|
768
766
|
* @type {boolean}
|
|
769
767
|
* @default
|
|
770
768
|
* @memberof Debug */
|
|
771
|
-
let
|
|
769
|
+
let debugWatermark = true;
|
|
772
770
|
|
|
773
771
|
/** Key code used to toggle debug mode, Esc by default
|
|
774
772
|
* @type {string}
|
|
@@ -1054,12 +1052,19 @@ function debugUpdate()
|
|
|
1054
1052
|
if (!debug)
|
|
1055
1053
|
return;
|
|
1056
1054
|
|
|
1055
|
+
if (debugVideoCaptureIsActive())
|
|
1056
|
+
{
|
|
1057
|
+
// control to stop video capture
|
|
1058
|
+
if (keyWasPressed('Digit6') || keyWasPressed(debugKey))
|
|
1059
|
+
debugVideoCaptureStop();
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1057
1062
|
if (keyWasPressed(debugKey)) // Esc
|
|
1058
1063
|
debugOverlay = !debugOverlay;
|
|
1059
1064
|
if (debugOverlay)
|
|
1060
1065
|
{
|
|
1061
1066
|
if (keyWasPressed('Digit0'))
|
|
1062
|
-
|
|
1067
|
+
debugWatermark = !debugWatermark;
|
|
1063
1068
|
if (keyWasPressed('Digit1'))
|
|
1064
1069
|
debugPhysics = !debugPhysics, debugParticles = false;
|
|
1065
1070
|
if (keyWasPressed('Digit2'))
|
|
@@ -1070,8 +1075,8 @@ function debugUpdate()
|
|
|
1070
1075
|
debugRaycast = !debugRaycast;
|
|
1071
1076
|
if (keyWasPressed('Digit5'))
|
|
1072
1077
|
debugScreenshot();
|
|
1073
|
-
if (keyWasPressed('Digit6'))
|
|
1074
|
-
|
|
1078
|
+
if (keyWasPressed('Digit6') && !debugVideoCaptureIsActive())
|
|
1079
|
+
debugVideoCaptureStart();
|
|
1075
1080
|
}
|
|
1076
1081
|
}
|
|
1077
1082
|
|
|
@@ -1087,13 +1092,11 @@ function debugRender()
|
|
|
1087
1092
|
{
|
|
1088
1093
|
// combine canvases, remove alpha and save
|
|
1089
1094
|
combineCanvases();
|
|
1090
|
-
|
|
1091
|
-
overlayContext.fillRect(0,0,w,h);
|
|
1092
|
-
overlayContext.drawImage(mainCanvas, 0, 0);
|
|
1093
|
-
debugSaveCanvas(overlayCanvas);
|
|
1095
|
+
debugSaveCanvas(mainCanvas);
|
|
1094
1096
|
debugTakeScreenshot = 0;
|
|
1095
1097
|
}
|
|
1096
1098
|
|
|
1099
|
+
const debugContext = mainContext;
|
|
1097
1100
|
if (debugGamepads && gamepadsEnable)
|
|
1098
1101
|
{
|
|
1099
1102
|
// draw gamepads
|
|
@@ -1141,9 +1144,6 @@ function debugRender()
|
|
|
1141
1144
|
let debugObject;
|
|
1142
1145
|
if (debugOverlay)
|
|
1143
1146
|
{
|
|
1144
|
-
const saveContext = mainContext;
|
|
1145
|
-
mainContext = overlayContext;
|
|
1146
|
-
|
|
1147
1147
|
// draw red rectangle around screen
|
|
1148
1148
|
const cameraSize = getCameraSize();
|
|
1149
1149
|
debugRect(cameraPos, cameraSize.subtract(vec2(.1)), '#f008');
|
|
@@ -1175,15 +1175,14 @@ function debugRender()
|
|
|
1175
1175
|
// show floored tile pick for tile collision
|
|
1176
1176
|
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), rgb(1,1,0,.5));
|
|
1177
1177
|
}
|
|
1178
|
-
mainContext = saveContext;
|
|
1179
1178
|
}
|
|
1180
1179
|
|
|
1181
1180
|
{
|
|
1182
1181
|
// draw debug primitives
|
|
1183
|
-
|
|
1182
|
+
debugContext.lineWidth = 2;
|
|
1184
1183
|
debugPrimitives.forEach(p=>
|
|
1185
1184
|
{
|
|
1186
|
-
|
|
1185
|
+
debugContext.save();
|
|
1187
1186
|
|
|
1188
1187
|
// create canvas transform from world space to screen space
|
|
1189
1188
|
// without scaling because we want consistent pixel sizes
|
|
@@ -1194,55 +1193,56 @@ function debugRender()
|
|
|
1194
1193
|
scale = cameraScale;
|
|
1195
1194
|
angle -= cameraAngle;
|
|
1196
1195
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1196
|
+
debugContext.translate(pos.x|0, pos.y|0);
|
|
1197
|
+
debugContext.rotate(angle);
|
|
1198
|
+
debugContext.scale(1, p.text ? 1 : -1);
|
|
1199
|
+
debugContext.fillStyle = p.color;
|
|
1200
|
+
debugContext.strokeStyle = p.color;
|
|
1201
1201
|
if (p.text !== undefined)
|
|
1202
1202
|
{
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1203
|
+
debugContext.font = p.size*scale + 'px '+ p.font;
|
|
1204
|
+
debugContext.textAlign = 'center';
|
|
1205
|
+
debugContext.textBaseline = 'middle';
|
|
1206
|
+
debugContext.fillText(p.text, 0, 0);
|
|
1207
1207
|
}
|
|
1208
1208
|
else if (p.points !== undefined)
|
|
1209
1209
|
{
|
|
1210
1210
|
// poly
|
|
1211
|
-
|
|
1211
|
+
debugContext.beginPath();
|
|
1212
1212
|
for (const point of p.points)
|
|
1213
1213
|
{
|
|
1214
1214
|
const p2 = point.scale(scale).floor();
|
|
1215
|
-
|
|
1215
|
+
debugContext.lineTo(p2.x, p2.y);
|
|
1216
1216
|
}
|
|
1217
|
-
|
|
1218
|
-
p.fill &&
|
|
1219
|
-
|
|
1217
|
+
debugContext.closePath();
|
|
1218
|
+
p.fill && debugContext.fill();
|
|
1219
|
+
debugContext.stroke();
|
|
1220
1220
|
}
|
|
1221
1221
|
else if (p.size === 0 || (p.size.x === 0 && p.size.y === 0))
|
|
1222
1222
|
{
|
|
1223
1223
|
// point
|
|
1224
1224
|
const pointSize = debugPointSize * scale;
|
|
1225
|
-
|
|
1226
|
-
|
|
1225
|
+
debugContext.fillRect(-pointSize/2, -1, pointSize, 3);
|
|
1226
|
+
debugContext.fillRect(-1, -pointSize/2, 3, pointSize);
|
|
1227
1227
|
}
|
|
1228
1228
|
else if (p.size.x !== undefined)
|
|
1229
1229
|
{
|
|
1230
1230
|
// rect
|
|
1231
1231
|
const s = p.size.scale(scale).floor();
|
|
1232
1232
|
const w = s.x, h = s.y;
|
|
1233
|
-
p.fill &&
|
|
1234
|
-
|
|
1233
|
+
p.fill && debugContext.fillRect(-w/2|0, -h/2|0, w, h);
|
|
1234
|
+
debugContext.strokeRect(-w/2|0, -h/2|0, w, h);
|
|
1235
1235
|
}
|
|
1236
1236
|
else
|
|
1237
1237
|
{
|
|
1238
1238
|
// circle
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
p.fill &&
|
|
1242
|
-
|
|
1239
|
+
debugContext.beginPath();
|
|
1240
|
+
debugContext.arc(0, 0, p.size*scale/2, 0, 9);
|
|
1241
|
+
p.fill && debugContext.fill();
|
|
1242
|
+
debugContext.stroke();
|
|
1243
1243
|
}
|
|
1244
1244
|
|
|
1245
|
-
|
|
1245
|
+
debugContext.restore();
|
|
1246
1246
|
});
|
|
1247
1247
|
|
|
1248
1248
|
// remove expired primitives
|
|
@@ -1251,55 +1251,53 @@ function debugRender()
|
|
|
1251
1251
|
|
|
1252
1252
|
if (debugObject)
|
|
1253
1253
|
{
|
|
1254
|
-
const saveContext = mainContext;
|
|
1255
|
-
mainContext = overlayContext;
|
|
1256
1254
|
const raycastHitPos = tileCollisionRaycast(debugObject.pos, mousePos);
|
|
1257
1255
|
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), rgb(0,1,1,.3));
|
|
1258
1256
|
drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5));
|
|
1259
1257
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
'\
|
|
1263
|
-
|
|
1258
|
+
let debugText = 'mouse pos = ' + mousePos;
|
|
1259
|
+
if (tileCollisionLayers.length)
|
|
1260
|
+
debugText += '\nmouse collision = ' + tileCollisionGetData(mousePos);
|
|
1261
|
+
debugText += '\n\n--- object info ---\n';
|
|
1262
|
+
debugText += debugObject.toString();
|
|
1264
1263
|
drawTextScreen(debugText, mousePosScreen, 24, rgb(), .05, undefined, 'center', 'monospace');
|
|
1265
|
-
mainContext = saveContext;
|
|
1266
1264
|
}
|
|
1267
1265
|
|
|
1268
1266
|
{
|
|
1269
1267
|
// draw debug overlay
|
|
1270
1268
|
const fontSize = 20;
|
|
1271
1269
|
const lineHeight = fontSize * 1.2 | 0;
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1270
|
+
debugContext.save();
|
|
1271
|
+
debugContext.fillStyle = '#fff';
|
|
1272
|
+
debugContext.textAlign = 'left';
|
|
1273
|
+
debugContext.textBaseline = 'top';
|
|
1274
|
+
debugContext.font = fontSize + 'px monospace';
|
|
1275
|
+
debugContext.shadowColor = '#000';
|
|
1276
|
+
debugContext.shadowBlur = 9;
|
|
1279
1277
|
|
|
1280
1278
|
let x = 9, y = 0, h = lineHeight;
|
|
1281
1279
|
if (debugOverlay)
|
|
1282
1280
|
{
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1281
|
+
debugContext.fillText(`${engineName} v${engineVersion}`, x, y += h/2 );
|
|
1282
|
+
debugContext.fillText('Time: ' + formatTime(time), x, y += h);
|
|
1283
|
+
debugContext.fillText('FPS: ' + averageFPS.toFixed(1) + (glEnable?' WebGL':' Canvas2D'),
|
|
1286
1284
|
x, y += h);
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1285
|
+
debugContext.fillText('Objects: ' + engineObjects.length, x, y += h);
|
|
1286
|
+
debugContext.fillText('Draw Count: ' + drawCount, x, y += h);
|
|
1287
|
+
debugContext.fillText('---------', x, y += h);
|
|
1288
|
+
debugContext.fillStyle = '#f00';
|
|
1289
|
+
debugContext.fillText('ESC: Debug Overlay', x, y += h);
|
|
1290
|
+
debugContext.fillStyle = debugPhysics ? '#f00' : '#fff';
|
|
1291
|
+
debugContext.fillText('1: Debug Physics', x, y += h);
|
|
1292
|
+
debugContext.fillStyle = debugParticles ? '#f00' : '#fff';
|
|
1293
|
+
debugContext.fillText('2: Debug Particles', x, y += h);
|
|
1294
|
+
debugContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
1295
|
+
debugContext.fillText('3: Debug Gamepads', x, y += h);
|
|
1296
|
+
debugContext.fillStyle = debugRaycast ? '#f00' : '#fff';
|
|
1297
|
+
debugContext.fillText('4: Debug Raycasts', x, y += h);
|
|
1298
|
+
debugContext.fillStyle = '#fff';
|
|
1299
|
+
debugContext.fillText('5: Save Screenshot', x, y += h);
|
|
1300
|
+
debugContext.fillText('6: Toggle Video Capture', x, y += h);
|
|
1303
1301
|
|
|
1304
1302
|
let keysPressed = '';
|
|
1305
1303
|
let mousePressed = '';
|
|
@@ -1312,8 +1310,8 @@ function debugRender()
|
|
|
1312
1310
|
else if (keyIsDown(i, 0))
|
|
1313
1311
|
keysPressed += i + ' ' ;
|
|
1314
1312
|
}
|
|
1315
|
-
mousePressed &&
|
|
1316
|
-
keysPressed &&
|
|
1313
|
+
mousePressed && debugContext.fillText('Mouse: ' + mousePressed, x, y += h);
|
|
1314
|
+
keysPressed && debugContext.fillText('Keys: ' + keysPressed, x, y += h);
|
|
1317
1315
|
|
|
1318
1316
|
// show gamepad buttons
|
|
1319
1317
|
for (let i = 1; i < inputData.length; i++)
|
|
@@ -1325,18 +1323,18 @@ function debugRender()
|
|
|
1325
1323
|
if (keyIsDown(j, i))
|
|
1326
1324
|
buttonsPressed += j + ' ' ;
|
|
1327
1325
|
}
|
|
1328
|
-
buttonsPressed &&
|
|
1326
|
+
buttonsPressed && debugContext.fillText(`Gamepad ${i-1}: ` + buttonsPressed, x, y += h);
|
|
1329
1327
|
}
|
|
1330
1328
|
}
|
|
1331
1329
|
else
|
|
1332
1330
|
{
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1331
|
+
debugContext.fillText(debugPhysics ? 'Debug Physics' : '', x, y += h);
|
|
1332
|
+
debugContext.fillText(debugParticles ? 'Debug Particles' : '', x, y += h);
|
|
1333
|
+
debugContext.fillText(debugRaycast ? 'Debug Raycasts' : '', x, y += h);
|
|
1334
|
+
debugContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
|
|
1337
1335
|
}
|
|
1338
1336
|
|
|
1339
|
-
|
|
1337
|
+
debugContext.restore();
|
|
1340
1338
|
}
|
|
1341
1339
|
}
|
|
1342
1340
|
|
|
@@ -1387,7 +1385,7 @@ function debugVideoCaptureStart()
|
|
|
1387
1385
|
// start recording
|
|
1388
1386
|
LOG('Video capture started.');
|
|
1389
1387
|
debugVideoCapture.start();
|
|
1390
|
-
debugVideoCaptureTimer = new Timer(0);
|
|
1388
|
+
debugVideoCaptureTimer = new Timer(0, true);
|
|
1391
1389
|
|
|
1392
1390
|
if (!debugVideoCaptureIcon)
|
|
1393
1391
|
{
|
|
@@ -1585,11 +1583,7 @@ function percent(value, valueA, valueB)
|
|
|
1585
1583
|
* @return {number}
|
|
1586
1584
|
* @memberof Utilities */
|
|
1587
1585
|
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
|
-
}
|
|
1586
|
+
{ return valueA + clamp(percent) * (valueB-valueA); }
|
|
1593
1587
|
|
|
1594
1588
|
/** Gets percent between percentA and percentB and linearly interpolates between lerpA and lerpB
|
|
1595
1589
|
* A shortcut for lerp(lerpA, lerpB, percent(value, percentA, percentB))
|
|
@@ -1620,11 +1614,7 @@ function distanceWrap(valueA, valueB, wrapSize=1)
|
|
|
1620
1614
|
* @return {number}
|
|
1621
1615
|
* @memberof Utilities */
|
|
1622
1616
|
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
|
-
}
|
|
1617
|
+
{ return valueA + clamp(percent) * distanceWrap(valueB, valueA, wrapSize); }
|
|
1628
1618
|
|
|
1629
1619
|
/** Returns signed wrapped distance between the two angles passed in
|
|
1630
1620
|
* @param {number} angleA
|
|
@@ -2764,15 +2754,7 @@ let canvasFixedSize = vec2();
|
|
|
2764
2754
|
* @type {boolean}
|
|
2765
2755
|
* @default
|
|
2766
2756
|
* @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;
|
|
2757
|
+
let canvasPixelated = false;
|
|
2776
2758
|
|
|
2777
2759
|
/** Disables texture filtering for crisper pixel art
|
|
2778
2760
|
* @type {boolean}
|
|
@@ -2820,13 +2802,19 @@ let glCircleSides = 32;
|
|
|
2820
2802
|
* @type {Vector2}
|
|
2821
2803
|
* @default Vector2(16,16)
|
|
2822
2804
|
* @memberof Settings */
|
|
2823
|
-
let
|
|
2805
|
+
let tileDefaultSize = vec2(16);
|
|
2824
2806
|
|
|
2825
|
-
/**
|
|
2807
|
+
/** Default padding pixels around tiles
|
|
2826
2808
|
* @type {number}
|
|
2827
2809
|
* @default
|
|
2828
2810
|
* @memberof Settings */
|
|
2829
|
-
let
|
|
2811
|
+
let tileDefaultPadding = 0;
|
|
2812
|
+
|
|
2813
|
+
/** Default amount of pixels smaller to draw tiles to prevent neighbor bleeding
|
|
2814
|
+
* @type {number}
|
|
2815
|
+
* @default
|
|
2816
|
+
* @memberof Settings */
|
|
2817
|
+
let tileDefaultBleed = 0;
|
|
2830
2818
|
|
|
2831
2819
|
///////////////////////////////////////////////////////////////////////////////
|
|
2832
2820
|
// Object settings
|
|
@@ -3064,7 +3052,6 @@ function setCanvasMaxAspect(aspect) { canvasMaxAspect = aspect; }
|
|
|
3064
3052
|
function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
|
|
3065
3053
|
|
|
3066
3054
|
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
3067
|
-
* - If enabled sets css image-rendering:pixelated
|
|
3068
3055
|
* @param {boolean} pixelated
|
|
3069
3056
|
* @memberof Settings */
|
|
3070
3057
|
function setCanvasPixelated(pixelated)
|
|
@@ -3076,18 +3063,6 @@ function setCanvasPixelated(pixelated)
|
|
|
3076
3063
|
glCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
3077
3064
|
}
|
|
3078
3065
|
|
|
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
3066
|
/** Disables texture filtering for crisper pixel art
|
|
3092
3067
|
* @param {boolean} pixelated
|
|
3093
3068
|
* @memberof Settings */
|
|
@@ -3131,12 +3106,17 @@ function setGLCircleSides(sides) { glCircleSides = sides; }
|
|
|
3131
3106
|
/** Set default size of tiles in pixels
|
|
3132
3107
|
* @param {Vector2} size
|
|
3133
3108
|
* @memberof Settings */
|
|
3134
|
-
function
|
|
3109
|
+
function setTileDefaultSize(size) { tileDefaultSize = size.copy(); }
|
|
3135
3110
|
|
|
3136
|
-
/**
|
|
3137
|
-
* @param {number}
|
|
3111
|
+
/** Default padding pixels around tiles
|
|
3112
|
+
* @param {number} padding
|
|
3138
3113
|
* @memberof Settings */
|
|
3139
|
-
function
|
|
3114
|
+
function setTileDefaultPadding(padding) { tileDefaultPadding = padding; }
|
|
3115
|
+
|
|
3116
|
+
/** Default amount of pixels smaller to draw tiles to prevent neighbor bleeding
|
|
3117
|
+
* @param {number} bleed
|
|
3118
|
+
* @memberof Settings */
|
|
3119
|
+
function setTileDefaultBleed(bleed) { tileDefaultBleed = bleed; }
|
|
3140
3120
|
|
|
3141
3121
|
/** Set if collisions between objects are enabled
|
|
3142
3122
|
* @param {boolean} enable
|
|
@@ -3287,7 +3267,7 @@ function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUn
|
|
|
3287
3267
|
/** Set if watermark with FPS should be shown
|
|
3288
3268
|
* @param {boolean} show
|
|
3289
3269
|
* @memberof Debug */
|
|
3290
|
-
function
|
|
3270
|
+
function setDebugWatermark(show) { debugWatermark = show; }
|
|
3291
3271
|
|
|
3292
3272
|
/** Set key code used to toggle debug mode, Esc by default
|
|
3293
3273
|
* @param {string} key
|
|
@@ -3723,10 +3703,11 @@ class EngineObject
|
|
|
3723
3703
|
* @return {number} -1 if this.mirror is true, or 1 if not mirrored */
|
|
3724
3704
|
getMirrorSign() { return this.mirror ? -1 : 1; }
|
|
3725
3705
|
|
|
3726
|
-
/** Attaches a child to this with a
|
|
3706
|
+
/** Attaches a child to this with a local transform, returns child for chaining
|
|
3727
3707
|
* @param {EngineObject} child
|
|
3728
3708
|
* @param {Vector2} [localPos=(0,0)]
|
|
3729
|
-
* @param {number} [localAngle]
|
|
3709
|
+
* @param {number} [localAngle]
|
|
3710
|
+
* @return {EngineObject} The child object added */
|
|
3730
3711
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
3731
3712
|
{
|
|
3732
3713
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
@@ -3736,6 +3717,7 @@ class EngineObject
|
|
|
3736
3717
|
child.parent = this;
|
|
3737
3718
|
child.localPos = localPos.copy();
|
|
3738
3719
|
child.localAngle = localAngle;
|
|
3720
|
+
return child;
|
|
3739
3721
|
}
|
|
3740
3722
|
|
|
3741
3723
|
/** Removes a child from this one
|
|
@@ -3826,7 +3808,6 @@ class EngineObject
|
|
|
3826
3808
|
* There are 3 canvas/contexts available to draw to...
|
|
3827
3809
|
* mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
|
|
3828
3810
|
* glCanvas - Used by the accelerated WebGL batch rendering system.
|
|
3829
|
-
* overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
|
|
3830
3811
|
*
|
|
3831
3812
|
* The WebGL rendering system is very fast with some caveats...
|
|
3832
3813
|
* - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
|
|
@@ -3846,16 +3827,6 @@ let mainCanvas;
|
|
|
3846
3827
|
* @memberof Draw */
|
|
3847
3828
|
let mainContext;
|
|
3848
3829
|
|
|
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
3830
|
/** The default canvas to use for drawing, usually mainCanvas
|
|
3860
3831
|
* @type {HTMLCanvasElement|OffscreenCanvas}
|
|
3861
3832
|
* @memberof Draw */
|
|
@@ -3876,6 +3847,16 @@ let workCanvas;
|
|
|
3876
3847
|
* @memberof Draw */
|
|
3877
3848
|
let workContext;
|
|
3878
3849
|
|
|
3850
|
+
/** Offscreen canvas with willReadFrequently that can be used for image processing
|
|
3851
|
+
* @type {OffscreenCanvas}
|
|
3852
|
+
* @memberof Draw */
|
|
3853
|
+
let workReadCanvas;
|
|
3854
|
+
|
|
3855
|
+
/** Offscreen canvas with willReadFrequently that can be used for image processing
|
|
3856
|
+
* @type {OffscreenCanvasRenderingContext2D}
|
|
3857
|
+
* @memberof Draw */
|
|
3858
|
+
let workReadContext;
|
|
3859
|
+
|
|
3879
3860
|
/** The size of the main canvas (and other secondary canvases)
|
|
3880
3861
|
* @type {Vector2}
|
|
3881
3862
|
* @memberof Draw */
|
|
@@ -3898,7 +3879,7 @@ let drawCount;
|
|
|
3898
3879
|
* - This can take vecs or floats for easier use and conversion
|
|
3899
3880
|
* - If an index is passed in, the tile size and index will determine the position
|
|
3900
3881
|
* @param {Vector2|number} [pos=0] - Position of the tile in pixels, or tile index
|
|
3901
|
-
* @param {Vector2|number} [size
|
|
3882
|
+
* @param {Vector2|number} [size] - Size of tile in pixels
|
|
3902
3883
|
* @param {number} [textureIndex] - Texture index to use
|
|
3903
3884
|
* @param {number} [padding] - How many pixels padding around tiles
|
|
3904
3885
|
* @return {TileInfo}
|
|
@@ -3908,7 +3889,7 @@ let drawCount;
|
|
|
3908
3889
|
* tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
|
|
3909
3890
|
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
3910
3891
|
* @memberof Draw */
|
|
3911
|
-
function tile(pos=new Vector2, size=
|
|
3892
|
+
function tile(pos=new Vector2, size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding)
|
|
3912
3893
|
{
|
|
3913
3894
|
if (headlessMode)
|
|
3914
3895
|
return new TileInfo;
|
|
@@ -3947,13 +3928,13 @@ function tile(pos=new Vector2, size=tileSizeDefault, textureIndex=0, padding=0)
|
|
|
3947
3928
|
class TileInfo
|
|
3948
3929
|
{
|
|
3949
3930
|
/** 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} [
|
|
3931
|
+
* @param {Vector2} [pos=(0,0)] - Top left corner of tile in pixels
|
|
3932
|
+
* @param {Vector2} [size] - Size of tile in pixels
|
|
3933
|
+
* @param {number} [textureIndex] - Texture index to use
|
|
3934
|
+
* @param {number} [padding] - How many pixels padding around tiles
|
|
3935
|
+
* @param {number} [bleed] - How many pixels smaller to draw tiles
|
|
3955
3936
|
*/
|
|
3956
|
-
constructor(pos=vec2(), size=
|
|
3937
|
+
constructor(pos=vec2(), size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding, bleed=tileDefaultBleed)
|
|
3957
3938
|
{
|
|
3958
3939
|
/** @property {Vector2} - Top left corner of tile in pixels */
|
|
3959
3940
|
this.pos = pos.copy();
|
|
@@ -3966,7 +3947,7 @@ class TileInfo
|
|
|
3966
3947
|
/** @property {TextureInfo} - The texture info for this tile */
|
|
3967
3948
|
this.textureInfo = textureInfos[this.textureIndex];
|
|
3968
3949
|
/** @property {number} - Shrinks tile by this many pixels to prevent neighbors bleeding */
|
|
3969
|
-
this.
|
|
3950
|
+
this.bleed = bleed;
|
|
3970
3951
|
}
|
|
3971
3952
|
|
|
3972
3953
|
/** Returns a copy of this tile offset by a vector
|
|
@@ -3974,7 +3955,7 @@ class TileInfo
|
|
|
3974
3955
|
* @return {TileInfo}
|
|
3975
3956
|
*/
|
|
3976
3957
|
offset(offset)
|
|
3977
|
-
{ return new TileInfo(this.pos.add(offset), this.size, this.textureIndex, this.padding, this.
|
|
3958
|
+
{ return new TileInfo(this.pos.add(offset), this.size, this.textureIndex, this.padding, this.bleed); }
|
|
3978
3959
|
|
|
3979
3960
|
/** Returns a copy of this tile offset by a number of animation frames
|
|
3980
3961
|
* @param {number} frame - Offset to apply in animation frames
|
|
@@ -3997,7 +3978,7 @@ class TileInfo
|
|
|
3997
3978
|
this.size = textureInfo.size.copy();
|
|
3998
3979
|
this.textureInfo = textureInfo;
|
|
3999
3980
|
// do not use padding or bleed
|
|
4000
|
-
this.
|
|
3981
|
+
this.bleed = this.padding = 0;
|
|
4001
3982
|
return this;
|
|
4002
3983
|
}
|
|
4003
3984
|
}
|
|
@@ -4063,7 +4044,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4063
4044
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
4064
4045
|
|
|
4065
4046
|
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
4066
|
-
const
|
|
4047
|
+
const bleed = tileInfo?.bleed ?? 0;
|
|
4067
4048
|
if (useWebGL && glEnable)
|
|
4068
4049
|
{
|
|
4069
4050
|
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
@@ -4078,13 +4059,13 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4078
4059
|
const w = tileInfo.size.x * sizeInverse.x;
|
|
4079
4060
|
const h = tileInfo.size.y * sizeInverse.y;
|
|
4080
4061
|
glSetTexture(textureInfo.glTexture);
|
|
4081
|
-
if (
|
|
4062
|
+
if (bleed)
|
|
4082
4063
|
{
|
|
4083
|
-
const
|
|
4084
|
-
const
|
|
4064
|
+
const bleedX = sizeInverse.x*bleed;
|
|
4065
|
+
const bleedY = sizeInverse.y*bleed;
|
|
4085
4066
|
glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
|
|
4086
|
-
x +
|
|
4087
|
-
x -
|
|
4067
|
+
x + bleedX, y + bleedY,
|
|
4068
|
+
x - bleedX + w, y - bleedY + h,
|
|
4088
4069
|
color.rgbaInt(), additiveColor && additiveColor.rgbaInt());
|
|
4089
4070
|
}
|
|
4090
4071
|
else
|
|
@@ -4112,7 +4093,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4112
4093
|
// calculate uvs and render
|
|
4113
4094
|
const x = tileInfo.pos.x, y = tileInfo.pos.y;
|
|
4114
4095
|
const w = tileInfo.size.x, h = tileInfo.size.y;
|
|
4115
|
-
drawImageColor(context, textureInfo.image, x, y, w, h, -.5, -.5, 1, 1, color, additiveColor,
|
|
4096
|
+
drawImageColor(context, textureInfo.image, x, y, w, h, -.5, -.5, 1, 1, color, additiveColor, bleed);
|
|
4116
4097
|
}
|
|
4117
4098
|
else
|
|
4118
4099
|
{
|
|
@@ -4477,26 +4458,7 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
4477
4458
|
drawTextScreen(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, angle, context);
|
|
4478
4459
|
}
|
|
4479
4460
|
|
|
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
|
|
4461
|
+
/** Draw text in screen space
|
|
4500
4462
|
* Automatically splits new lines into rows
|
|
4501
4463
|
* @param {string|number} text
|
|
4502
4464
|
* @param {Vector2} pos
|
|
@@ -4509,9 +4471,9 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
4509
4471
|
* @param {string} [fontStyle]
|
|
4510
4472
|
* @param {number} [maxWidth]
|
|
4511
4473
|
* @param {number} [angle]
|
|
4512
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=
|
|
4474
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=mainContext]
|
|
4513
4475
|
* @memberof Draw */
|
|
4514
|
-
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=
|
|
4476
|
+
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=mainContext)
|
|
4515
4477
|
{
|
|
4516
4478
|
ASSERT(isString(text), 'text must be a string');
|
|
4517
4479
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -4555,16 +4517,16 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
4555
4517
|
* @memberof Draw */
|
|
4556
4518
|
function screenToWorld(screenPos)
|
|
4557
4519
|
{
|
|
4520
|
+
ASSERT(isVector2(screenPos), 'screenPos must be a vec2');
|
|
4521
|
+
|
|
4558
4522
|
let x = (screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale;
|
|
4559
4523
|
let y = (screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale;
|
|
4560
4524
|
if (cameraAngle)
|
|
4561
4525
|
{
|
|
4562
4526
|
// apply camera rotation
|
|
4563
4527
|
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
4564
|
-
const
|
|
4565
|
-
|
|
4566
|
-
x = rotatedX;
|
|
4567
|
-
y = rotatedY;
|
|
4528
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4529
|
+
x = xr; y = yr;
|
|
4568
4530
|
}
|
|
4569
4531
|
return new Vector2(x + cameraPos.x, y + cameraPos.y);
|
|
4570
4532
|
}
|
|
@@ -4575,16 +4537,16 @@ function screenToWorld(screenPos)
|
|
|
4575
4537
|
* @memberof Draw */
|
|
4576
4538
|
function worldToScreen(worldPos)
|
|
4577
4539
|
{
|
|
4540
|
+
ASSERT(isVector2(worldPos), 'worldPos must be a vec2');
|
|
4541
|
+
|
|
4578
4542
|
let x = worldPos.x - cameraPos.x;
|
|
4579
4543
|
let y = worldPos.y - cameraPos.y;
|
|
4580
4544
|
if (cameraAngle)
|
|
4581
4545
|
{
|
|
4582
4546
|
// apply inverse camera rotation
|
|
4583
4547
|
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
4584
|
-
const
|
|
4585
|
-
|
|
4586
|
-
x = rotatedX;
|
|
4587
|
-
y = rotatedY;
|
|
4548
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4549
|
+
x = xr; y = yr;
|
|
4588
4550
|
}
|
|
4589
4551
|
return new Vector2
|
|
4590
4552
|
(
|
|
@@ -4599,16 +4561,16 @@ function worldToScreen(worldPos)
|
|
|
4599
4561
|
* @memberof Draw */
|
|
4600
4562
|
function screenToWorldDelta(screenDelta)
|
|
4601
4563
|
{
|
|
4564
|
+
ASSERT(isVector2(screenDelta), 'screenDelta must be a vec2');
|
|
4565
|
+
|
|
4602
4566
|
let x = screenDelta.x / cameraScale;
|
|
4603
4567
|
let y = screenDelta.y / -cameraScale;
|
|
4604
4568
|
if (cameraAngle)
|
|
4605
4569
|
{
|
|
4606
4570
|
// apply camera rotation
|
|
4607
4571
|
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
4608
|
-
const
|
|
4609
|
-
|
|
4610
|
-
x = rotatedX;
|
|
4611
|
-
y = rotatedY;
|
|
4572
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4573
|
+
x = xr; y = yr;
|
|
4612
4574
|
}
|
|
4613
4575
|
return new Vector2(x, y);
|
|
4614
4576
|
}
|
|
@@ -4619,16 +4581,16 @@ function screenToWorldDelta(screenDelta)
|
|
|
4619
4581
|
* @memberof Draw */
|
|
4620
4582
|
function worldToScreenDelta(worldDelta)
|
|
4621
4583
|
{
|
|
4584
|
+
ASSERT(isVector2(worldDelta), 'worldDelta must be a vec2');
|
|
4585
|
+
|
|
4622
4586
|
let x = worldDelta.x;
|
|
4623
4587
|
let y = worldDelta.y;
|
|
4624
4588
|
if (cameraAngle)
|
|
4625
4589
|
{
|
|
4626
4590
|
// apply inverse camera rotation
|
|
4627
4591
|
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
4628
|
-
const
|
|
4629
|
-
|
|
4630
|
-
x = rotatedX;
|
|
4631
|
-
y = rotatedY;
|
|
4592
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4593
|
+
x = xr; y = yr;
|
|
4632
4594
|
}
|
|
4633
4595
|
return new Vector2(x * cameraScale, y * -cameraScale);
|
|
4634
4596
|
}
|
|
@@ -4641,6 +4603,10 @@ function worldToScreenDelta(worldDelta)
|
|
|
4641
4603
|
* @memberof Draw */
|
|
4642
4604
|
function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
|
|
4643
4605
|
{
|
|
4606
|
+
ASSERT(isVector2(screenPos), 'screenPos must be a vec2');
|
|
4607
|
+
ASSERT(isVector2(screenSize), 'screenSize must be a vec2');
|
|
4608
|
+
ASSERT(isNumber(screenAngle), 'screenAngle must be a number');
|
|
4609
|
+
|
|
4644
4610
|
return [
|
|
4645
4611
|
screenToWorld(screenPos),
|
|
4646
4612
|
screenSize.scale(1/cameraScale),
|
|
@@ -4656,6 +4622,10 @@ function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
|
|
|
4656
4622
|
* @memberof Draw */
|
|
4657
4623
|
function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
|
|
4658
4624
|
{
|
|
4625
|
+
ASSERT(isVector2(worldPos), 'worldPos must be a vec2');
|
|
4626
|
+
ASSERT(isVector2(worldSize), 'worldSize must be a vec2');
|
|
4627
|
+
ASSERT(isNumber(worldAngle), 'worldAngle must be a number');
|
|
4628
|
+
|
|
4659
4629
|
return [
|
|
4660
4630
|
worldToScreen(worldPos),
|
|
4661
4631
|
worldSize.scale(cameraScale),
|
|
@@ -4668,8 +4638,8 @@ function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
|
|
|
4668
4638
|
* @memberof Draw */
|
|
4669
4639
|
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
4670
4640
|
|
|
4671
|
-
/** Check if a point or circle is on screen
|
|
4672
|
-
* If size is a Vector2, uses the
|
|
4641
|
+
/** Check if a box, point, or circle is on screen with a circle test
|
|
4642
|
+
* If size is a Vector2, uses the length as diameter
|
|
4673
4643
|
* This can be used to cull offscreen objects from render or update
|
|
4674
4644
|
* @param {Vector2} pos - world space position
|
|
4675
4645
|
* @param {Vector2|number} size - world space size or diameter
|
|
@@ -4677,12 +4647,30 @@ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
|
4677
4647
|
* @memberof Draw */
|
|
4678
4648
|
function isOnScreen(pos, size=0)
|
|
4679
4649
|
{
|
|
4680
|
-
pos
|
|
4650
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
4651
|
+
ASSERT(isVector2(size) || isNumber(size), 'size must be a vec2 or number');
|
|
4652
|
+
|
|
4653
|
+
// optimized circle on screen test
|
|
4654
|
+
// pos = worldToScreen(pos);
|
|
4655
|
+
let x = pos.x - cameraPos.x;
|
|
4656
|
+
let y = pos.y - cameraPos.y;
|
|
4657
|
+
if (cameraAngle)
|
|
4658
|
+
{
|
|
4659
|
+
// apply inverse camera rotation
|
|
4660
|
+
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
4661
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4662
|
+
x = xr; y = yr;
|
|
4663
|
+
}
|
|
4664
|
+
x *= cameraScale*2; y *= -cameraScale*2;
|
|
4665
|
+
|
|
4681
4666
|
if (size instanceof Vector2)
|
|
4682
|
-
size =
|
|
4683
|
-
size *= cameraScale
|
|
4684
|
-
|
|
4685
|
-
|
|
4667
|
+
size = size.length(); // use length of vector as diameter
|
|
4668
|
+
size *= cameraScale;
|
|
4669
|
+
|
|
4670
|
+
// check against screen bounds
|
|
4671
|
+
const w = mainCanvasSize.x, h = mainCanvasSize.y;
|
|
4672
|
+
return x + size > -w && x - size < w &&
|
|
4673
|
+
y + size > -h && y - size < h;
|
|
4686
4674
|
}
|
|
4687
4675
|
|
|
4688
4676
|
/** Enable normal or additive blend mode
|
|
@@ -4696,18 +4684,19 @@ function setBlendMode(additive=false, context)
|
|
|
4696
4684
|
context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
4697
4685
|
}
|
|
4698
4686
|
|
|
4699
|
-
/** Combines
|
|
4700
|
-
* This is necessary for things like
|
|
4687
|
+
/** Combines LittleJS canvases onto the main canvas
|
|
4688
|
+
* This is necessary for things like screenshots and video
|
|
4701
4689
|
* @memberof Draw */
|
|
4702
4690
|
function combineCanvases()
|
|
4703
4691
|
{
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4692
|
+
const w = mainCanvasSize.x, h = mainCanvasSize.y;
|
|
4693
|
+
workCanvas.width = w;
|
|
4694
|
+
workCanvas.height = h;
|
|
4695
|
+
workContext.fillRect(0,0,w,h); // remove background alpha
|
|
4696
|
+
glCopyToContext(workContext);
|
|
4697
|
+
workContext.drawImage(mainCanvas, 0, 0);
|
|
4698
|
+
mainCanvas.width |= 0;
|
|
4699
|
+
mainContext.drawImage(workCanvas, 0, 0);
|
|
4711
4700
|
}
|
|
4712
4701
|
|
|
4713
4702
|
/** Helper function to draw an image with color and additive color applied
|
|
@@ -4724,18 +4713,18 @@ function combineCanvases()
|
|
|
4724
4713
|
* @param {number} dHeight
|
|
4725
4714
|
* @param {Color} color
|
|
4726
4715
|
* @param {Color} [additiveColor]
|
|
4727
|
-
* @param {number} [
|
|
4716
|
+
* @param {number} [bleed] - How many pixels to shrink the source, used to fix bleeding
|
|
4728
4717
|
* @memberof Draw */
|
|
4729
|
-
function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, color, additiveColor,
|
|
4718
|
+
function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, color, additiveColor, bleed=0)
|
|
4730
4719
|
{
|
|
4731
4720
|
function isWhite(c) { return c.r >= 1 && c.g >= 1 && c.b >= 1; }
|
|
4732
4721
|
function isBlack(c) { return c.r <= 0 && c.g <= 0 && c.b <= 0 && c.a <= 0; }
|
|
4733
|
-
const sx2 =
|
|
4734
|
-
const sy2 =
|
|
4722
|
+
const sx2 = bleed;
|
|
4723
|
+
const sy2 = bleed;
|
|
4735
4724
|
sWidth = max(1,sWidth|0);
|
|
4736
4725
|
sHeight = max(1,sHeight|0);
|
|
4737
|
-
const sWidth2 = sWidth - 2*
|
|
4738
|
-
const sHeight2 = sHeight - 2*
|
|
4726
|
+
const sWidth2 = sWidth - 2*bleed;
|
|
4727
|
+
const sHeight2 = sHeight - 2*bleed;
|
|
4739
4728
|
if (!canvasColorTiles || (additiveColor ? isWhite(color.add(additiveColor)) && additiveColor.a <= 0 : isWhite(color)))
|
|
4740
4729
|
{
|
|
4741
4730
|
// white texture with no additive alpha, no need to tint
|
|
@@ -4746,12 +4735,12 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4746
4735
|
else
|
|
4747
4736
|
{
|
|
4748
4737
|
// copy to offscreen canvas
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4738
|
+
workReadCanvas.width = sWidth;
|
|
4739
|
+
workReadCanvas.height = sHeight;
|
|
4740
|
+
workReadContext.drawImage(image, sx|0, sy|0, sWidth, sHeight, 0, 0, sWidth, sHeight);
|
|
4752
4741
|
|
|
4753
4742
|
// tint image using offscreen work context
|
|
4754
|
-
const imageData =
|
|
4743
|
+
const imageData = workReadContext.getImageData(0, 0, sWidth, sHeight);
|
|
4755
4744
|
const data = imageData.data;
|
|
4756
4745
|
if (additiveColor && !isBlack(additiveColor))
|
|
4757
4746
|
{
|
|
@@ -4760,8 +4749,8 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4760
4749
|
const colorAdd = [additiveColor.r * 255, additiveColor.g * 255, additiveColor.b * 255, additiveColor.a * 255];
|
|
4761
4750
|
for (let i = 0; i < data.length; ++i)
|
|
4762
4751
|
data[i] = data[i] * colorMultiply[i&3] + colorAdd[i&3] |0;
|
|
4763
|
-
|
|
4764
|
-
context.drawImage(
|
|
4752
|
+
workReadContext.putImageData(imageData, 0, 0);
|
|
4753
|
+
context.drawImage(workReadCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
4765
4754
|
}
|
|
4766
4755
|
else
|
|
4767
4756
|
{
|
|
@@ -4772,9 +4761,9 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4772
4761
|
data[i+1] *= color.g;
|
|
4773
4762
|
data[i+2] *= color.b;
|
|
4774
4763
|
}
|
|
4775
|
-
|
|
4764
|
+
workReadContext.putImageData(imageData, 0, 0);
|
|
4776
4765
|
context.globalAlpha = color.a;
|
|
4777
|
-
context.drawImage(
|
|
4766
|
+
context.drawImage(workReadCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
4778
4767
|
context.globalAlpha = 1;
|
|
4779
4768
|
}
|
|
4780
4769
|
}
|
|
@@ -4836,7 +4825,7 @@ class FontImage
|
|
|
4836
4825
|
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1))
|
|
4837
4826
|
{
|
|
4838
4827
|
// load default font image
|
|
4839
|
-
if (!engineFontImage)
|
|
4828
|
+
if (!image && !engineFontImage)
|
|
4840
4829
|
{
|
|
4841
4830
|
engineFontImage = new Image;
|
|
4842
4831
|
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
@@ -4859,23 +4848,14 @@ class FontImage
|
|
|
4859
4848
|
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center, context);
|
|
4860
4849
|
}
|
|
4861
4850
|
|
|
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
|
|
4851
|
+
/** Draw text in screen space using the image font
|
|
4872
4852
|
* @param {string|number} text
|
|
4873
4853
|
* @param {Vector2} pos
|
|
4874
4854
|
* @param {number} [scale]
|
|
4875
4855
|
* @param {boolean} [center]
|
|
4876
4856
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4877
4857
|
*/
|
|
4878
|
-
drawTextScreen(text, pos, scale=4, center=true, context=
|
|
4858
|
+
drawTextScreen(text, pos, scale=4, center=true, context=mainContext)
|
|
4879
4859
|
{
|
|
4880
4860
|
context.save();
|
|
4881
4861
|
const size = this.tileSize;
|
|
@@ -5625,7 +5605,7 @@ function inputRender()
|
|
|
5625
5605
|
return;
|
|
5626
5606
|
|
|
5627
5607
|
// setup the canvas
|
|
5628
|
-
const context =
|
|
5608
|
+
const context = mainContext;
|
|
5629
5609
|
context.save();
|
|
5630
5610
|
context.globalAlpha = alpha*touchGamepadAlpha;
|
|
5631
5611
|
context.strokeStyle = '#fff';
|
|
@@ -5867,12 +5847,12 @@ class Sound
|
|
|
5867
5847
|
///////////////////////////////////////////////////////////////////////////////
|
|
5868
5848
|
|
|
5869
5849
|
/**
|
|
5870
|
-
* Sound Wave Object -
|
|
5871
|
-
* - this can be used to play wave, mp3, and ogg files
|
|
5850
|
+
* Sound Wave Object - Loads and stores an audio file for later use
|
|
5851
|
+
* - this can be used to load and play wave, mp3, and ogg files
|
|
5872
5852
|
* @extends Sound
|
|
5873
5853
|
* @memberof Audio
|
|
5874
5854
|
* @example
|
|
5875
|
-
* //
|
|
5855
|
+
* // load an audio asset file
|
|
5876
5856
|
* const sound_example = new SoundWave('sound.mp3');
|
|
5877
5857
|
*
|
|
5878
5858
|
* // play the sound
|
|
@@ -6824,8 +6804,8 @@ class TileLayer extends CanvasLayer
|
|
|
6824
6804
|
if (!this.context) return;
|
|
6825
6805
|
|
|
6826
6806
|
// save current render settings
|
|
6827
|
-
/** @type {[HTMLCanvasElement|OffscreenCanvas, CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
6828
|
-
this.savedRenderSettings = [drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale];
|
|
6807
|
+
/** @type {[HTMLCanvasElement|OffscreenCanvas, CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number, Color]} */
|
|
6808
|
+
this.savedRenderSettings = [drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor];
|
|
6829
6809
|
|
|
6830
6810
|
// set the draw canvas and context to this layer
|
|
6831
6811
|
// use camera settings to match this layer's canvas
|
|
@@ -6834,6 +6814,7 @@ class TileLayer extends CanvasLayer
|
|
|
6834
6814
|
cameraPos = this.size.scale(.5);
|
|
6835
6815
|
const tileSize = this.tileInfo ? this.tileInfo.size : vec2(1);
|
|
6836
6816
|
cameraScale = tileSize.x;
|
|
6817
|
+
canvasClearColor = CLEAR_BLACK;
|
|
6837
6818
|
mainCanvasSize = this.size.multiply(tileSize);
|
|
6838
6819
|
if (clear)
|
|
6839
6820
|
{
|
|
@@ -6843,7 +6824,7 @@ class TileLayer extends CanvasLayer
|
|
|
6843
6824
|
}
|
|
6844
6825
|
|
|
6845
6826
|
// disable smoothing for pixel art
|
|
6846
|
-
|
|
6827
|
+
drawContext.imageSmoothingEnabled = !tilesPixelated;
|
|
6847
6828
|
|
|
6848
6829
|
// setup gl rendering if enabled
|
|
6849
6830
|
glPreRender();
|
|
@@ -6859,7 +6840,7 @@ class TileLayer extends CanvasLayer
|
|
|
6859
6840
|
//debugSaveCanvas(this.canvas);
|
|
6860
6841
|
|
|
6861
6842
|
// set stuff back to normal
|
|
6862
|
-
[drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale] = this.savedRenderSettings;
|
|
6843
|
+
[drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor] = this.savedRenderSettings;
|
|
6863
6844
|
}
|
|
6864
6845
|
|
|
6865
6846
|
/** Draw the tile at a given position in the tile grid
|
|
@@ -7572,10 +7553,10 @@ class Medal
|
|
|
7572
7553
|
*/
|
|
7573
7554
|
render(hidePercent=0)
|
|
7574
7555
|
{
|
|
7575
|
-
const context =
|
|
7556
|
+
const context = mainContext;
|
|
7576
7557
|
const width = min(medalDisplaySize.x, mainCanvas.width);
|
|
7577
7558
|
const height = medalDisplaySize.y;
|
|
7578
|
-
const x =
|
|
7559
|
+
const x = mainCanvas.width - width;
|
|
7579
7560
|
const y = -height*hidePercent;
|
|
7580
7561
|
const backgroundColor = hsl(0,0,.9);
|
|
7581
7562
|
|
|
@@ -7616,7 +7597,7 @@ class Medal
|
|
|
7616
7597
|
{
|
|
7617
7598
|
// draw the image or icon
|
|
7618
7599
|
if (this.image)
|
|
7619
|
-
|
|
7600
|
+
mainContext.drawImage(this.image, pos.x-size/2, pos.y-size/2, size, size);
|
|
7620
7601
|
else
|
|
7621
7602
|
drawTextScreen(this.icon, pos, size*.7, BLACK);
|
|
7622
7603
|
}
|
|
@@ -7637,7 +7618,7 @@ class Medal
|
|
|
7637
7618
|
* @namespace WebGL
|
|
7638
7619
|
*/
|
|
7639
7620
|
|
|
7640
|
-
/** The WebGL canvas which appears
|
|
7621
|
+
/** The WebGL canvas which appears below the main canvas
|
|
7641
7622
|
* @type {HTMLCanvasElement}
|
|
7642
7623
|
* @memberof WebGL */
|
|
7643
7624
|
let glCanvas;
|
|
@@ -7667,7 +7648,7 @@ const gl_MAX_POLY_VERTEXES = gl_ARRAY_BUFFER_SIZE / gl_POLY_VERTEX_BYTE_STRIDE |
|
|
|
7667
7648
|
///////////////////////////////////////////////////////////////////////////////
|
|
7668
7649
|
|
|
7669
7650
|
// Initialize WebGL, called automatically by the engine
|
|
7670
|
-
function glInit()
|
|
7651
|
+
function glInit(rootElement)
|
|
7671
7652
|
{
|
|
7672
7653
|
// keep set of texture infos so they can be restored if context is lost
|
|
7673
7654
|
glTextureInfos = new Set;
|
|
@@ -7691,8 +7672,7 @@ function glInit()
|
|
|
7691
7672
|
return;
|
|
7692
7673
|
}
|
|
7693
7674
|
|
|
7694
|
-
// attach the WebGL canvas
|
|
7695
|
-
const rootElement = mainCanvas.parentElement;
|
|
7675
|
+
// attach the WebGL canvas;
|
|
7696
7676
|
rootElement.appendChild(glCanvas);
|
|
7697
7677
|
|
|
7698
7678
|
// startup webgl
|
|
@@ -7910,6 +7890,9 @@ function glClearCanvas()
|
|
|
7910
7890
|
glCanvas.width = drawCanvas.width;
|
|
7911
7891
|
glCanvas.height = drawCanvas.height;
|
|
7912
7892
|
glContext.viewport(0, 0, glCanvas.width, glCanvas.height);
|
|
7893
|
+
const color = canvasClearColor;
|
|
7894
|
+
if (color.a > 0)
|
|
7895
|
+
glContext.clearColor(color.r, color.g, color.b, color.a);
|
|
7913
7896
|
glContext.clear(glContext.COLOR_BUFFER_BIT);
|
|
7914
7897
|
}
|
|
7915
7898
|
|
|
@@ -8139,7 +8122,6 @@ function glDraw(x, y, sizeX, sizeY, angle=0, uv0X=0, uv0Y=0, uv1X=1, uv1Y=1, rgb
|
|
|
8139
8122
|
glFlush();
|
|
8140
8123
|
glSetInstancedMode();
|
|
8141
8124
|
|
|
8142
|
-
glPolyMode = false;
|
|
8143
8125
|
let offset = glBatchCount++ * gl_INDICES_PER_INSTANCE;
|
|
8144
8126
|
glPositionData[offset++] = x;
|
|
8145
8127
|
glPositionData[offset++] = y;
|
|
@@ -8660,13 +8642,12 @@ class PostProcessPlugin
|
|
|
8660
8642
|
{
|
|
8661
8643
|
/** Create global post processing shader
|
|
8662
8644
|
* @param {string} shaderCode
|
|
8663
|
-
* @param {boolean} [includeOverlay]
|
|
8664
8645
|
* @param {boolean} [includeMainCanvas]
|
|
8665
8646
|
* @example
|
|
8666
8647
|
* // create the post process plugin object
|
|
8667
8648
|
* new PostProcessPlugin(shaderCode);
|
|
8668
8649
|
*/
|
|
8669
|
-
constructor(shaderCode,
|
|
8650
|
+
constructor(shaderCode, includeMainCanvas=true)
|
|
8670
8651
|
{
|
|
8671
8652
|
ASSERT(!postProcess, 'Post process already initialized');
|
|
8672
8653
|
postProcess = this;
|
|
@@ -8738,17 +8719,19 @@ class PostProcessPlugin
|
|
|
8738
8719
|
// clear out the buffer
|
|
8739
8720
|
glFlush();
|
|
8740
8721
|
|
|
8741
|
-
|
|
8722
|
+
// setup texture
|
|
8723
|
+
glContext.activeTexture(glContext.TEXTURE0);
|
|
8724
|
+
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
8725
|
+
if (includeMainCanvas)
|
|
8742
8726
|
{
|
|
8743
|
-
// copy
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
}
|
|
8727
|
+
// copy main canvas to work canvas
|
|
8728
|
+
workCanvas.width = mainCanvasSize.x;
|
|
8729
|
+
workCanvas.height = mainCanvasSize.y;
|
|
8730
|
+
glCopyToContext(workContext);
|
|
8731
|
+
workContext.drawImage(mainCanvas, 0, 0);
|
|
8732
|
+
|
|
8733
|
+
// copy work canvas to texture
|
|
8734
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, workCanvas);
|
|
8752
8735
|
}
|
|
8753
8736
|
|
|
8754
8737
|
// setup shader program to draw a quad
|
|
@@ -8757,14 +8740,6 @@ class PostProcessPlugin
|
|
|
8757
8740
|
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, 1);
|
|
8758
8741
|
glContext.disable(glContext.BLEND);
|
|
8759
8742
|
|
|
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
8743
|
// set vertex position attribute
|
|
8769
8744
|
const vertexByteStride = 8;
|
|
8770
8745
|
const pLocation = glContext.getAttribLocation(postProcess.shader, 'p');
|
|
@@ -8993,12 +8968,14 @@ class UISystemPlugin
|
|
|
8993
8968
|
* // create the ui plugin object
|
|
8994
8969
|
* new UISystemPlugin;
|
|
8995
8970
|
*/
|
|
8996
|
-
constructor(context=
|
|
8971
|
+
constructor(context=mainContext)
|
|
8997
8972
|
{
|
|
8998
8973
|
ASSERT(!uiSystem, 'UI system already initialized');
|
|
8999
8974
|
uiSystem = this;
|
|
9000
8975
|
|
|
9001
8976
|
// default settings
|
|
8977
|
+
/** @property {boolean} - Activate when mouse is pressed down instead of clicked */
|
|
8978
|
+
this.activateOnPress = false;
|
|
9002
8979
|
/** @property {Color} - Default fill color for UI elements */
|
|
9003
8980
|
this.defaultColor = WHITE;
|
|
9004
8981
|
/** @property {Color} - Default outline color for UI elements */
|
|
@@ -9650,13 +9627,15 @@ class UIObject
|
|
|
9650
9627
|
uiSystem.uiObjects.push(this);
|
|
9651
9628
|
}
|
|
9652
9629
|
|
|
9653
|
-
/** Add a child UIObject to this object
|
|
9654
|
-
* @param {UIObject} child
|
|
9630
|
+
/** Add a child UIObject to this object, returns child for chaining
|
|
9631
|
+
* @param {UIObject} child
|
|
9632
|
+
* @return {UIObject} The child object added */
|
|
9655
9633
|
addChild(child)
|
|
9656
9634
|
{
|
|
9657
9635
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
9658
9636
|
this.children.push(child);
|
|
9659
9637
|
child.parent = this;
|
|
9638
|
+
return child;
|
|
9660
9639
|
}
|
|
9661
9640
|
|
|
9662
9641
|
/** Remove a child UIObject from this object
|
|
@@ -9729,8 +9708,16 @@ class UIObject
|
|
|
9729
9708
|
if (uiSystem.activeObject && !isActive)
|
|
9730
9709
|
uiSystem.activeObject.onRelease();
|
|
9731
9710
|
uiSystem.activeObject = this;
|
|
9711
|
+
|
|
9712
|
+
if (uiSystem.activateOnPress)
|
|
9713
|
+
{
|
|
9714
|
+
this.onClick();
|
|
9715
|
+
if (!this.soundPress && this.soundClick)
|
|
9716
|
+
this.soundClick.play();
|
|
9717
|
+
}
|
|
9732
9718
|
}
|
|
9733
9719
|
}
|
|
9720
|
+
if (!uiSystem.activateOnPress)
|
|
9734
9721
|
if (!mouseDown && this.isActiveObject() && this.interactive)
|
|
9735
9722
|
{
|
|
9736
9723
|
this.onClick();
|
|
@@ -12170,33 +12157,33 @@ async function box2dInit()
|
|
|
12170
12157
|
color = getDebugColor(color);
|
|
12171
12158
|
point1 = box2d.vec2FromPointer(point1);
|
|
12172
12159
|
point2 = box2d.vec2FromPointer(point2);
|
|
12173
|
-
drawLine(point1, point2, debugLineWidth, color, vec2(), 0, false
|
|
12160
|
+
drawLine(point1, point2, debugLineWidth, color, vec2(), 0, false);
|
|
12174
12161
|
};
|
|
12175
12162
|
debugDraw.DrawPolygon = function(vertices, vertexCount, color)
|
|
12176
12163
|
{
|
|
12177
12164
|
color = getDebugColor(color);
|
|
12178
12165
|
const points = getPointsList(vertices, vertexCount);
|
|
12179
|
-
drawPoly(points, CLEAR_WHITE, debugLineWidth, color, vec2(), 0, false
|
|
12166
|
+
drawPoly(points, CLEAR_WHITE, debugLineWidth, color, vec2(), 0, false);
|
|
12180
12167
|
};
|
|
12181
12168
|
debugDraw.DrawSolidPolygon = function(vertices, vertexCount, color)
|
|
12182
12169
|
{
|
|
12183
12170
|
color = getDebugColor(color);
|
|
12184
12171
|
const points = getPointsList(vertices, vertexCount);
|
|
12185
|
-
drawPoly(points, color, 0, color, vec2(), 0, false
|
|
12172
|
+
drawPoly(points, color, 0, color, vec2(), 0, false);
|
|
12186
12173
|
};
|
|
12187
12174
|
debugDraw.DrawCircle = function(center, radius, color)
|
|
12188
12175
|
{
|
|
12189
12176
|
color = getDebugColor(color);
|
|
12190
12177
|
center = box2d.vec2FromPointer(center);
|
|
12191
|
-
drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false
|
|
12178
|
+
drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false);
|
|
12192
12179
|
};
|
|
12193
12180
|
debugDraw.DrawSolidCircle = function(center, radius, axis, color)
|
|
12194
12181
|
{
|
|
12195
12182
|
color = getDebugColor(color);
|
|
12196
12183
|
center = box2d.vec2FromPointer(center);
|
|
12197
12184
|
axis = box2d.vec2FromPointer(axis).scale(radius);
|
|
12198
|
-
drawCircle(center, radius*2, color, debugLineWidth, color, false
|
|
12199
|
-
drawLine(vec2(), axis, debugLineWidth, color, center, 0, false
|
|
12185
|
+
drawCircle(center, radius*2, color, debugLineWidth, color, false);
|
|
12186
|
+
drawLine(vec2(), axis, debugLineWidth, color, center, 0, false);
|
|
12200
12187
|
};
|
|
12201
12188
|
debugDraw.DrawTransform = function(transform)
|
|
12202
12189
|
{
|
|
@@ -12205,8 +12192,8 @@ async function box2dInit()
|
|
|
12205
12192
|
const angle = -transform.get_q().GetAngle();
|
|
12206
12193
|
const p1 = vec2(1,0), c1 = rgb(.75,0,0,.8);
|
|
12207
12194
|
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
|
|
12195
|
+
drawLine(vec2(), p1, debugLineWidth, c1, pos, angle, false);
|
|
12196
|
+
drawLine(vec2(), p2, debugLineWidth, c2, pos, angle, false);
|
|
12210
12197
|
}
|
|
12211
12198
|
|
|
12212
12199
|
debugDraw.AppendFlags(box2d.instance.b2Draw.e_shapeBit);
|
|
@@ -12226,8 +12213,8 @@ async function box2dInit()
|
|
|
12226
12213
|
|
|
12227
12214
|
///////////////////////////////////////////////////////////////////////////////
|
|
12228
12215
|
|
|
12229
|
-
/** Draw a scalable nine-slice UI element to the
|
|
12230
|
-
* This function can not apply color because it draws using the
|
|
12216
|
+
/** Draw a scalable nine-slice UI element to the main canvas in screen space
|
|
12217
|
+
* This function can not apply color because it draws using the 2d context
|
|
12231
12218
|
* @param {Vector2} pos - Screen space position
|
|
12232
12219
|
* @param {Vector2} size - Screen space size
|
|
12233
12220
|
* @param {TileInfo} startTile - Starting tile for the nine-slice pattern
|
|
@@ -12237,7 +12224,7 @@ async function box2dInit()
|
|
|
12237
12224
|
* @memberof DrawUtilities */
|
|
12238
12225
|
function drawNineSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
12239
12226
|
{
|
|
12240
|
-
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true
|
|
12227
|
+
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true);
|
|
12241
12228
|
}
|
|
12242
12229
|
|
|
12243
12230
|
/** Draw a scalable nine-slice UI element in world space
|
|
@@ -12286,8 +12273,8 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
12286
12273
|
}
|
|
12287
12274
|
}
|
|
12288
12275
|
|
|
12289
|
-
/** Draw a scalable three-slice UI element to the
|
|
12290
|
-
* This function can not apply color because it draws using the
|
|
12276
|
+
/** Draw a scalable three-slice UI element to the main canvas in screen space
|
|
12277
|
+
* This function can not apply color because it draws using the 2d context
|
|
12291
12278
|
* @param {Vector2} pos - Screen space position
|
|
12292
12279
|
* @param {Vector2} size - Screen space size
|
|
12293
12280
|
* @param {TileInfo} startTile - Starting tile for the three-slice pattern
|
|
@@ -12297,7 +12284,7 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
12297
12284
|
* @memberof DrawUtilities */
|
|
12298
12285
|
function drawThreeSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
12299
12286
|
{
|
|
12300
|
-
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true
|
|
12287
|
+
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true);
|
|
12301
12288
|
}
|
|
12302
12289
|
|
|
12303
12290
|
/** Draw a scalable three-slice UI element in world space
|
|
@@ -12377,7 +12364,7 @@ export
|
|
|
12377
12364
|
// Globals
|
|
12378
12365
|
debug,
|
|
12379
12366
|
debugOverlay,
|
|
12380
|
-
|
|
12367
|
+
debugWatermark,
|
|
12381
12368
|
|
|
12382
12369
|
// Debug
|
|
12383
12370
|
ASSERT,
|
|
@@ -12410,13 +12397,13 @@ export
|
|
|
12410
12397
|
canvasMaxAspect,
|
|
12411
12398
|
canvasFixedSize,
|
|
12412
12399
|
canvasPixelated,
|
|
12413
|
-
overlayCanvasPixelated,
|
|
12414
12400
|
tilesPixelated,
|
|
12415
12401
|
fontDefault,
|
|
12416
12402
|
showSplashScreen,
|
|
12417
12403
|
headlessMode,
|
|
12418
|
-
|
|
12419
|
-
|
|
12404
|
+
tileDefaultSize,
|
|
12405
|
+
tileDefaultPadding,
|
|
12406
|
+
tileDefaultBleed,
|
|
12420
12407
|
enablePhysicsSolver,
|
|
12421
12408
|
objectDefaultMass,
|
|
12422
12409
|
objectDefaultDamping,
|
|
@@ -12455,14 +12442,14 @@ export
|
|
|
12455
12442
|
setCanvasMaxAspect,
|
|
12456
12443
|
setCanvasFixedSize,
|
|
12457
12444
|
setCanvasPixelated,
|
|
12458
|
-
setOverlayCanvasPixelated,
|
|
12459
12445
|
setTilesPixelated,
|
|
12460
12446
|
setFontDefault,
|
|
12461
12447
|
setShowSplashScreen,
|
|
12462
12448
|
setHeadlessMode,
|
|
12463
12449
|
setGLEnable,
|
|
12464
|
-
|
|
12465
|
-
|
|
12450
|
+
setTileDefaultSize,
|
|
12451
|
+
setTileDefaultPadding,
|
|
12452
|
+
setTileDefaultBleed,
|
|
12466
12453
|
setEnablePhysicsSolver,
|
|
12467
12454
|
setObjectDefaultMass,
|
|
12468
12455
|
setObjectDefaultDamping,
|
|
@@ -12491,7 +12478,7 @@ export
|
|
|
12491
12478
|
setMedalDisplaySlideTime,
|
|
12492
12479
|
setMedalDisplaySize,
|
|
12493
12480
|
setMedalsPreventUnlock,
|
|
12494
|
-
|
|
12481
|
+
setDebugWatermark,
|
|
12495
12482
|
setDebugKey,
|
|
12496
12483
|
|
|
12497
12484
|
// Utilities
|
|
@@ -12571,8 +12558,10 @@ export
|
|
|
12571
12558
|
mainContext,
|
|
12572
12559
|
drawCanvas,
|
|
12573
12560
|
drawContext,
|
|
12574
|
-
|
|
12575
|
-
|
|
12561
|
+
workCanvas,
|
|
12562
|
+
workContext,
|
|
12563
|
+
workReadCanvas,
|
|
12564
|
+
workReadContext,
|
|
12576
12565
|
mainCanvasSize,
|
|
12577
12566
|
textureInfos,
|
|
12578
12567
|
drawCount,
|
|
@@ -12590,7 +12579,6 @@ export
|
|
|
12590
12579
|
drawCircle,
|
|
12591
12580
|
drawCanvas2D,
|
|
12592
12581
|
drawText,
|
|
12593
|
-
drawTextOverlay,
|
|
12594
12582
|
drawTextScreen,
|
|
12595
12583
|
setBlendMode,
|
|
12596
12584
|
combineCanvases,
|