littlejsengine 1.15.8 → 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 +87 -85
- package/dist/littlejs.esm.js +578 -532
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +562 -521
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +485 -444
- package/examples/box2d/game.js +2 -2
- 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 +11 -4
- package/examples/puzzle/game.js +2 -2
- package/examples/shorts/base.html +3 -4
- 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/shorts/uiSystem.js +1 -1
- package/examples/starter/game.js +1 -2
- package/examples/starter/index.html +2 -3
- package/examples/stress/index.html +1 -1
- package/examples/typescript/game.js +1 -2
- package/examples/typescript/game.ts +1 -2
- package/examples/uiSystem/game.js +4 -7
- 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 +240 -53
- package/src/engineAudio.js +3 -3
- package/src/engineBuild.js +0 -1
- package/src/engineDebug.js +78 -78
- package/src/engineDraw.js +107 -109
- package/src/engineExport.js +16 -11
- package/src/engineInput.js +14 -2
- package/src/engineMedals.js +3 -3
- package/src/engineObject.js +4 -2
- package/src/engineParticles.js +8 -6
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +44 -30
- package/src/engineTileLayer.js +6 -6
- package/src/engineUtilities.js +2 -10
- package/src/engineWebGL.js +6 -5
- package/src/engineSplash.js +0 -173
package/dist/littlejs.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
|
}
|
|
@@ -308,42 +309,60 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
308
309
|
|
|
309
310
|
if (canvasFixedSize.x)
|
|
310
311
|
{
|
|
311
|
-
//
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
312
|
+
// set canvas fixed size
|
|
313
|
+
mainCanvasSize = canvasFixedSize.copy();
|
|
314
|
+
|
|
315
|
+
// fit to window using css width and height
|
|
316
|
+
const innerAspect = innerWidth / innerHeight;
|
|
317
|
+
const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
|
|
318
|
+
const w = innerAspect < fixedAspect ? '100%' : '';
|
|
319
|
+
const h = innerAspect < fixedAspect ? '' : '100%';
|
|
320
|
+
mainCanvas.style.width = w;
|
|
321
|
+
mainCanvas.style.height = h;
|
|
322
|
+
if (glCanvas)
|
|
323
|
+
{
|
|
324
|
+
glCanvas.style.width = w;
|
|
325
|
+
glCanvas.style.height = h;
|
|
326
|
+
}
|
|
320
327
|
}
|
|
321
328
|
else
|
|
322
329
|
{
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
|
|
330
|
+
// get main canvas size based on window size
|
|
331
|
+
mainCanvasSize.x = min(innerWidth, canvasMaxSize.x);
|
|
332
|
+
mainCanvasSize.y = min(innerHeight, canvasMaxSize.y);
|
|
333
|
+
|
|
334
|
+
// responsive aspect ratio with native resolution
|
|
335
|
+
const innerAspect = innerWidth / innerHeight;
|
|
336
|
+
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
337
|
+
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
338
|
+
{
|
|
339
|
+
// full height
|
|
340
|
+
const w = mainCanvasSize.y * canvasMaxAspect | 0;
|
|
341
|
+
mainCanvasSize.x = min(w, canvasMaxSize.x);
|
|
342
|
+
}
|
|
343
|
+
else if (innerAspect < canvasMinAspect)
|
|
344
|
+
{
|
|
345
|
+
// full width
|
|
346
|
+
const h = mainCanvasSize.x / canvasMinAspect | 0;
|
|
347
|
+
mainCanvasSize.y = min(h, canvasMaxSize.y);
|
|
348
|
+
}
|
|
326
349
|
}
|
|
327
350
|
|
|
351
|
+
// clear main canvas and set size
|
|
352
|
+
mainCanvas.width = mainCanvasSize.x;
|
|
353
|
+
mainCanvas.height = mainCanvasSize.y;
|
|
354
|
+
|
|
328
355
|
// apply the clear color to main canvas
|
|
329
|
-
if (canvasClearColor.a > 0)
|
|
356
|
+
if (canvasClearColor.a > 0 && !glEnable)
|
|
330
357
|
{
|
|
331
358
|
mainContext.fillStyle = canvasClearColor.toString();
|
|
332
359
|
mainContext.fillRect(0, 0, mainCanvasSize.x, mainCanvasSize.y);
|
|
333
360
|
mainContext.fillStyle = BLACK.toString();
|
|
334
361
|
}
|
|
335
362
|
|
|
336
|
-
// clear overlay canvas and set size
|
|
337
|
-
overlayCanvas.width = mainCanvas.width;
|
|
338
|
-
overlayCanvas.height = mainCanvas.height;
|
|
339
|
-
|
|
340
|
-
// save canvas size
|
|
341
|
-
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
342
|
-
|
|
343
363
|
// set default line join and cap
|
|
344
|
-
|
|
345
|
-
mainContext.
|
|
346
|
-
mainContext.lineCap = overlayContext.lineCap = lineCap;
|
|
364
|
+
mainContext.lineJoin = 'round';
|
|
365
|
+
mainContext.lineCap = 'round';
|
|
347
366
|
}
|
|
348
367
|
|
|
349
368
|
// wait for gameInit to load
|
|
@@ -355,6 +374,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
355
374
|
if (headlessMode)
|
|
356
375
|
return startEngine();
|
|
357
376
|
|
|
377
|
+
// setup webgl
|
|
378
|
+
glInit(rootElement);
|
|
379
|
+
|
|
358
380
|
// setup html
|
|
359
381
|
const styleRoot =
|
|
360
382
|
'margin:0;' + // fill the window
|
|
@@ -363,37 +385,32 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
363
385
|
'user-select:none;' + // prevent hold to select
|
|
364
386
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
365
387
|
'touch-action:none;' + // prevent mobile pinch to resize
|
|
366
|
-
'-webkit-touch-callout:none'
|
|
388
|
+
'-webkit-touch-callout:none'; // compatibility for ios
|
|
367
389
|
rootElement.style.cssText = styleRoot;
|
|
368
|
-
drawCanvas = mainCanvas = document.createElement('canvas');
|
|
369
|
-
rootElement.appendChild(mainCanvas);
|
|
390
|
+
drawCanvas = mainCanvas = rootElement.appendChild(document.createElement('canvas'));
|
|
370
391
|
drawContext = mainContext = mainCanvas.getContext('2d');
|
|
371
392
|
|
|
372
393
|
// init stuff and start engine
|
|
373
394
|
inputInit();
|
|
374
395
|
audioInit();
|
|
375
396
|
debugInit();
|
|
376
|
-
glInit();
|
|
377
397
|
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
rootElement.appendChild(overlayCanvas);
|
|
381
|
-
overlayContext = overlayCanvas.getContext('2d');
|
|
382
|
-
|
|
383
|
-
// set canvases
|
|
398
|
+
// setup canvases
|
|
399
|
+
// transform way is still more reliable then flexbox or grid
|
|
384
400
|
const styleCanvas = 'position:absolute;'+ // allow canvases to overlap
|
|
385
401
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center on screen
|
|
386
|
-
mainCanvas.style.cssText =
|
|
402
|
+
mainCanvas.style.cssText = styleCanvas;
|
|
387
403
|
if (glCanvas)
|
|
388
404
|
glCanvas.style.cssText = styleCanvas;
|
|
389
405
|
setCanvasPixelated(canvasPixelated);
|
|
390
|
-
setOverlayCanvasPixelated(overlayCanvasPixelated);
|
|
391
406
|
updateCanvas();
|
|
392
407
|
glPreRender();
|
|
393
408
|
|
|
394
|
-
// create offscreen
|
|
395
|
-
workCanvas = new OffscreenCanvas(
|
|
396
|
-
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 });
|
|
397
414
|
|
|
398
415
|
// create promises for loading images
|
|
399
416
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -444,9 +461,176 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
444
461
|
// wait for all the promises to finish
|
|
445
462
|
await Promise.all(promises);
|
|
446
463
|
return startEngine();
|
|
464
|
+
|
|
465
|
+
///////////////////////////////////////////////////////////////////////////
|
|
466
|
+
// LittleJS Splash Screen
|
|
467
|
+
function drawEngineSplashScreen(t)
|
|
468
|
+
{
|
|
469
|
+
const x = mainContext;
|
|
470
|
+
const w = mainCanvas.width = innerWidth;
|
|
471
|
+
const h = mainCanvas.height = innerHeight;
|
|
472
|
+
|
|
473
|
+
{
|
|
474
|
+
// background
|
|
475
|
+
const p3 = percent(t, 1, .8);
|
|
476
|
+
const p4 = percent(t, 0, .5);
|
|
477
|
+
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
478
|
+
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
479
|
+
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
480
|
+
x.save();
|
|
481
|
+
x.fillStyle = g;
|
|
482
|
+
x.fillRect(0,0,w,h);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// draw LittleJS logo...
|
|
486
|
+
const rect = (X, Y, W, H, C)=>
|
|
487
|
+
{
|
|
488
|
+
x.beginPath();
|
|
489
|
+
x.rect(X,Y,W,C?H*p:H);
|
|
490
|
+
x.fillStyle = C;
|
|
491
|
+
C ? x.fill() : x.stroke();
|
|
492
|
+
};
|
|
493
|
+
const line = (X, Y, Z, W)=>
|
|
494
|
+
{
|
|
495
|
+
x.beginPath();
|
|
496
|
+
x.lineTo(X,Y);
|
|
497
|
+
x.lineTo(Z,W);
|
|
498
|
+
x.stroke();
|
|
499
|
+
};
|
|
500
|
+
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
501
|
+
{
|
|
502
|
+
const D = (A+B)/2, E = p*(B-A)/2;
|
|
503
|
+
x.beginPath();
|
|
504
|
+
F && x.lineTo(X,Y);
|
|
505
|
+
x.arc(X,Y,R,D-E,D+E);
|
|
506
|
+
x.fillStyle = C;
|
|
507
|
+
C ? x.fill() : x.stroke();
|
|
508
|
+
};
|
|
509
|
+
const color = (c=0, l=0) =>
|
|
510
|
+
hsl([.98,.3,.57,.14][c%4],.8,[0,.3,.5,.8,.9][l]).toString();
|
|
511
|
+
const alpha = wave(1,1,t);
|
|
512
|
+
const p = percent(alpha, .1, .5);
|
|
513
|
+
|
|
514
|
+
// setup
|
|
515
|
+
x.translate(w/2,h/2);
|
|
516
|
+
const size = min(6, min(w,h)/99); // fit to screen
|
|
517
|
+
x.scale(size,size);
|
|
518
|
+
x.translate(-40,-35);
|
|
519
|
+
x.lineJoin = x.lineCap = 'round';
|
|
520
|
+
x.lineWidth = .1 + p*1.9;
|
|
521
|
+
|
|
522
|
+
// drawing effect
|
|
523
|
+
const p2 = percent(alpha,.1,1);
|
|
524
|
+
x.setLineDash([99*p2,99]);
|
|
525
|
+
|
|
526
|
+
// cab top
|
|
527
|
+
rect(7,16,18,-8,color(2,2));
|
|
528
|
+
rect(7,8,18,4,color(2,3));
|
|
529
|
+
rect(25,8,8,8,color(2,1));
|
|
530
|
+
rect(25,8,-18,8);
|
|
531
|
+
rect(25,8,8,8);
|
|
532
|
+
|
|
533
|
+
// cab
|
|
534
|
+
rect(25,16,7,23,color());
|
|
535
|
+
rect(11,39,14,-23,color(1,1));
|
|
536
|
+
rect(11,16,14,18,color(1,2));
|
|
537
|
+
rect(11,16,14,8,color(1,3));
|
|
538
|
+
rect(25,16,-14,24);
|
|
539
|
+
|
|
540
|
+
// cab window
|
|
541
|
+
rect(15,29,6,-9,color(2,2));
|
|
542
|
+
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
543
|
+
rect(21,21,-6,9);
|
|
544
|
+
|
|
545
|
+
// little stack
|
|
546
|
+
rect(37,14,9,6,color(3,2));
|
|
547
|
+
rect(37,14,4.5,6,color(3,3));
|
|
548
|
+
rect(37,14,9,6);
|
|
549
|
+
|
|
550
|
+
// big stack
|
|
551
|
+
rect(50,20,10,-8,color(0,1));
|
|
552
|
+
rect(50,20,6.5,-8,color(0,2));
|
|
553
|
+
rect(50,20,3.5,-8,color(0,3));
|
|
554
|
+
rect(50,20,10,-8);
|
|
555
|
+
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
556
|
+
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
557
|
+
circle(55,2,11.4,.5,PI-.5);
|
|
558
|
+
rect(45,7,20,-7,color(0,2));
|
|
559
|
+
rect(45,-1,20,4,color(0,3));
|
|
560
|
+
rect(45,-1,20,8);
|
|
561
|
+
|
|
562
|
+
// engine
|
|
563
|
+
for (let i=5; i--;)
|
|
564
|
+
{
|
|
565
|
+
// stagger radius to fix slight seam
|
|
566
|
+
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
567
|
+
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
568
|
+
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// engine outline
|
|
572
|
+
circle(36,30,10,PI/2,PI*3/2);
|
|
573
|
+
circle(48,30,10,PI/2,PI*3/2);
|
|
574
|
+
circle(60,30,10);
|
|
575
|
+
line(36,20,60,20);
|
|
576
|
+
|
|
577
|
+
// engine front light
|
|
578
|
+
circle(60,30,4,PI,3*PI,color(3,2));
|
|
579
|
+
circle(60,30,4,PI,2*PI,color(3,3));
|
|
580
|
+
circle(60,30,4,PI,3*PI);
|
|
581
|
+
|
|
582
|
+
// front brush
|
|
583
|
+
for (let i=6; i--;)
|
|
584
|
+
{
|
|
585
|
+
x.beginPath();
|
|
586
|
+
x.lineTo(53,54);
|
|
587
|
+
x.lineTo(53,40);
|
|
588
|
+
x.lineTo(53+(1+i*2.9)*p,40);
|
|
589
|
+
x.lineTo(53+(4+i*3.5)*p,54);
|
|
590
|
+
x.fillStyle = color(0,i%2+2);
|
|
591
|
+
x.fill();
|
|
592
|
+
i%2 && x.stroke();
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// wheels
|
|
596
|
+
rect(6,40,5,5);
|
|
597
|
+
rect(6,40,5,5,color());
|
|
598
|
+
rect(15,54,38,-14,color());
|
|
599
|
+
for (let i=3; i--;)
|
|
600
|
+
for (let j=2; j--;)
|
|
601
|
+
{
|
|
602
|
+
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
603
|
+
x.stroke();
|
|
604
|
+
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
605
|
+
x.stroke();
|
|
606
|
+
}
|
|
607
|
+
line(6,40,68,40); // center
|
|
608
|
+
line(77,54,4,54); // bottom
|
|
609
|
+
|
|
610
|
+
// draw engine name
|
|
611
|
+
const s = engineName;
|
|
612
|
+
x.font = '900 16px arial';
|
|
613
|
+
x.textAlign = 'center';
|
|
614
|
+
x.textBaseline = 'top';
|
|
615
|
+
x.lineWidth = .1+p*3.9;
|
|
616
|
+
let w2 = 0;
|
|
617
|
+
for (let i=0; i<s.length; ++i)
|
|
618
|
+
w2 += x.measureText(s[i]).width;
|
|
619
|
+
for (let j=2; j--;)
|
|
620
|
+
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
621
|
+
{
|
|
622
|
+
x.fillStyle = color(i,2);
|
|
623
|
+
const w = x.measureText(s[i]).width;
|
|
624
|
+
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
625
|
+
X += w;
|
|
626
|
+
}
|
|
627
|
+
x.restore();
|
|
628
|
+
}
|
|
629
|
+
///////////////////////////////////////////////////////////////////////////
|
|
447
630
|
}
|
|
448
631
|
|
|
449
632
|
/** Update each engine object, remove destroyed objects, and update time
|
|
633
|
+
* can be called manually if objects need to be updated outside of main loop
|
|
450
634
|
* @memberof Engine */
|
|
451
635
|
function engineObjectsUpdate()
|
|
452
636
|
{
|
|
@@ -498,18 +682,21 @@ function engineObjectsDestroy()
|
|
|
498
682
|
function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
499
683
|
{
|
|
500
684
|
const collectedObjects = [];
|
|
501
|
-
if (!pos)
|
|
685
|
+
if (!pos)
|
|
502
686
|
{
|
|
687
|
+
// all objects
|
|
503
688
|
for (const o of objects)
|
|
504
689
|
collectedObjects.push(o);
|
|
505
690
|
}
|
|
506
|
-
else if (size instanceof Vector2)
|
|
691
|
+
else if (size instanceof Vector2)
|
|
507
692
|
{
|
|
693
|
+
// bounding box test
|
|
508
694
|
for (const o of objects)
|
|
509
695
|
o.isOverlapping(pos, size) && collectedObjects.push(o);
|
|
510
696
|
}
|
|
511
|
-
else
|
|
697
|
+
else
|
|
512
698
|
{
|
|
699
|
+
// circle test
|
|
513
700
|
const sizeSquared = size*size;
|
|
514
701
|
for (const o of objects)
|
|
515
702
|
pos.distanceSquared(o.pos) < sizeSquared && collectedObjects.push(o);
|
|
@@ -552,173 +739,6 @@ function engineObjectsRaycast(start, end, objects=engineObjects)
|
|
|
552
739
|
|
|
553
740
|
debugRaycast && debugLine(start, end, hitObjects.length ? '#f00' : '#00f', .02);
|
|
554
741
|
return hitObjects;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
558
|
-
// LittleJS splash screen and logo
|
|
559
|
-
|
|
560
|
-
function drawEngineSplashScreen(t)
|
|
561
|
-
{
|
|
562
|
-
const x = overlayContext;
|
|
563
|
-
const w = overlayCanvas.width = innerWidth;
|
|
564
|
-
const h = overlayCanvas.height = innerHeight;
|
|
565
|
-
|
|
566
|
-
{
|
|
567
|
-
// background
|
|
568
|
-
const p3 = percent(t, 1, .8);
|
|
569
|
-
const p4 = percent(t, 0, .5);
|
|
570
|
-
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
571
|
-
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
572
|
-
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
573
|
-
x.save();
|
|
574
|
-
x.fillStyle = g;
|
|
575
|
-
x.fillRect(0,0,w,h);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// draw LittleJS logo...
|
|
579
|
-
const rect = (X, Y, W, H, C)=>
|
|
580
|
-
{
|
|
581
|
-
x.beginPath();
|
|
582
|
-
x.rect(X,Y,W,C?H*p:H);
|
|
583
|
-
x.fillStyle = C;
|
|
584
|
-
C ? x.fill() : x.stroke();
|
|
585
|
-
};
|
|
586
|
-
const line = (X, Y, Z, W)=>
|
|
587
|
-
{
|
|
588
|
-
x.beginPath();
|
|
589
|
-
x.lineTo(X,Y);
|
|
590
|
-
x.lineTo(Z,W);
|
|
591
|
-
x.stroke();
|
|
592
|
-
};
|
|
593
|
-
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
594
|
-
{
|
|
595
|
-
const D = (A+B)/2, E = p*(B-A)/2;
|
|
596
|
-
x.beginPath();
|
|
597
|
-
F && x.lineTo(X,Y);
|
|
598
|
-
x.arc(X,Y,R,D-E,D+E);
|
|
599
|
-
x.fillStyle = C;
|
|
600
|
-
C ? x.fill() : x.stroke();
|
|
601
|
-
};
|
|
602
|
-
const color = (c=0, l=0) =>
|
|
603
|
-
hsl([.98,.3,.57,.14][c%4],.8,[0,.3,.5,.8,.9][l]).toString();
|
|
604
|
-
const alpha = wave(1,1,t);
|
|
605
|
-
const p = percent(alpha, .1, .5);
|
|
606
|
-
|
|
607
|
-
// setup
|
|
608
|
-
x.translate(w/2,h/2);
|
|
609
|
-
const size = min(6, min(w,h)/99); // fit to screen
|
|
610
|
-
x.scale(size,size);
|
|
611
|
-
x.translate(-40,-35);
|
|
612
|
-
x.lineJoin = x.lineCap = 'round';
|
|
613
|
-
x.lineWidth = .1 + p*1.9;
|
|
614
|
-
|
|
615
|
-
// drawing effect
|
|
616
|
-
const p2 = percent(alpha,.1,1);
|
|
617
|
-
x.setLineDash([99*p2,99]);
|
|
618
|
-
|
|
619
|
-
// cab top
|
|
620
|
-
rect(7,16,18,-8,color(2,2));
|
|
621
|
-
rect(7,8,18,4,color(2,3));
|
|
622
|
-
rect(25,8,8,8,color(2,1));
|
|
623
|
-
rect(25,8,-18,8);
|
|
624
|
-
rect(25,8,8,8);
|
|
625
|
-
|
|
626
|
-
// cab
|
|
627
|
-
rect(25,16,7,23,color());
|
|
628
|
-
rect(11,39,14,-23,color(1,1));
|
|
629
|
-
rect(11,16,14,18,color(1,2));
|
|
630
|
-
rect(11,16,14,8,color(1,3));
|
|
631
|
-
rect(25,16,-14,24);
|
|
632
|
-
|
|
633
|
-
// cab window
|
|
634
|
-
rect(15,29,6,-9,color(2,2));
|
|
635
|
-
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
636
|
-
rect(21,21,-6,9);
|
|
637
|
-
|
|
638
|
-
// little stack
|
|
639
|
-
rect(37,14,9,6,color(3,2));
|
|
640
|
-
rect(37,14,4.5,6,color(3,3));
|
|
641
|
-
rect(37,14,9,6);
|
|
642
|
-
|
|
643
|
-
// big stack
|
|
644
|
-
rect(50,20,10,-8,color(0,1));
|
|
645
|
-
rect(50,20,6.5,-8,color(0,2));
|
|
646
|
-
rect(50,20,3.5,-8,color(0,3));
|
|
647
|
-
rect(50,20,10,-8);
|
|
648
|
-
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
649
|
-
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
650
|
-
circle(55,2,11.4,.5,PI-.5);
|
|
651
|
-
rect(45,7,20,-7,color(0,2));
|
|
652
|
-
rect(45,-1,20,4,color(0,3));
|
|
653
|
-
rect(45,-1,20,8);
|
|
654
|
-
|
|
655
|
-
// engine
|
|
656
|
-
for (let i=5; i--;)
|
|
657
|
-
{
|
|
658
|
-
// stagger radius to fix slight seam
|
|
659
|
-
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
660
|
-
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
661
|
-
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// engine outline
|
|
665
|
-
circle(36,30,10,PI/2,PI*3/2);
|
|
666
|
-
circle(48,30,10,PI/2,PI*3/2);
|
|
667
|
-
circle(60,30,10);
|
|
668
|
-
line(36,20,60,20);
|
|
669
|
-
|
|
670
|
-
// engine front light
|
|
671
|
-
circle(60,30,4,PI,3*PI,color(3,2));
|
|
672
|
-
circle(60,30,4,PI,2*PI,color(3,3));
|
|
673
|
-
circle(60,30,4,PI,3*PI);
|
|
674
|
-
|
|
675
|
-
// front brush
|
|
676
|
-
for (let i=6; i--;)
|
|
677
|
-
{
|
|
678
|
-
x.beginPath();
|
|
679
|
-
x.lineTo(53,54);
|
|
680
|
-
x.lineTo(53,40);
|
|
681
|
-
x.lineTo(53+(1+i*2.9)*p,40);
|
|
682
|
-
x.lineTo(53+(4+i*3.5)*p,54);
|
|
683
|
-
x.fillStyle = color(0,i%2+2);
|
|
684
|
-
x.fill();
|
|
685
|
-
i%2 && x.stroke();
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// wheels
|
|
689
|
-
rect(6,40,5,5);
|
|
690
|
-
rect(6,40,5,5,color());
|
|
691
|
-
rect(15,54,38,-14,color());
|
|
692
|
-
for (let i=3; i--;)
|
|
693
|
-
for (let j=2; j--;)
|
|
694
|
-
{
|
|
695
|
-
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
696
|
-
x.stroke();
|
|
697
|
-
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
698
|
-
x.stroke();
|
|
699
|
-
}
|
|
700
|
-
line(6,40,68,40); // center
|
|
701
|
-
line(77,54,4,54); // bottom
|
|
702
|
-
|
|
703
|
-
// draw engine name
|
|
704
|
-
const s = engineName;
|
|
705
|
-
x.font = '900 16px arial';
|
|
706
|
-
x.textAlign = 'center';
|
|
707
|
-
x.textBaseline = 'top';
|
|
708
|
-
x.lineWidth = .1+p*3.9;
|
|
709
|
-
let w2 = 0;
|
|
710
|
-
for (let i=0; i<s.length; ++i)
|
|
711
|
-
w2 += x.measureText(s[i]).width;
|
|
712
|
-
for (let j=2; j--;)
|
|
713
|
-
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
714
|
-
{
|
|
715
|
-
x.fillStyle = color(i,2);
|
|
716
|
-
const w = x.measureText(s[i]).width;
|
|
717
|
-
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
718
|
-
X += w;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
x.restore();
|
|
722
742
|
}
|
|
723
743
|
/**
|
|
724
744
|
* LittleJS Debug System
|
|
@@ -746,7 +766,7 @@ const debugPointSize = .5;
|
|
|
746
766
|
* @type {boolean}
|
|
747
767
|
* @default
|
|
748
768
|
* @memberof Debug */
|
|
749
|
-
let
|
|
769
|
+
let debugWatermark = true;
|
|
750
770
|
|
|
751
771
|
/** Key code used to toggle debug mode, Esc by default
|
|
752
772
|
* @type {string}
|
|
@@ -1032,12 +1052,19 @@ function debugUpdate()
|
|
|
1032
1052
|
if (!debug)
|
|
1033
1053
|
return;
|
|
1034
1054
|
|
|
1055
|
+
if (debugVideoCaptureIsActive())
|
|
1056
|
+
{
|
|
1057
|
+
// control to stop video capture
|
|
1058
|
+
if (keyWasPressed('Digit6') || keyWasPressed(debugKey))
|
|
1059
|
+
debugVideoCaptureStop();
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1035
1062
|
if (keyWasPressed(debugKey)) // Esc
|
|
1036
1063
|
debugOverlay = !debugOverlay;
|
|
1037
1064
|
if (debugOverlay)
|
|
1038
1065
|
{
|
|
1039
1066
|
if (keyWasPressed('Digit0'))
|
|
1040
|
-
|
|
1067
|
+
debugWatermark = !debugWatermark;
|
|
1041
1068
|
if (keyWasPressed('Digit1'))
|
|
1042
1069
|
debugPhysics = !debugPhysics, debugParticles = false;
|
|
1043
1070
|
if (keyWasPressed('Digit2'))
|
|
@@ -1048,8 +1075,8 @@ function debugUpdate()
|
|
|
1048
1075
|
debugRaycast = !debugRaycast;
|
|
1049
1076
|
if (keyWasPressed('Digit5'))
|
|
1050
1077
|
debugScreenshot();
|
|
1051
|
-
if (keyWasPressed('Digit6'))
|
|
1052
|
-
|
|
1078
|
+
if (keyWasPressed('Digit6') && !debugVideoCaptureIsActive())
|
|
1079
|
+
debugVideoCaptureStart();
|
|
1053
1080
|
}
|
|
1054
1081
|
}
|
|
1055
1082
|
|
|
@@ -1065,13 +1092,11 @@ function debugRender()
|
|
|
1065
1092
|
{
|
|
1066
1093
|
// combine canvases, remove alpha and save
|
|
1067
1094
|
combineCanvases();
|
|
1068
|
-
|
|
1069
|
-
overlayContext.fillRect(0,0,w,h);
|
|
1070
|
-
overlayContext.drawImage(mainCanvas, 0, 0);
|
|
1071
|
-
debugSaveCanvas(overlayCanvas);
|
|
1095
|
+
debugSaveCanvas(mainCanvas);
|
|
1072
1096
|
debugTakeScreenshot = 0;
|
|
1073
1097
|
}
|
|
1074
1098
|
|
|
1099
|
+
const debugContext = mainContext;
|
|
1075
1100
|
if (debugGamepads && gamepadsEnable)
|
|
1076
1101
|
{
|
|
1077
1102
|
// draw gamepads
|
|
@@ -1119,9 +1144,6 @@ function debugRender()
|
|
|
1119
1144
|
let debugObject;
|
|
1120
1145
|
if (debugOverlay)
|
|
1121
1146
|
{
|
|
1122
|
-
const saveContext = mainContext;
|
|
1123
|
-
mainContext = overlayContext;
|
|
1124
|
-
|
|
1125
1147
|
// draw red rectangle around screen
|
|
1126
1148
|
const cameraSize = getCameraSize();
|
|
1127
1149
|
debugRect(cameraPos, cameraSize.subtract(vec2(.1)), '#f008');
|
|
@@ -1153,15 +1175,14 @@ function debugRender()
|
|
|
1153
1175
|
// show floored tile pick for tile collision
|
|
1154
1176
|
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), rgb(1,1,0,.5));
|
|
1155
1177
|
}
|
|
1156
|
-
mainContext = saveContext;
|
|
1157
1178
|
}
|
|
1158
1179
|
|
|
1159
1180
|
{
|
|
1160
1181
|
// draw debug primitives
|
|
1161
|
-
|
|
1182
|
+
debugContext.lineWidth = 2;
|
|
1162
1183
|
debugPrimitives.forEach(p=>
|
|
1163
1184
|
{
|
|
1164
|
-
|
|
1185
|
+
debugContext.save();
|
|
1165
1186
|
|
|
1166
1187
|
// create canvas transform from world space to screen space
|
|
1167
1188
|
// without scaling because we want consistent pixel sizes
|
|
@@ -1172,55 +1193,56 @@ function debugRender()
|
|
|
1172
1193
|
scale = cameraScale;
|
|
1173
1194
|
angle -= cameraAngle;
|
|
1174
1195
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
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;
|
|
1179
1201
|
if (p.text !== undefined)
|
|
1180
1202
|
{
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1203
|
+
debugContext.font = p.size*scale + 'px '+ p.font;
|
|
1204
|
+
debugContext.textAlign = 'center';
|
|
1205
|
+
debugContext.textBaseline = 'middle';
|
|
1206
|
+
debugContext.fillText(p.text, 0, 0);
|
|
1185
1207
|
}
|
|
1186
1208
|
else if (p.points !== undefined)
|
|
1187
1209
|
{
|
|
1188
1210
|
// poly
|
|
1189
|
-
|
|
1211
|
+
debugContext.beginPath();
|
|
1190
1212
|
for (const point of p.points)
|
|
1191
1213
|
{
|
|
1192
1214
|
const p2 = point.scale(scale).floor();
|
|
1193
|
-
|
|
1215
|
+
debugContext.lineTo(p2.x, p2.y);
|
|
1194
1216
|
}
|
|
1195
|
-
|
|
1196
|
-
p.fill &&
|
|
1197
|
-
|
|
1217
|
+
debugContext.closePath();
|
|
1218
|
+
p.fill && debugContext.fill();
|
|
1219
|
+
debugContext.stroke();
|
|
1198
1220
|
}
|
|
1199
1221
|
else if (p.size === 0 || (p.size.x === 0 && p.size.y === 0))
|
|
1200
1222
|
{
|
|
1201
1223
|
// point
|
|
1202
1224
|
const pointSize = debugPointSize * scale;
|
|
1203
|
-
|
|
1204
|
-
|
|
1225
|
+
debugContext.fillRect(-pointSize/2, -1, pointSize, 3);
|
|
1226
|
+
debugContext.fillRect(-1, -pointSize/2, 3, pointSize);
|
|
1205
1227
|
}
|
|
1206
1228
|
else if (p.size.x !== undefined)
|
|
1207
1229
|
{
|
|
1208
1230
|
// rect
|
|
1209
1231
|
const s = p.size.scale(scale).floor();
|
|
1210
1232
|
const w = s.x, h = s.y;
|
|
1211
|
-
p.fill &&
|
|
1212
|
-
|
|
1233
|
+
p.fill && debugContext.fillRect(-w/2|0, -h/2|0, w, h);
|
|
1234
|
+
debugContext.strokeRect(-w/2|0, -h/2|0, w, h);
|
|
1213
1235
|
}
|
|
1214
1236
|
else
|
|
1215
1237
|
{
|
|
1216
1238
|
// circle
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
p.fill &&
|
|
1220
|
-
|
|
1239
|
+
debugContext.beginPath();
|
|
1240
|
+
debugContext.arc(0, 0, p.size*scale/2, 0, 9);
|
|
1241
|
+
p.fill && debugContext.fill();
|
|
1242
|
+
debugContext.stroke();
|
|
1221
1243
|
}
|
|
1222
1244
|
|
|
1223
|
-
|
|
1245
|
+
debugContext.restore();
|
|
1224
1246
|
});
|
|
1225
1247
|
|
|
1226
1248
|
// remove expired primitives
|
|
@@ -1229,55 +1251,53 @@ function debugRender()
|
|
|
1229
1251
|
|
|
1230
1252
|
if (debugObject)
|
|
1231
1253
|
{
|
|
1232
|
-
const saveContext = mainContext;
|
|
1233
|
-
mainContext = overlayContext;
|
|
1234
1254
|
const raycastHitPos = tileCollisionRaycast(debugObject.pos, mousePos);
|
|
1235
1255
|
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), rgb(0,1,1,.3));
|
|
1236
1256
|
drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5));
|
|
1237
1257
|
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
'\
|
|
1241
|
-
|
|
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();
|
|
1242
1263
|
drawTextScreen(debugText, mousePosScreen, 24, rgb(), .05, undefined, 'center', 'monospace');
|
|
1243
|
-
mainContext = saveContext;
|
|
1244
1264
|
}
|
|
1245
1265
|
|
|
1246
1266
|
{
|
|
1247
1267
|
// draw debug overlay
|
|
1248
1268
|
const fontSize = 20;
|
|
1249
1269
|
const lineHeight = fontSize * 1.2 | 0;
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
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;
|
|
1257
1277
|
|
|
1258
1278
|
let x = 9, y = 0, h = lineHeight;
|
|
1259
1279
|
if (debugOverlay)
|
|
1260
1280
|
{
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
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'),
|
|
1264
1284
|
x, y += h);
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
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);
|
|
1281
1301
|
|
|
1282
1302
|
let keysPressed = '';
|
|
1283
1303
|
let mousePressed = '';
|
|
@@ -1290,8 +1310,8 @@ function debugRender()
|
|
|
1290
1310
|
else if (keyIsDown(i, 0))
|
|
1291
1311
|
keysPressed += i + ' ' ;
|
|
1292
1312
|
}
|
|
1293
|
-
mousePressed &&
|
|
1294
|
-
keysPressed &&
|
|
1313
|
+
mousePressed && debugContext.fillText('Mouse: ' + mousePressed, x, y += h);
|
|
1314
|
+
keysPressed && debugContext.fillText('Keys: ' + keysPressed, x, y += h);
|
|
1295
1315
|
|
|
1296
1316
|
// show gamepad buttons
|
|
1297
1317
|
for (let i = 1; i < inputData.length; i++)
|
|
@@ -1303,18 +1323,18 @@ function debugRender()
|
|
|
1303
1323
|
if (keyIsDown(j, i))
|
|
1304
1324
|
buttonsPressed += j + ' ' ;
|
|
1305
1325
|
}
|
|
1306
|
-
buttonsPressed &&
|
|
1326
|
+
buttonsPressed && debugContext.fillText(`Gamepad ${i-1}: ` + buttonsPressed, x, y += h);
|
|
1307
1327
|
}
|
|
1308
1328
|
}
|
|
1309
1329
|
else
|
|
1310
1330
|
{
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
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);
|
|
1315
1335
|
}
|
|
1316
1336
|
|
|
1317
|
-
|
|
1337
|
+
debugContext.restore();
|
|
1318
1338
|
}
|
|
1319
1339
|
}
|
|
1320
1340
|
|
|
@@ -1365,7 +1385,7 @@ function debugVideoCaptureStart()
|
|
|
1365
1385
|
// start recording
|
|
1366
1386
|
LOG('Video capture started.');
|
|
1367
1387
|
debugVideoCapture.start();
|
|
1368
|
-
debugVideoCaptureTimer = new Timer(0);
|
|
1388
|
+
debugVideoCaptureTimer = new Timer(0, true);
|
|
1369
1389
|
|
|
1370
1390
|
if (!debugVideoCaptureIcon)
|
|
1371
1391
|
{
|
|
@@ -1563,11 +1583,7 @@ function percent(value, valueA, valueB)
|
|
|
1563
1583
|
* @return {number}
|
|
1564
1584
|
* @memberof Utilities */
|
|
1565
1585
|
function lerp(valueA, valueB, percent)
|
|
1566
|
-
{
|
|
1567
|
-
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
1568
|
-
console.warn('lerp() parameter order changed! use lerp(start, end, p)');
|
|
1569
|
-
return valueA + clamp(percent) * (valueB-valueA);
|
|
1570
|
-
}
|
|
1586
|
+
{ return valueA + clamp(percent) * (valueB-valueA); }
|
|
1571
1587
|
|
|
1572
1588
|
/** Gets percent between percentA and percentB and linearly interpolates between lerpA and lerpB
|
|
1573
1589
|
* A shortcut for lerp(lerpA, lerpB, percent(value, percentA, percentB))
|
|
@@ -1598,11 +1614,7 @@ function distanceWrap(valueA, valueB, wrapSize=1)
|
|
|
1598
1614
|
* @return {number}
|
|
1599
1615
|
* @memberof Utilities */
|
|
1600
1616
|
function lerpWrap(valueA, valueB, percent, wrapSize=1)
|
|
1601
|
-
{
|
|
1602
|
-
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
1603
|
-
console.warn('lerpWrap() parameter order changed! use lerpWrap(start, end, p)');
|
|
1604
|
-
return valueA + clamp(percent) * distanceWrap(valueB, valueA, wrapSize);
|
|
1605
|
-
}
|
|
1617
|
+
{ return valueA + clamp(percent) * distanceWrap(valueB, valueA, wrapSize); }
|
|
1606
1618
|
|
|
1607
1619
|
/** Returns signed wrapped distance between the two angles passed in
|
|
1608
1620
|
* @param {number} angleA
|
|
@@ -2716,6 +2728,20 @@ let canvasClearColor = CLEAR_BLACK;
|
|
|
2716
2728
|
* @memberof Settings */
|
|
2717
2729
|
let canvasMaxSize = vec2(1920, 1080);
|
|
2718
2730
|
|
|
2731
|
+
/** Minimum aspect ratio of the canvas (width/height), unused if 0
|
|
2732
|
+
* Can be used with canvasMaxAspect to limit aspect ratio
|
|
2733
|
+
* @type {number}
|
|
2734
|
+
* @default
|
|
2735
|
+
* @memberof Settings */
|
|
2736
|
+
let canvasMinAspect = 0;
|
|
2737
|
+
|
|
2738
|
+
/** Maximum aspect ratio of the canvas (width/height), unused if 0
|
|
2739
|
+
* Can be used with canvasMinAspect to limit aspect ratio
|
|
2740
|
+
* @type {number}
|
|
2741
|
+
* @default
|
|
2742
|
+
* @memberof Settings */
|
|
2743
|
+
let canvasMaxAspect = 0;
|
|
2744
|
+
|
|
2719
2745
|
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
2720
2746
|
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
2721
2747
|
* @type {Vector2}
|
|
@@ -2728,15 +2754,7 @@ let canvasFixedSize = vec2();
|
|
|
2728
2754
|
* @type {boolean}
|
|
2729
2755
|
* @default
|
|
2730
2756
|
* @memberof Settings */
|
|
2731
|
-
let canvasPixelated =
|
|
2732
|
-
|
|
2733
|
-
/** Use nearest canvas scaling for more pixelated look
|
|
2734
|
-
* - If enabled sets css image-rendering:pixelated
|
|
2735
|
-
* - This defaults to false because text looks better with smoothing
|
|
2736
|
-
* @type {boolean}
|
|
2737
|
-
* @default
|
|
2738
|
-
* @memberof Settings */
|
|
2739
|
-
let overlayCanvasPixelated = false;
|
|
2757
|
+
let canvasPixelated = false;
|
|
2740
2758
|
|
|
2741
2759
|
/** Disables texture filtering for crisper pixel art
|
|
2742
2760
|
* @type {boolean}
|
|
@@ -2784,13 +2802,19 @@ let glCircleSides = 32;
|
|
|
2784
2802
|
* @type {Vector2}
|
|
2785
2803
|
* @default Vector2(16,16)
|
|
2786
2804
|
* @memberof Settings */
|
|
2787
|
-
let
|
|
2805
|
+
let tileDefaultSize = vec2(16);
|
|
2788
2806
|
|
|
2789
|
-
/**
|
|
2807
|
+
/** Default padding pixels around tiles
|
|
2790
2808
|
* @type {number}
|
|
2791
2809
|
* @default
|
|
2792
2810
|
* @memberof Settings */
|
|
2793
|
-
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;
|
|
2794
2818
|
|
|
2795
2819
|
///////////////////////////////////////////////////////////////////////////////
|
|
2796
2820
|
// Object settings
|
|
@@ -3012,13 +3036,22 @@ function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
|
|
|
3012
3036
|
* @memberof Settings */
|
|
3013
3037
|
function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
|
|
3014
3038
|
|
|
3039
|
+
/** Set minimum aspect ratio of the canvas (width/height), unused if 0
|
|
3040
|
+
* @param {number} aspect
|
|
3041
|
+
* @memberof Settings */
|
|
3042
|
+
function setCanvasMinAspect(aspect) { canvasMinAspect = aspect; }
|
|
3043
|
+
|
|
3044
|
+
/** Set maximum aspect ratio of the canvas (width/height), unused if 0
|
|
3045
|
+
* @param {number} aspect
|
|
3046
|
+
* @memberof Settings */
|
|
3047
|
+
function setCanvasMaxAspect(aspect) { canvasMaxAspect = aspect; }
|
|
3048
|
+
|
|
3015
3049
|
/** Set fixed size of the canvas
|
|
3016
3050
|
* @param {Vector2} size
|
|
3017
3051
|
* @memberof Settings */
|
|
3018
3052
|
function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
|
|
3019
3053
|
|
|
3020
3054
|
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
3021
|
-
* - If enabled sets css image-rendering:pixelated
|
|
3022
3055
|
* @param {boolean} pixelated
|
|
3023
3056
|
* @memberof Settings */
|
|
3024
3057
|
function setCanvasPixelated(pixelated)
|
|
@@ -3030,18 +3063,6 @@ function setCanvasPixelated(pixelated)
|
|
|
3030
3063
|
glCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
3031
3064
|
}
|
|
3032
3065
|
|
|
3033
|
-
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
3034
|
-
* - If enabled sets css image-rendering:pixelated
|
|
3035
|
-
* - This defaults to false because text looks better with smoothing
|
|
3036
|
-
* @param {boolean} pixelated
|
|
3037
|
-
* @memberof Settings */
|
|
3038
|
-
function setOverlayCanvasPixelated(pixelated)
|
|
3039
|
-
{
|
|
3040
|
-
overlayCanvasPixelated = pixelated;
|
|
3041
|
-
if (overlayCanvas)
|
|
3042
|
-
overlayCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
3043
|
-
}
|
|
3044
|
-
|
|
3045
3066
|
/** Disables texture filtering for crisper pixel art
|
|
3046
3067
|
* @param {boolean} pixelated
|
|
3047
3068
|
* @memberof Settings */
|
|
@@ -3085,12 +3106,17 @@ function setGLCircleSides(sides) { glCircleSides = sides; }
|
|
|
3085
3106
|
/** Set default size of tiles in pixels
|
|
3086
3107
|
* @param {Vector2} size
|
|
3087
3108
|
* @memberof Settings */
|
|
3088
|
-
function
|
|
3109
|
+
function setTileDefaultSize(size) { tileDefaultSize = size.copy(); }
|
|
3089
3110
|
|
|
3090
|
-
/**
|
|
3091
|
-
* @param {number}
|
|
3111
|
+
/** Default padding pixels around tiles
|
|
3112
|
+
* @param {number} padding
|
|
3113
|
+
* @memberof Settings */
|
|
3114
|
+
function setTileDefaultPadding(padding) { tileDefaultPadding = padding; }
|
|
3115
|
+
|
|
3116
|
+
/** Default amount of pixels smaller to draw tiles to prevent neighbor bleeding
|
|
3117
|
+
* @param {number} bleed
|
|
3092
3118
|
* @memberof Settings */
|
|
3093
|
-
function
|
|
3119
|
+
function setTileDefaultBleed(bleed) { tileDefaultBleed = bleed; }
|
|
3094
3120
|
|
|
3095
3121
|
/** Set if collisions between objects are enabled
|
|
3096
3122
|
* @param {boolean} enable
|
|
@@ -3241,7 +3267,7 @@ function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUn
|
|
|
3241
3267
|
/** Set if watermark with FPS should be shown
|
|
3242
3268
|
* @param {boolean} show
|
|
3243
3269
|
* @memberof Debug */
|
|
3244
|
-
function
|
|
3270
|
+
function setDebugWatermark(show) { debugWatermark = show; }
|
|
3245
3271
|
|
|
3246
3272
|
/** Set key code used to toggle debug mode, Esc by default
|
|
3247
3273
|
* @param {string} key
|
|
@@ -3677,10 +3703,11 @@ class EngineObject
|
|
|
3677
3703
|
* @return {number} -1 if this.mirror is true, or 1 if not mirrored */
|
|
3678
3704
|
getMirrorSign() { return this.mirror ? -1 : 1; }
|
|
3679
3705
|
|
|
3680
|
-
/** Attaches a child to this with a
|
|
3706
|
+
/** Attaches a child to this with a local transform, returns child for chaining
|
|
3681
3707
|
* @param {EngineObject} child
|
|
3682
3708
|
* @param {Vector2} [localPos=(0,0)]
|
|
3683
|
-
* @param {number} [localAngle]
|
|
3709
|
+
* @param {number} [localAngle]
|
|
3710
|
+
* @return {EngineObject} The child object added */
|
|
3684
3711
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
3685
3712
|
{
|
|
3686
3713
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
@@ -3690,6 +3717,7 @@ class EngineObject
|
|
|
3690
3717
|
child.parent = this;
|
|
3691
3718
|
child.localPos = localPos.copy();
|
|
3692
3719
|
child.localAngle = localAngle;
|
|
3720
|
+
return child;
|
|
3693
3721
|
}
|
|
3694
3722
|
|
|
3695
3723
|
/** Removes a child from this one
|
|
@@ -3780,7 +3808,6 @@ class EngineObject
|
|
|
3780
3808
|
* There are 3 canvas/contexts available to draw to...
|
|
3781
3809
|
* mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
|
|
3782
3810
|
* glCanvas - Used by the accelerated WebGL batch rendering system.
|
|
3783
|
-
* overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
|
|
3784
3811
|
*
|
|
3785
3812
|
* The WebGL rendering system is very fast with some caveats...
|
|
3786
3813
|
* - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
|
|
@@ -3800,16 +3827,6 @@ let mainCanvas;
|
|
|
3800
3827
|
* @memberof Draw */
|
|
3801
3828
|
let mainContext;
|
|
3802
3829
|
|
|
3803
|
-
/** A canvas that appears on top of everything the same size as mainCanvas
|
|
3804
|
-
* @type {HTMLCanvasElement}
|
|
3805
|
-
* @memberof Draw */
|
|
3806
|
-
let overlayCanvas;
|
|
3807
|
-
|
|
3808
|
-
/** 2d context for overlayCanvas
|
|
3809
|
-
* @type {CanvasRenderingContext2D}
|
|
3810
|
-
* @memberof Draw */
|
|
3811
|
-
let overlayContext;
|
|
3812
|
-
|
|
3813
3830
|
/** The default canvas to use for drawing, usually mainCanvas
|
|
3814
3831
|
* @type {HTMLCanvasElement|OffscreenCanvas}
|
|
3815
3832
|
* @memberof Draw */
|
|
@@ -3830,6 +3847,16 @@ let workCanvas;
|
|
|
3830
3847
|
* @memberof Draw */
|
|
3831
3848
|
let workContext;
|
|
3832
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
|
+
|
|
3833
3860
|
/** The size of the main canvas (and other secondary canvases)
|
|
3834
3861
|
* @type {Vector2}
|
|
3835
3862
|
* @memberof Draw */
|
|
@@ -3852,7 +3879,7 @@ let drawCount;
|
|
|
3852
3879
|
* - This can take vecs or floats for easier use and conversion
|
|
3853
3880
|
* - If an index is passed in, the tile size and index will determine the position
|
|
3854
3881
|
* @param {Vector2|number} [pos=0] - Position of the tile in pixels, or tile index
|
|
3855
|
-
* @param {Vector2|number} [size
|
|
3882
|
+
* @param {Vector2|number} [size] - Size of tile in pixels
|
|
3856
3883
|
* @param {number} [textureIndex] - Texture index to use
|
|
3857
3884
|
* @param {number} [padding] - How many pixels padding around tiles
|
|
3858
3885
|
* @return {TileInfo}
|
|
@@ -3862,7 +3889,7 @@ let drawCount;
|
|
|
3862
3889
|
* tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
|
|
3863
3890
|
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
3864
3891
|
* @memberof Draw */
|
|
3865
|
-
function tile(pos=new Vector2, size=
|
|
3892
|
+
function tile(pos=new Vector2, size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding)
|
|
3866
3893
|
{
|
|
3867
3894
|
if (headlessMode)
|
|
3868
3895
|
return new TileInfo;
|
|
@@ -3901,13 +3928,13 @@ function tile(pos=new Vector2, size=tileSizeDefault, textureIndex=0, padding=0)
|
|
|
3901
3928
|
class TileInfo
|
|
3902
3929
|
{
|
|
3903
3930
|
/** Create a tile info object
|
|
3904
|
-
* @param {Vector2} [pos=(0,0)]
|
|
3905
|
-
* @param {Vector2} [size
|
|
3906
|
-
* @param {number} [textureIndex]
|
|
3907
|
-
* @param {number} [padding]
|
|
3908
|
-
* @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
|
|
3909
3936
|
*/
|
|
3910
|
-
constructor(pos=vec2(), size=
|
|
3937
|
+
constructor(pos=vec2(), size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding, bleed=tileDefaultBleed)
|
|
3911
3938
|
{
|
|
3912
3939
|
/** @property {Vector2} - Top left corner of tile in pixels */
|
|
3913
3940
|
this.pos = pos.copy();
|
|
@@ -3920,7 +3947,7 @@ class TileInfo
|
|
|
3920
3947
|
/** @property {TextureInfo} - The texture info for this tile */
|
|
3921
3948
|
this.textureInfo = textureInfos[this.textureIndex];
|
|
3922
3949
|
/** @property {number} - Shrinks tile by this many pixels to prevent neighbors bleeding */
|
|
3923
|
-
this.
|
|
3950
|
+
this.bleed = bleed;
|
|
3924
3951
|
}
|
|
3925
3952
|
|
|
3926
3953
|
/** Returns a copy of this tile offset by a vector
|
|
@@ -3928,7 +3955,7 @@ class TileInfo
|
|
|
3928
3955
|
* @return {TileInfo}
|
|
3929
3956
|
*/
|
|
3930
3957
|
offset(offset)
|
|
3931
|
-
{ 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); }
|
|
3932
3959
|
|
|
3933
3960
|
/** Returns a copy of this tile offset by a number of animation frames
|
|
3934
3961
|
* @param {number} frame - Offset to apply in animation frames
|
|
@@ -3951,7 +3978,7 @@ class TileInfo
|
|
|
3951
3978
|
this.size = textureInfo.size.copy();
|
|
3952
3979
|
this.textureInfo = textureInfo;
|
|
3953
3980
|
// do not use padding or bleed
|
|
3954
|
-
this.
|
|
3981
|
+
this.bleed = this.padding = 0;
|
|
3955
3982
|
return this;
|
|
3956
3983
|
}
|
|
3957
3984
|
}
|
|
@@ -4017,7 +4044,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4017
4044
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
4018
4045
|
|
|
4019
4046
|
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
4020
|
-
const
|
|
4047
|
+
const bleed = tileInfo?.bleed ?? 0;
|
|
4021
4048
|
if (useWebGL && glEnable)
|
|
4022
4049
|
{
|
|
4023
4050
|
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
@@ -4032,13 +4059,13 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4032
4059
|
const w = tileInfo.size.x * sizeInverse.x;
|
|
4033
4060
|
const h = tileInfo.size.y * sizeInverse.y;
|
|
4034
4061
|
glSetTexture(textureInfo.glTexture);
|
|
4035
|
-
if (
|
|
4062
|
+
if (bleed)
|
|
4036
4063
|
{
|
|
4037
|
-
const
|
|
4038
|
-
const
|
|
4064
|
+
const bleedX = sizeInverse.x*bleed;
|
|
4065
|
+
const bleedY = sizeInverse.y*bleed;
|
|
4039
4066
|
glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
|
|
4040
|
-
x +
|
|
4041
|
-
x -
|
|
4067
|
+
x + bleedX, y + bleedY,
|
|
4068
|
+
x - bleedX + w, y - bleedY + h,
|
|
4042
4069
|
color.rgbaInt(), additiveColor && additiveColor.rgbaInt());
|
|
4043
4070
|
}
|
|
4044
4071
|
else
|
|
@@ -4066,7 +4093,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
4066
4093
|
// calculate uvs and render
|
|
4067
4094
|
const x = tileInfo.pos.x, y = tileInfo.pos.y;
|
|
4068
4095
|
const w = tileInfo.size.x, h = tileInfo.size.y;
|
|
4069
|
-
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);
|
|
4070
4097
|
}
|
|
4071
4098
|
else
|
|
4072
4099
|
{
|
|
@@ -4431,26 +4458,7 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
4431
4458
|
drawTextScreen(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, angle, context);
|
|
4432
4459
|
}
|
|
4433
4460
|
|
|
4434
|
-
/** Draw text
|
|
4435
|
-
* Automatically splits new lines into rows
|
|
4436
|
-
* @param {string|number} text
|
|
4437
|
-
* @param {Vector2} pos
|
|
4438
|
-
* @param {number} [size]
|
|
4439
|
-
* @param {Color} [color=(1,1,1,1)]
|
|
4440
|
-
* @param {number} [lineWidth]
|
|
4441
|
-
* @param {Color} [lineColor=(0,0,0,1)]
|
|
4442
|
-
* @param {CanvasTextAlign} [textAlign='center']
|
|
4443
|
-
* @param {string} [font=fontDefault]
|
|
4444
|
-
* @param {string} [fontStyle]
|
|
4445
|
-
* @param {number} [maxWidth]
|
|
4446
|
-
* @param {number} [angle]
|
|
4447
|
-
* @memberof Draw */
|
|
4448
|
-
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, angle=0)
|
|
4449
|
-
{
|
|
4450
|
-
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, angle, overlayContext);
|
|
4451
|
-
}
|
|
4452
|
-
|
|
4453
|
-
/** Draw text on overlay canvas in screen space
|
|
4461
|
+
/** Draw text in screen space
|
|
4454
4462
|
* Automatically splits new lines into rows
|
|
4455
4463
|
* @param {string|number} text
|
|
4456
4464
|
* @param {Vector2} pos
|
|
@@ -4463,9 +4471,9 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
4463
4471
|
* @param {string} [fontStyle]
|
|
4464
4472
|
* @param {number} [maxWidth]
|
|
4465
4473
|
* @param {number} [angle]
|
|
4466
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=
|
|
4474
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=mainContext]
|
|
4467
4475
|
* @memberof Draw */
|
|
4468
|
-
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)
|
|
4469
4477
|
{
|
|
4470
4478
|
ASSERT(isString(text), 'text must be a string');
|
|
4471
4479
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -4509,16 +4517,16 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
4509
4517
|
* @memberof Draw */
|
|
4510
4518
|
function screenToWorld(screenPos)
|
|
4511
4519
|
{
|
|
4520
|
+
ASSERT(isVector2(screenPos), 'screenPos must be a vec2');
|
|
4521
|
+
|
|
4512
4522
|
let x = (screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale;
|
|
4513
4523
|
let y = (screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale;
|
|
4514
4524
|
if (cameraAngle)
|
|
4515
4525
|
{
|
|
4516
4526
|
// apply camera rotation
|
|
4517
4527
|
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
4518
|
-
const
|
|
4519
|
-
|
|
4520
|
-
x = rotatedX;
|
|
4521
|
-
y = rotatedY;
|
|
4528
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4529
|
+
x = xr; y = yr;
|
|
4522
4530
|
}
|
|
4523
4531
|
return new Vector2(x + cameraPos.x, y + cameraPos.y);
|
|
4524
4532
|
}
|
|
@@ -4529,16 +4537,16 @@ function screenToWorld(screenPos)
|
|
|
4529
4537
|
* @memberof Draw */
|
|
4530
4538
|
function worldToScreen(worldPos)
|
|
4531
4539
|
{
|
|
4540
|
+
ASSERT(isVector2(worldPos), 'worldPos must be a vec2');
|
|
4541
|
+
|
|
4532
4542
|
let x = worldPos.x - cameraPos.x;
|
|
4533
4543
|
let y = worldPos.y - cameraPos.y;
|
|
4534
4544
|
if (cameraAngle)
|
|
4535
4545
|
{
|
|
4536
4546
|
// apply inverse camera rotation
|
|
4537
4547
|
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
4538
|
-
const
|
|
4539
|
-
|
|
4540
|
-
x = rotatedX;
|
|
4541
|
-
y = rotatedY;
|
|
4548
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4549
|
+
x = xr; y = yr;
|
|
4542
4550
|
}
|
|
4543
4551
|
return new Vector2
|
|
4544
4552
|
(
|
|
@@ -4553,16 +4561,16 @@ function worldToScreen(worldPos)
|
|
|
4553
4561
|
* @memberof Draw */
|
|
4554
4562
|
function screenToWorldDelta(screenDelta)
|
|
4555
4563
|
{
|
|
4564
|
+
ASSERT(isVector2(screenDelta), 'screenDelta must be a vec2');
|
|
4565
|
+
|
|
4556
4566
|
let x = screenDelta.x / cameraScale;
|
|
4557
4567
|
let y = screenDelta.y / -cameraScale;
|
|
4558
4568
|
if (cameraAngle)
|
|
4559
4569
|
{
|
|
4560
4570
|
// apply camera rotation
|
|
4561
4571
|
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
4562
|
-
const
|
|
4563
|
-
|
|
4564
|
-
x = rotatedX;
|
|
4565
|
-
y = rotatedY;
|
|
4572
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4573
|
+
x = xr; y = yr;
|
|
4566
4574
|
}
|
|
4567
4575
|
return new Vector2(x, y);
|
|
4568
4576
|
}
|
|
@@ -4573,16 +4581,16 @@ function screenToWorldDelta(screenDelta)
|
|
|
4573
4581
|
* @memberof Draw */
|
|
4574
4582
|
function worldToScreenDelta(worldDelta)
|
|
4575
4583
|
{
|
|
4584
|
+
ASSERT(isVector2(worldDelta), 'worldDelta must be a vec2');
|
|
4585
|
+
|
|
4576
4586
|
let x = worldDelta.x;
|
|
4577
4587
|
let y = worldDelta.y;
|
|
4578
4588
|
if (cameraAngle)
|
|
4579
4589
|
{
|
|
4580
4590
|
// apply inverse camera rotation
|
|
4581
4591
|
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
4582
|
-
const
|
|
4583
|
-
|
|
4584
|
-
x = rotatedX;
|
|
4585
|
-
y = rotatedY;
|
|
4592
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
4593
|
+
x = xr; y = yr;
|
|
4586
4594
|
}
|
|
4587
4595
|
return new Vector2(x * cameraScale, y * -cameraScale);
|
|
4588
4596
|
}
|
|
@@ -4595,6 +4603,10 @@ function worldToScreenDelta(worldDelta)
|
|
|
4595
4603
|
* @memberof Draw */
|
|
4596
4604
|
function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
|
|
4597
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
|
+
|
|
4598
4610
|
return [
|
|
4599
4611
|
screenToWorld(screenPos),
|
|
4600
4612
|
screenSize.scale(1/cameraScale),
|
|
@@ -4610,6 +4622,10 @@ function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
|
|
|
4610
4622
|
* @memberof Draw */
|
|
4611
4623
|
function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
|
|
4612
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
|
+
|
|
4613
4629
|
return [
|
|
4614
4630
|
worldToScreen(worldPos),
|
|
4615
4631
|
worldSize.scale(cameraScale),
|
|
@@ -4622,8 +4638,8 @@ function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
|
|
|
4622
4638
|
* @memberof Draw */
|
|
4623
4639
|
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
4624
4640
|
|
|
4625
|
-
/** Check if a point or circle is on screen
|
|
4626
|
-
* 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
|
|
4627
4643
|
* This can be used to cull offscreen objects from render or update
|
|
4628
4644
|
* @param {Vector2} pos - world space position
|
|
4629
4645
|
* @param {Vector2|number} size - world space size or diameter
|
|
@@ -4631,12 +4647,30 @@ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
|
4631
4647
|
* @memberof Draw */
|
|
4632
4648
|
function isOnScreen(pos, size=0)
|
|
4633
4649
|
{
|
|
4634
|
-
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
|
+
|
|
4635
4666
|
if (size instanceof Vector2)
|
|
4636
|
-
size =
|
|
4637
|
-
size *= cameraScale
|
|
4638
|
-
|
|
4639
|
-
|
|
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;
|
|
4640
4674
|
}
|
|
4641
4675
|
|
|
4642
4676
|
/** Enable normal or additive blend mode
|
|
@@ -4650,18 +4684,19 @@ function setBlendMode(additive=false, context)
|
|
|
4650
4684
|
context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
4651
4685
|
}
|
|
4652
4686
|
|
|
4653
|
-
/** Combines
|
|
4654
|
-
* This is necessary for things like
|
|
4687
|
+
/** Combines LittleJS canvases onto the main canvas
|
|
4688
|
+
* This is necessary for things like screenshots and video
|
|
4655
4689
|
* @memberof Draw */
|
|
4656
4690
|
function combineCanvases()
|
|
4657
4691
|
{
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
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);
|
|
4665
4700
|
}
|
|
4666
4701
|
|
|
4667
4702
|
/** Helper function to draw an image with color and additive color applied
|
|
@@ -4678,18 +4713,18 @@ function combineCanvases()
|
|
|
4678
4713
|
* @param {number} dHeight
|
|
4679
4714
|
* @param {Color} color
|
|
4680
4715
|
* @param {Color} [additiveColor]
|
|
4681
|
-
* @param {number} [
|
|
4716
|
+
* @param {number} [bleed] - How many pixels to shrink the source, used to fix bleeding
|
|
4682
4717
|
* @memberof Draw */
|
|
4683
|
-
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)
|
|
4684
4719
|
{
|
|
4685
4720
|
function isWhite(c) { return c.r >= 1 && c.g >= 1 && c.b >= 1; }
|
|
4686
4721
|
function isBlack(c) { return c.r <= 0 && c.g <= 0 && c.b <= 0 && c.a <= 0; }
|
|
4687
|
-
const sx2 =
|
|
4688
|
-
const sy2 =
|
|
4722
|
+
const sx2 = bleed;
|
|
4723
|
+
const sy2 = bleed;
|
|
4689
4724
|
sWidth = max(1,sWidth|0);
|
|
4690
4725
|
sHeight = max(1,sHeight|0);
|
|
4691
|
-
const sWidth2 = sWidth - 2*
|
|
4692
|
-
const sHeight2 = sHeight - 2*
|
|
4726
|
+
const sWidth2 = sWidth - 2*bleed;
|
|
4727
|
+
const sHeight2 = sHeight - 2*bleed;
|
|
4693
4728
|
if (!canvasColorTiles || (additiveColor ? isWhite(color.add(additiveColor)) && additiveColor.a <= 0 : isWhite(color)))
|
|
4694
4729
|
{
|
|
4695
4730
|
// white texture with no additive alpha, no need to tint
|
|
@@ -4700,12 +4735,12 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4700
4735
|
else
|
|
4701
4736
|
{
|
|
4702
4737
|
// copy to offscreen canvas
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4738
|
+
workReadCanvas.width = sWidth;
|
|
4739
|
+
workReadCanvas.height = sHeight;
|
|
4740
|
+
workReadContext.drawImage(image, sx|0, sy|0, sWidth, sHeight, 0, 0, sWidth, sHeight);
|
|
4706
4741
|
|
|
4707
4742
|
// tint image using offscreen work context
|
|
4708
|
-
const imageData =
|
|
4743
|
+
const imageData = workReadContext.getImageData(0, 0, sWidth, sHeight);
|
|
4709
4744
|
const data = imageData.data;
|
|
4710
4745
|
if (additiveColor && !isBlack(additiveColor))
|
|
4711
4746
|
{
|
|
@@ -4714,8 +4749,8 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4714
4749
|
const colorAdd = [additiveColor.r * 255, additiveColor.g * 255, additiveColor.b * 255, additiveColor.a * 255];
|
|
4715
4750
|
for (let i = 0; i < data.length; ++i)
|
|
4716
4751
|
data[i] = data[i] * colorMultiply[i&3] + colorAdd[i&3] |0;
|
|
4717
|
-
|
|
4718
|
-
context.drawImage(
|
|
4752
|
+
workReadContext.putImageData(imageData, 0, 0);
|
|
4753
|
+
context.drawImage(workReadCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
4719
4754
|
}
|
|
4720
4755
|
else
|
|
4721
4756
|
{
|
|
@@ -4726,9 +4761,9 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4726
4761
|
data[i+1] *= color.g;
|
|
4727
4762
|
data[i+2] *= color.b;
|
|
4728
4763
|
}
|
|
4729
|
-
|
|
4764
|
+
workReadContext.putImageData(imageData, 0, 0);
|
|
4730
4765
|
context.globalAlpha = color.a;
|
|
4731
|
-
context.drawImage(
|
|
4766
|
+
context.drawImage(workReadCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
4732
4767
|
context.globalAlpha = 1;
|
|
4733
4768
|
}
|
|
4734
4769
|
}
|
|
@@ -4790,7 +4825,7 @@ class FontImage
|
|
|
4790
4825
|
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1))
|
|
4791
4826
|
{
|
|
4792
4827
|
// load default font image
|
|
4793
|
-
if (!engineFontImage)
|
|
4828
|
+
if (!image && !engineFontImage)
|
|
4794
4829
|
{
|
|
4795
4830
|
engineFontImage = new Image;
|
|
4796
4831
|
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
@@ -4813,23 +4848,14 @@ class FontImage
|
|
|
4813
4848
|
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center, context);
|
|
4814
4849
|
}
|
|
4815
4850
|
|
|
4816
|
-
/** Draw text
|
|
4817
|
-
* @param {string|number} text
|
|
4818
|
-
* @param {Vector2} pos
|
|
4819
|
-
* @param {number} [scale]
|
|
4820
|
-
* @param {boolean} [center]
|
|
4821
|
-
*/
|
|
4822
|
-
drawTextOverlay(text, pos, scale=4, center)
|
|
4823
|
-
{ this.drawText(text, pos, scale, center, overlayContext); }
|
|
4824
|
-
|
|
4825
|
-
/** Draw text on overlay canvas in screen space using the image font
|
|
4851
|
+
/** Draw text in screen space using the image font
|
|
4826
4852
|
* @param {string|number} text
|
|
4827
4853
|
* @param {Vector2} pos
|
|
4828
4854
|
* @param {number} [scale]
|
|
4829
4855
|
* @param {boolean} [center]
|
|
4830
4856
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4831
4857
|
*/
|
|
4832
|
-
drawTextScreen(text, pos, scale=4, center=true, context=
|
|
4858
|
+
drawTextScreen(text, pos, scale=4, center=true, context=mainContext)
|
|
4833
4859
|
{
|
|
4834
4860
|
context.save();
|
|
4835
4861
|
const size = this.tileSize;
|
|
@@ -5100,6 +5126,16 @@ function gamepadConnected(gamepad=gamepadPrimary)
|
|
|
5100
5126
|
return !!inputData[gamepad+1];
|
|
5101
5127
|
}
|
|
5102
5128
|
|
|
5129
|
+
/** Returns how many control sticks the passed in gamepad has
|
|
5130
|
+
* @param {number} [gamepad]
|
|
5131
|
+
* @return {number}
|
|
5132
|
+
* @memberof Input */
|
|
5133
|
+
function gamepadStickCount(gamepad=gamepadPrimary)
|
|
5134
|
+
{
|
|
5135
|
+
ASSERT(isNumber(gamepad), 'gamepad must be a number');
|
|
5136
|
+
return gamepadStickData[gamepad]?.length ?? 0;
|
|
5137
|
+
}
|
|
5138
|
+
|
|
5103
5139
|
/** True if a touch device has been detected
|
|
5104
5140
|
* @memberof Input */
|
|
5105
5141
|
const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
@@ -5440,9 +5476,11 @@ function inputUpdate()
|
|
|
5440
5476
|
dpad.set(x, -y);
|
|
5441
5477
|
sticks[0] = dpad.clampLength(); // clamp to circle
|
|
5442
5478
|
}
|
|
5443
|
-
const rightTouchStick = touchGamepadSticks[1] ?? vec2();
|
|
5444
5479
|
if (touchGamepadButtonCount === 1)
|
|
5480
|
+
{
|
|
5481
|
+
const rightTouchStick = touchGamepadSticks[1] ?? vec2();
|
|
5445
5482
|
sticks[1] = applyDeadZones(rightTouchStick);
|
|
5483
|
+
}
|
|
5446
5484
|
|
|
5447
5485
|
// read virtual gamepad buttons
|
|
5448
5486
|
const data = inputData[1] ?? (inputData[1] = []);
|
|
@@ -5567,7 +5605,7 @@ function inputRender()
|
|
|
5567
5605
|
return;
|
|
5568
5606
|
|
|
5569
5607
|
// setup the canvas
|
|
5570
|
-
const context =
|
|
5608
|
+
const context = mainContext;
|
|
5571
5609
|
context.save();
|
|
5572
5610
|
context.globalAlpha = alpha*touchGamepadAlpha;
|
|
5573
5611
|
context.strokeStyle = '#fff';
|
|
@@ -5809,12 +5847,12 @@ class Sound
|
|
|
5809
5847
|
///////////////////////////////////////////////////////////////////////////////
|
|
5810
5848
|
|
|
5811
5849
|
/**
|
|
5812
|
-
* Sound Wave Object -
|
|
5813
|
-
* - 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
|
|
5814
5852
|
* @extends Sound
|
|
5815
5853
|
* @memberof Audio
|
|
5816
5854
|
* @example
|
|
5817
|
-
* //
|
|
5855
|
+
* // load an audio asset file
|
|
5818
5856
|
* const sound_example = new SoundWave('sound.mp3');
|
|
5819
5857
|
*
|
|
5820
5858
|
* // play the sound
|
|
@@ -6534,8 +6572,7 @@ class CanvasLayer extends EngineObject
|
|
|
6534
6572
|
/** Destroy this canvas layer */
|
|
6535
6573
|
destroy()
|
|
6536
6574
|
{
|
|
6537
|
-
if (this.destroyed)
|
|
6538
|
-
return;
|
|
6575
|
+
if (this.destroyed) return;
|
|
6539
6576
|
|
|
6540
6577
|
this.textureInfo.destroyWebGLTexture();
|
|
6541
6578
|
super.destroy();
|
|
@@ -6767,8 +6804,8 @@ class TileLayer extends CanvasLayer
|
|
|
6767
6804
|
if (!this.context) return;
|
|
6768
6805
|
|
|
6769
6806
|
// save current render settings
|
|
6770
|
-
/** @type {[HTMLCanvasElement|OffscreenCanvas, CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
6771
|
-
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];
|
|
6772
6809
|
|
|
6773
6810
|
// set the draw canvas and context to this layer
|
|
6774
6811
|
// use camera settings to match this layer's canvas
|
|
@@ -6777,6 +6814,7 @@ class TileLayer extends CanvasLayer
|
|
|
6777
6814
|
cameraPos = this.size.scale(.5);
|
|
6778
6815
|
const tileSize = this.tileInfo ? this.tileInfo.size : vec2(1);
|
|
6779
6816
|
cameraScale = tileSize.x;
|
|
6817
|
+
canvasClearColor = CLEAR_BLACK;
|
|
6780
6818
|
mainCanvasSize = this.size.multiply(tileSize);
|
|
6781
6819
|
if (clear)
|
|
6782
6820
|
{
|
|
@@ -6786,7 +6824,7 @@ class TileLayer extends CanvasLayer
|
|
|
6786
6824
|
}
|
|
6787
6825
|
|
|
6788
6826
|
// disable smoothing for pixel art
|
|
6789
|
-
|
|
6827
|
+
drawContext.imageSmoothingEnabled = !tilesPixelated;
|
|
6790
6828
|
|
|
6791
6829
|
// setup gl rendering if enabled
|
|
6792
6830
|
glPreRender();
|
|
@@ -6802,7 +6840,7 @@ class TileLayer extends CanvasLayer
|
|
|
6802
6840
|
//debugSaveCanvas(this.canvas);
|
|
6803
6841
|
|
|
6804
6842
|
// set stuff back to normal
|
|
6805
|
-
[drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale] = this.savedRenderSettings;
|
|
6843
|
+
[drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor] = this.savedRenderSettings;
|
|
6806
6844
|
}
|
|
6807
6845
|
|
|
6808
6846
|
/** Draw the tile at a given position in the tile grid
|
|
@@ -7087,9 +7125,9 @@ class ParticleEmitter extends EngineObject
|
|
|
7087
7125
|
/** @property {Color} - Color at start of life 2, randomized between start colors */
|
|
7088
7126
|
this.colorStartB = colorStartB.copy();
|
|
7089
7127
|
/** @property {Color} - Color at end of life 1, randomized between end colors */
|
|
7090
|
-
this.colorEndA
|
|
7128
|
+
this.colorEndA = colorEndA.copy();
|
|
7091
7129
|
/** @property {Color} - Color at end of life 2, randomized between end colors */
|
|
7092
|
-
this.colorEndB
|
|
7130
|
+
this.colorEndB = colorEndB.copy();
|
|
7093
7131
|
/** @property {boolean} - Should color be randomized linearly or across each component */
|
|
7094
7132
|
this.randomColorLinear = randomColorLinear;
|
|
7095
7133
|
|
|
@@ -7173,8 +7211,10 @@ class ParticleEmitter extends EngineObject
|
|
|
7173
7211
|
if (debugParticles)
|
|
7174
7212
|
{
|
|
7175
7213
|
// show emitter bounds
|
|
7176
|
-
|
|
7177
|
-
|
|
7214
|
+
if (typeof this.emitSize === 'number')
|
|
7215
|
+
debugCircle(this.pos, this.emitSize/2, '#0f0');
|
|
7216
|
+
else
|
|
7217
|
+
debugRect(this.pos, this.emitSize, '#0f0', 0, this.angle);
|
|
7178
7218
|
}
|
|
7179
7219
|
}
|
|
7180
7220
|
|
|
@@ -7184,8 +7224,8 @@ class ParticleEmitter extends EngineObject
|
|
|
7184
7224
|
{
|
|
7185
7225
|
// spawn a particle
|
|
7186
7226
|
let pos = typeof this.emitSize === 'number' ? // check if number was used
|
|
7187
|
-
randInCircle(this.emitSize/2)
|
|
7188
|
-
: vec2(rand(-.5,.5), rand(-.5,.5))
|
|
7227
|
+
randInCircle(this.emitSize/2) // circle emitter
|
|
7228
|
+
: vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
|
|
7189
7229
|
.multiply(this.emitSize).rotate(this.angle)
|
|
7190
7230
|
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
7191
7231
|
if (!this.localSpace)
|
|
@@ -7513,10 +7553,10 @@ class Medal
|
|
|
7513
7553
|
*/
|
|
7514
7554
|
render(hidePercent=0)
|
|
7515
7555
|
{
|
|
7516
|
-
const context =
|
|
7556
|
+
const context = mainContext;
|
|
7517
7557
|
const width = min(medalDisplaySize.x, mainCanvas.width);
|
|
7518
7558
|
const height = medalDisplaySize.y;
|
|
7519
|
-
const x =
|
|
7559
|
+
const x = mainCanvas.width - width;
|
|
7520
7560
|
const y = -height*hidePercent;
|
|
7521
7561
|
const backgroundColor = hsl(0,0,.9);
|
|
7522
7562
|
|
|
@@ -7557,7 +7597,7 @@ class Medal
|
|
|
7557
7597
|
{
|
|
7558
7598
|
// draw the image or icon
|
|
7559
7599
|
if (this.image)
|
|
7560
|
-
|
|
7600
|
+
mainContext.drawImage(this.image, pos.x-size/2, pos.y-size/2, size, size);
|
|
7561
7601
|
else
|
|
7562
7602
|
drawTextScreen(this.icon, pos, size*.7, BLACK);
|
|
7563
7603
|
}
|
|
@@ -7578,7 +7618,7 @@ class Medal
|
|
|
7578
7618
|
* @namespace WebGL
|
|
7579
7619
|
*/
|
|
7580
7620
|
|
|
7581
|
-
/** The WebGL canvas which appears
|
|
7621
|
+
/** The WebGL canvas which appears below the main canvas
|
|
7582
7622
|
* @type {HTMLCanvasElement}
|
|
7583
7623
|
* @memberof WebGL */
|
|
7584
7624
|
let glCanvas;
|
|
@@ -7608,7 +7648,7 @@ const gl_MAX_POLY_VERTEXES = gl_ARRAY_BUFFER_SIZE / gl_POLY_VERTEX_BYTE_STRIDE |
|
|
|
7608
7648
|
///////////////////////////////////////////////////////////////////////////////
|
|
7609
7649
|
|
|
7610
7650
|
// Initialize WebGL, called automatically by the engine
|
|
7611
|
-
function glInit()
|
|
7651
|
+
function glInit(rootElement)
|
|
7612
7652
|
{
|
|
7613
7653
|
// keep set of texture infos so they can be restored if context is lost
|
|
7614
7654
|
glTextureInfos = new Set;
|
|
@@ -7632,8 +7672,7 @@ function glInit()
|
|
|
7632
7672
|
return;
|
|
7633
7673
|
}
|
|
7634
7674
|
|
|
7635
|
-
// attach the WebGL canvas
|
|
7636
|
-
const rootElement = mainCanvas.parentElement;
|
|
7675
|
+
// attach the WebGL canvas;
|
|
7637
7676
|
rootElement.appendChild(glCanvas);
|
|
7638
7677
|
|
|
7639
7678
|
// startup webgl
|
|
@@ -7851,6 +7890,9 @@ function glClearCanvas()
|
|
|
7851
7890
|
glCanvas.width = drawCanvas.width;
|
|
7852
7891
|
glCanvas.height = drawCanvas.height;
|
|
7853
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);
|
|
7854
7896
|
glContext.clear(glContext.COLOR_BUFFER_BIT);
|
|
7855
7897
|
}
|
|
7856
7898
|
|
|
@@ -8080,7 +8122,6 @@ function glDraw(x, y, sizeX, sizeY, angle=0, uv0X=0, uv0Y=0, uv1X=1, uv1Y=1, rgb
|
|
|
8080
8122
|
glFlush();
|
|
8081
8123
|
glSetInstancedMode();
|
|
8082
8124
|
|
|
8083
|
-
glPolyMode = false;
|
|
8084
8125
|
let offset = glBatchCount++ * gl_INDICES_PER_INSTANCE;
|
|
8085
8126
|
glPositionData[offset++] = x;
|
|
8086
8127
|
glPositionData[offset++] = y;
|
|
@@ -8601,13 +8642,12 @@ class PostProcessPlugin
|
|
|
8601
8642
|
{
|
|
8602
8643
|
/** Create global post processing shader
|
|
8603
8644
|
* @param {string} shaderCode
|
|
8604
|
-
* @param {boolean} [includeOverlay]
|
|
8605
8645
|
* @param {boolean} [includeMainCanvas]
|
|
8606
8646
|
* @example
|
|
8607
8647
|
* // create the post process plugin object
|
|
8608
8648
|
* new PostProcessPlugin(shaderCode);
|
|
8609
8649
|
*/
|
|
8610
|
-
constructor(shaderCode,
|
|
8650
|
+
constructor(shaderCode, includeMainCanvas=true)
|
|
8611
8651
|
{
|
|
8612
8652
|
ASSERT(!postProcess, 'Post process already initialized');
|
|
8613
8653
|
postProcess = this;
|
|
@@ -8679,17 +8719,19 @@ class PostProcessPlugin
|
|
|
8679
8719
|
// clear out the buffer
|
|
8680
8720
|
glFlush();
|
|
8681
8721
|
|
|
8682
|
-
|
|
8722
|
+
// setup texture
|
|
8723
|
+
glContext.activeTexture(glContext.TEXTURE0);
|
|
8724
|
+
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
8725
|
+
if (includeMainCanvas)
|
|
8683
8726
|
{
|
|
8684
|
-
// copy
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
}
|
|
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);
|
|
8693
8735
|
}
|
|
8694
8736
|
|
|
8695
8737
|
// setup shader program to draw a quad
|
|
@@ -8698,14 +8740,6 @@ class PostProcessPlugin
|
|
|
8698
8740
|
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, 1);
|
|
8699
8741
|
glContext.disable(glContext.BLEND);
|
|
8700
8742
|
|
|
8701
|
-
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
8702
|
-
glContext.activeTexture(glContext.TEXTURE0);
|
|
8703
|
-
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
8704
|
-
if (includeMainCanvas || includeOverlay)
|
|
8705
|
-
{
|
|
8706
|
-
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, mainCanvas);
|
|
8707
|
-
}
|
|
8708
|
-
|
|
8709
8743
|
// set vertex position attribute
|
|
8710
8744
|
const vertexByteStride = 8;
|
|
8711
8745
|
const pLocation = glContext.getAttribLocation(postProcess.shader, 'p');
|
|
@@ -8934,12 +8968,14 @@ class UISystemPlugin
|
|
|
8934
8968
|
* // create the ui plugin object
|
|
8935
8969
|
* new UISystemPlugin;
|
|
8936
8970
|
*/
|
|
8937
|
-
constructor(context=
|
|
8971
|
+
constructor(context=mainContext)
|
|
8938
8972
|
{
|
|
8939
8973
|
ASSERT(!uiSystem, 'UI system already initialized');
|
|
8940
8974
|
uiSystem = this;
|
|
8941
8975
|
|
|
8942
8976
|
// default settings
|
|
8977
|
+
/** @property {boolean} - Activate when mouse is pressed down instead of clicked */
|
|
8978
|
+
this.activateOnPress = false;
|
|
8943
8979
|
/** @property {Color} - Default fill color for UI elements */
|
|
8944
8980
|
this.defaultColor = WHITE;
|
|
8945
8981
|
/** @property {Color} - Default outline color for UI elements */
|
|
@@ -8980,8 +9016,6 @@ class UISystemPlugin
|
|
|
8980
9016
|
// navigation properties
|
|
8981
9017
|
/** @property {UIObject} - Object currently selected by navigation (gamepad or keyboard) */
|
|
8982
9018
|
this.navigationObject = undefined;
|
|
8983
|
-
/** @property {number} - Gamepad index to use for UI navigation */
|
|
8984
|
-
this.navigationGamepadIndex = 0;
|
|
8985
9019
|
/** @property {Timer} - Cooldown timer for navigation inputs */
|
|
8986
9020
|
this.navigationTimer = new Timer(undefined, true);
|
|
8987
9021
|
/** @property {number} - Time between navigation inputs in seconds */
|
|
@@ -9389,9 +9423,8 @@ class UISystemPlugin
|
|
|
9389
9423
|
const both = uiSystem.navigationDirection === 2;
|
|
9390
9424
|
if (isUsingGamepad)
|
|
9391
9425
|
{
|
|
9392
|
-
const
|
|
9393
|
-
const
|
|
9394
|
-
const dpad = gamepadDpad(gamepad);
|
|
9426
|
+
const stick = gamepadStick(0, gamepadPrimary);
|
|
9427
|
+
const dpad = gamepadDpad(gamepadPrimary);
|
|
9395
9428
|
if (both)
|
|
9396
9429
|
return -(stick.y || dpad.y) || (stick.x || dpad.x);
|
|
9397
9430
|
return vertical ? -(stick.y || dpad.y) : (stick.x || dpad.x);
|
|
@@ -9417,9 +9450,8 @@ class UISystemPlugin
|
|
|
9417
9450
|
const vertical = uiSystem.navigationDirection === 1;
|
|
9418
9451
|
if (isUsingGamepad)
|
|
9419
9452
|
{
|
|
9420
|
-
const
|
|
9421
|
-
const
|
|
9422
|
-
const dpad = gamepadDpad(gamepad);
|
|
9453
|
+
const stick = gamepadStick(0, gamepadPrimary);
|
|
9454
|
+
const dpad = gamepadDpad(gamepadPrimary);
|
|
9423
9455
|
return !vertical ? (stick.y || dpad.y) : (stick.x || dpad.x);
|
|
9424
9456
|
}
|
|
9425
9457
|
const back = !vertical ? 'ArrowUp' : 'ArrowLeft';
|
|
@@ -9431,8 +9463,7 @@ class UISystemPlugin
|
|
|
9431
9463
|
* @return {boolean} */
|
|
9432
9464
|
getNavigationWasPressed()
|
|
9433
9465
|
{
|
|
9434
|
-
|
|
9435
|
-
return isUsingGamepad ? gamepadWasPressed(0, gamepad) :
|
|
9466
|
+
return isUsingGamepad ? gamepadWasPressed(0, gamepadPrimary) :
|
|
9436
9467
|
keyWasPressed('Space') || keyWasPressed('Enter');
|
|
9437
9468
|
}
|
|
9438
9469
|
|
|
@@ -9596,13 +9627,15 @@ class UIObject
|
|
|
9596
9627
|
uiSystem.uiObjects.push(this);
|
|
9597
9628
|
}
|
|
9598
9629
|
|
|
9599
|
-
/** Add a child UIObject to this object
|
|
9600
|
-
* @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 */
|
|
9601
9633
|
addChild(child)
|
|
9602
9634
|
{
|
|
9603
9635
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
9604
9636
|
this.children.push(child);
|
|
9605
9637
|
child.parent = this;
|
|
9638
|
+
return child;
|
|
9606
9639
|
}
|
|
9607
9640
|
|
|
9608
9641
|
/** Remove a child UIObject from this object
|
|
@@ -9675,8 +9708,16 @@ class UIObject
|
|
|
9675
9708
|
if (uiSystem.activeObject && !isActive)
|
|
9676
9709
|
uiSystem.activeObject.onRelease();
|
|
9677
9710
|
uiSystem.activeObject = this;
|
|
9711
|
+
|
|
9712
|
+
if (uiSystem.activateOnPress)
|
|
9713
|
+
{
|
|
9714
|
+
this.onClick();
|
|
9715
|
+
if (!this.soundPress && this.soundClick)
|
|
9716
|
+
this.soundClick.play();
|
|
9717
|
+
}
|
|
9678
9718
|
}
|
|
9679
9719
|
}
|
|
9720
|
+
if (!uiSystem.activateOnPress)
|
|
9680
9721
|
if (!mouseDown && this.isActiveObject() && this.interactive)
|
|
9681
9722
|
{
|
|
9682
9723
|
this.onClick();
|
|
@@ -12116,33 +12157,33 @@ async function box2dInit()
|
|
|
12116
12157
|
color = getDebugColor(color);
|
|
12117
12158
|
point1 = box2d.vec2FromPointer(point1);
|
|
12118
12159
|
point2 = box2d.vec2FromPointer(point2);
|
|
12119
|
-
drawLine(point1, point2, debugLineWidth, color, vec2(), 0, false
|
|
12160
|
+
drawLine(point1, point2, debugLineWidth, color, vec2(), 0, false);
|
|
12120
12161
|
};
|
|
12121
12162
|
debugDraw.DrawPolygon = function(vertices, vertexCount, color)
|
|
12122
12163
|
{
|
|
12123
12164
|
color = getDebugColor(color);
|
|
12124
12165
|
const points = getPointsList(vertices, vertexCount);
|
|
12125
|
-
drawPoly(points, CLEAR_WHITE, debugLineWidth, color, vec2(), 0, false
|
|
12166
|
+
drawPoly(points, CLEAR_WHITE, debugLineWidth, color, vec2(), 0, false);
|
|
12126
12167
|
};
|
|
12127
12168
|
debugDraw.DrawSolidPolygon = function(vertices, vertexCount, color)
|
|
12128
12169
|
{
|
|
12129
12170
|
color = getDebugColor(color);
|
|
12130
12171
|
const points = getPointsList(vertices, vertexCount);
|
|
12131
|
-
drawPoly(points, color, 0, color, vec2(), 0, false
|
|
12172
|
+
drawPoly(points, color, 0, color, vec2(), 0, false);
|
|
12132
12173
|
};
|
|
12133
12174
|
debugDraw.DrawCircle = function(center, radius, color)
|
|
12134
12175
|
{
|
|
12135
12176
|
color = getDebugColor(color);
|
|
12136
12177
|
center = box2d.vec2FromPointer(center);
|
|
12137
|
-
drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false
|
|
12178
|
+
drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false);
|
|
12138
12179
|
};
|
|
12139
12180
|
debugDraw.DrawSolidCircle = function(center, radius, axis, color)
|
|
12140
12181
|
{
|
|
12141
12182
|
color = getDebugColor(color);
|
|
12142
12183
|
center = box2d.vec2FromPointer(center);
|
|
12143
12184
|
axis = box2d.vec2FromPointer(axis).scale(radius);
|
|
12144
|
-
drawCircle(center, radius*2, color, debugLineWidth, color, false
|
|
12145
|
-
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);
|
|
12146
12187
|
};
|
|
12147
12188
|
debugDraw.DrawTransform = function(transform)
|
|
12148
12189
|
{
|
|
@@ -12151,8 +12192,8 @@ async function box2dInit()
|
|
|
12151
12192
|
const angle = -transform.get_q().GetAngle();
|
|
12152
12193
|
const p1 = vec2(1,0), c1 = rgb(.75,0,0,.8);
|
|
12153
12194
|
const p2 = vec2(0,1), c2 = rgb(0,.75,0,.8);
|
|
12154
|
-
drawLine(vec2(), p1, debugLineWidth, c1, pos, angle, false
|
|
12155
|
-
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);
|
|
12156
12197
|
}
|
|
12157
12198
|
|
|
12158
12199
|
debugDraw.AppendFlags(box2d.instance.b2Draw.e_shapeBit);
|
|
@@ -12172,8 +12213,8 @@ async function box2dInit()
|
|
|
12172
12213
|
|
|
12173
12214
|
///////////////////////////////////////////////////////////////////////////////
|
|
12174
12215
|
|
|
12175
|
-
/** Draw a scalable nine-slice UI element to the
|
|
12176
|
-
* 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
|
|
12177
12218
|
* @param {Vector2} pos - Screen space position
|
|
12178
12219
|
* @param {Vector2} size - Screen space size
|
|
12179
12220
|
* @param {TileInfo} startTile - Starting tile for the nine-slice pattern
|
|
@@ -12183,7 +12224,7 @@ async function box2dInit()
|
|
|
12183
12224
|
* @memberof DrawUtilities */
|
|
12184
12225
|
function drawNineSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
12185
12226
|
{
|
|
12186
|
-
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true
|
|
12227
|
+
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true);
|
|
12187
12228
|
}
|
|
12188
12229
|
|
|
12189
12230
|
/** Draw a scalable nine-slice UI element in world space
|
|
@@ -12232,8 +12273,8 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
12232
12273
|
}
|
|
12233
12274
|
}
|
|
12234
12275
|
|
|
12235
|
-
/** Draw a scalable three-slice UI element to the
|
|
12236
|
-
* 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
|
|
12237
12278
|
* @param {Vector2} pos - Screen space position
|
|
12238
12279
|
* @param {Vector2} size - Screen space size
|
|
12239
12280
|
* @param {TileInfo} startTile - Starting tile for the three-slice pattern
|
|
@@ -12243,7 +12284,7 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
12243
12284
|
* @memberof DrawUtilities */
|
|
12244
12285
|
function drawThreeSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
12245
12286
|
{
|
|
12246
|
-
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true
|
|
12287
|
+
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true);
|
|
12247
12288
|
}
|
|
12248
12289
|
|
|
12249
12290
|
/** Draw a scalable three-slice UI element in world space
|