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.release.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 - Release Mode
|
|
@@ -726,7 +746,7 @@ function drawEngineSplashScreen(t)
|
|
|
726
746
|
* - Debug functionality is disabled to reduce size and increase performance
|
|
727
747
|
*/
|
|
728
748
|
|
|
729
|
-
let
|
|
749
|
+
let debugWatermark = 0;
|
|
730
750
|
let debugKey = '';
|
|
731
751
|
const debug = 0;
|
|
732
752
|
const debugOverlay = 0;
|
|
@@ -886,11 +906,7 @@ function percent(value, valueA, valueB)
|
|
|
886
906
|
* @return {number}
|
|
887
907
|
* @memberof Utilities */
|
|
888
908
|
function lerp(valueA, valueB, percent)
|
|
889
|
-
{
|
|
890
|
-
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
891
|
-
console.warn('lerp() parameter order changed! use lerp(start, end, p)');
|
|
892
|
-
return valueA + clamp(percent) * (valueB-valueA);
|
|
893
|
-
}
|
|
909
|
+
{ return valueA + clamp(percent) * (valueB-valueA); }
|
|
894
910
|
|
|
895
911
|
/** Gets percent between percentA and percentB and linearly interpolates between lerpA and lerpB
|
|
896
912
|
* A shortcut for lerp(lerpA, lerpB, percent(value, percentA, percentB))
|
|
@@ -921,11 +937,7 @@ function distanceWrap(valueA, valueB, wrapSize=1)
|
|
|
921
937
|
* @return {number}
|
|
922
938
|
* @memberof Utilities */
|
|
923
939
|
function lerpWrap(valueA, valueB, percent, wrapSize=1)
|
|
924
|
-
{
|
|
925
|
-
if (valueA >= 0 && valueA <= 1 && ((valueB < 0 || valueB > 1) && (percent < 0 || percent > 1)))
|
|
926
|
-
console.warn('lerpWrap() parameter order changed! use lerpWrap(start, end, p)');
|
|
927
|
-
return valueA + clamp(percent) * distanceWrap(valueB, valueA, wrapSize);
|
|
928
|
-
}
|
|
940
|
+
{ return valueA + clamp(percent) * distanceWrap(valueB, valueA, wrapSize); }
|
|
929
941
|
|
|
930
942
|
/** Returns signed wrapped distance between the two angles passed in
|
|
931
943
|
* @param {number} angleA
|
|
@@ -2039,6 +2051,20 @@ let canvasClearColor = CLEAR_BLACK;
|
|
|
2039
2051
|
* @memberof Settings */
|
|
2040
2052
|
let canvasMaxSize = vec2(1920, 1080);
|
|
2041
2053
|
|
|
2054
|
+
/** Minimum aspect ratio of the canvas (width/height), unused if 0
|
|
2055
|
+
* Can be used with canvasMaxAspect to limit aspect ratio
|
|
2056
|
+
* @type {number}
|
|
2057
|
+
* @default
|
|
2058
|
+
* @memberof Settings */
|
|
2059
|
+
let canvasMinAspect = 0;
|
|
2060
|
+
|
|
2061
|
+
/** Maximum aspect ratio of the canvas (width/height), unused if 0
|
|
2062
|
+
* Can be used with canvasMinAspect to limit aspect ratio
|
|
2063
|
+
* @type {number}
|
|
2064
|
+
* @default
|
|
2065
|
+
* @memberof Settings */
|
|
2066
|
+
let canvasMaxAspect = 0;
|
|
2067
|
+
|
|
2042
2068
|
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
2043
2069
|
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
2044
2070
|
* @type {Vector2}
|
|
@@ -2051,15 +2077,7 @@ let canvasFixedSize = vec2();
|
|
|
2051
2077
|
* @type {boolean}
|
|
2052
2078
|
* @default
|
|
2053
2079
|
* @memberof Settings */
|
|
2054
|
-
let canvasPixelated =
|
|
2055
|
-
|
|
2056
|
-
/** Use nearest canvas scaling for more pixelated look
|
|
2057
|
-
* - If enabled sets css image-rendering:pixelated
|
|
2058
|
-
* - This defaults to false because text looks better with smoothing
|
|
2059
|
-
* @type {boolean}
|
|
2060
|
-
* @default
|
|
2061
|
-
* @memberof Settings */
|
|
2062
|
-
let overlayCanvasPixelated = false;
|
|
2080
|
+
let canvasPixelated = false;
|
|
2063
2081
|
|
|
2064
2082
|
/** Disables texture filtering for crisper pixel art
|
|
2065
2083
|
* @type {boolean}
|
|
@@ -2107,13 +2125,19 @@ let glCircleSides = 32;
|
|
|
2107
2125
|
* @type {Vector2}
|
|
2108
2126
|
* @default Vector2(16,16)
|
|
2109
2127
|
* @memberof Settings */
|
|
2110
|
-
let
|
|
2128
|
+
let tileDefaultSize = vec2(16);
|
|
2111
2129
|
|
|
2112
|
-
/**
|
|
2130
|
+
/** Default padding pixels around tiles
|
|
2113
2131
|
* @type {number}
|
|
2114
2132
|
* @default
|
|
2115
2133
|
* @memberof Settings */
|
|
2116
|
-
let
|
|
2134
|
+
let tileDefaultPadding = 0;
|
|
2135
|
+
|
|
2136
|
+
/** Default amount of pixels smaller to draw tiles to prevent neighbor bleeding
|
|
2137
|
+
* @type {number}
|
|
2138
|
+
* @default
|
|
2139
|
+
* @memberof Settings */
|
|
2140
|
+
let tileDefaultBleed = 0;
|
|
2117
2141
|
|
|
2118
2142
|
///////////////////////////////////////////////////////////////////////////////
|
|
2119
2143
|
// Object settings
|
|
@@ -2335,13 +2359,22 @@ function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
|
|
|
2335
2359
|
* @memberof Settings */
|
|
2336
2360
|
function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
|
|
2337
2361
|
|
|
2362
|
+
/** Set minimum aspect ratio of the canvas (width/height), unused if 0
|
|
2363
|
+
* @param {number} aspect
|
|
2364
|
+
* @memberof Settings */
|
|
2365
|
+
function setCanvasMinAspect(aspect) { canvasMinAspect = aspect; }
|
|
2366
|
+
|
|
2367
|
+
/** Set maximum aspect ratio of the canvas (width/height), unused if 0
|
|
2368
|
+
* @param {number} aspect
|
|
2369
|
+
* @memberof Settings */
|
|
2370
|
+
function setCanvasMaxAspect(aspect) { canvasMaxAspect = aspect; }
|
|
2371
|
+
|
|
2338
2372
|
/** Set fixed size of the canvas
|
|
2339
2373
|
* @param {Vector2} size
|
|
2340
2374
|
* @memberof Settings */
|
|
2341
2375
|
function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
|
|
2342
2376
|
|
|
2343
2377
|
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
2344
|
-
* - If enabled sets css image-rendering:pixelated
|
|
2345
2378
|
* @param {boolean} pixelated
|
|
2346
2379
|
* @memberof Settings */
|
|
2347
2380
|
function setCanvasPixelated(pixelated)
|
|
@@ -2353,18 +2386,6 @@ function setCanvasPixelated(pixelated)
|
|
|
2353
2386
|
glCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
2354
2387
|
}
|
|
2355
2388
|
|
|
2356
|
-
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
2357
|
-
* - If enabled sets css image-rendering:pixelated
|
|
2358
|
-
* - This defaults to false because text looks better with smoothing
|
|
2359
|
-
* @param {boolean} pixelated
|
|
2360
|
-
* @memberof Settings */
|
|
2361
|
-
function setOverlayCanvasPixelated(pixelated)
|
|
2362
|
-
{
|
|
2363
|
-
overlayCanvasPixelated = pixelated;
|
|
2364
|
-
if (overlayCanvas)
|
|
2365
|
-
overlayCanvas.style.imageRendering = pixelated ? 'pixelated' : '';
|
|
2366
|
-
}
|
|
2367
|
-
|
|
2368
2389
|
/** Disables texture filtering for crisper pixel art
|
|
2369
2390
|
* @param {boolean} pixelated
|
|
2370
2391
|
* @memberof Settings */
|
|
@@ -2408,12 +2429,17 @@ function setGLCircleSides(sides) { glCircleSides = sides; }
|
|
|
2408
2429
|
/** Set default size of tiles in pixels
|
|
2409
2430
|
* @param {Vector2} size
|
|
2410
2431
|
* @memberof Settings */
|
|
2411
|
-
function
|
|
2432
|
+
function setTileDefaultSize(size) { tileDefaultSize = size.copy(); }
|
|
2412
2433
|
|
|
2413
|
-
/**
|
|
2414
|
-
* @param {number}
|
|
2434
|
+
/** Default padding pixels around tiles
|
|
2435
|
+
* @param {number} padding
|
|
2436
|
+
* @memberof Settings */
|
|
2437
|
+
function setTileDefaultPadding(padding) { tileDefaultPadding = padding; }
|
|
2438
|
+
|
|
2439
|
+
/** Default amount of pixels smaller to draw tiles to prevent neighbor bleeding
|
|
2440
|
+
* @param {number} bleed
|
|
2415
2441
|
* @memberof Settings */
|
|
2416
|
-
function
|
|
2442
|
+
function setTileDefaultBleed(bleed) { tileDefaultBleed = bleed; }
|
|
2417
2443
|
|
|
2418
2444
|
/** Set if collisions between objects are enabled
|
|
2419
2445
|
* @param {boolean} enable
|
|
@@ -2564,7 +2590,7 @@ function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUn
|
|
|
2564
2590
|
/** Set if watermark with FPS should be shown
|
|
2565
2591
|
* @param {boolean} show
|
|
2566
2592
|
* @memberof Debug */
|
|
2567
|
-
function
|
|
2593
|
+
function setDebugWatermark(show) { debugWatermark = show; }
|
|
2568
2594
|
|
|
2569
2595
|
/** Set key code used to toggle debug mode, Esc by default
|
|
2570
2596
|
* @param {string} key
|
|
@@ -3000,10 +3026,11 @@ class EngineObject
|
|
|
3000
3026
|
* @return {number} -1 if this.mirror is true, or 1 if not mirrored */
|
|
3001
3027
|
getMirrorSign() { return this.mirror ? -1 : 1; }
|
|
3002
3028
|
|
|
3003
|
-
/** Attaches a child to this with a
|
|
3029
|
+
/** Attaches a child to this with a local transform, returns child for chaining
|
|
3004
3030
|
* @param {EngineObject} child
|
|
3005
3031
|
* @param {Vector2} [localPos=(0,0)]
|
|
3006
|
-
* @param {number} [localAngle]
|
|
3032
|
+
* @param {number} [localAngle]
|
|
3033
|
+
* @return {EngineObject} The child object added */
|
|
3007
3034
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
3008
3035
|
{
|
|
3009
3036
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
@@ -3013,6 +3040,7 @@ class EngineObject
|
|
|
3013
3040
|
child.parent = this;
|
|
3014
3041
|
child.localPos = localPos.copy();
|
|
3015
3042
|
child.localAngle = localAngle;
|
|
3043
|
+
return child;
|
|
3016
3044
|
}
|
|
3017
3045
|
|
|
3018
3046
|
/** Removes a child from this one
|
|
@@ -3103,7 +3131,6 @@ class EngineObject
|
|
|
3103
3131
|
* There are 3 canvas/contexts available to draw to...
|
|
3104
3132
|
* mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
|
|
3105
3133
|
* glCanvas - Used by the accelerated WebGL batch rendering system.
|
|
3106
|
-
* overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
|
|
3107
3134
|
*
|
|
3108
3135
|
* The WebGL rendering system is very fast with some caveats...
|
|
3109
3136
|
* - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
|
|
@@ -3123,16 +3150,6 @@ let mainCanvas;
|
|
|
3123
3150
|
* @memberof Draw */
|
|
3124
3151
|
let mainContext;
|
|
3125
3152
|
|
|
3126
|
-
/** A canvas that appears on top of everything the same size as mainCanvas
|
|
3127
|
-
* @type {HTMLCanvasElement}
|
|
3128
|
-
* @memberof Draw */
|
|
3129
|
-
let overlayCanvas;
|
|
3130
|
-
|
|
3131
|
-
/** 2d context for overlayCanvas
|
|
3132
|
-
* @type {CanvasRenderingContext2D}
|
|
3133
|
-
* @memberof Draw */
|
|
3134
|
-
let overlayContext;
|
|
3135
|
-
|
|
3136
3153
|
/** The default canvas to use for drawing, usually mainCanvas
|
|
3137
3154
|
* @type {HTMLCanvasElement|OffscreenCanvas}
|
|
3138
3155
|
* @memberof Draw */
|
|
@@ -3153,6 +3170,16 @@ let workCanvas;
|
|
|
3153
3170
|
* @memberof Draw */
|
|
3154
3171
|
let workContext;
|
|
3155
3172
|
|
|
3173
|
+
/** Offscreen canvas with willReadFrequently that can be used for image processing
|
|
3174
|
+
* @type {OffscreenCanvas}
|
|
3175
|
+
* @memberof Draw */
|
|
3176
|
+
let workReadCanvas;
|
|
3177
|
+
|
|
3178
|
+
/** Offscreen canvas with willReadFrequently that can be used for image processing
|
|
3179
|
+
* @type {OffscreenCanvasRenderingContext2D}
|
|
3180
|
+
* @memberof Draw */
|
|
3181
|
+
let workReadContext;
|
|
3182
|
+
|
|
3156
3183
|
/** The size of the main canvas (and other secondary canvases)
|
|
3157
3184
|
* @type {Vector2}
|
|
3158
3185
|
* @memberof Draw */
|
|
@@ -3175,7 +3202,7 @@ let drawCount;
|
|
|
3175
3202
|
* - This can take vecs or floats for easier use and conversion
|
|
3176
3203
|
* - If an index is passed in, the tile size and index will determine the position
|
|
3177
3204
|
* @param {Vector2|number} [pos=0] - Position of the tile in pixels, or tile index
|
|
3178
|
-
* @param {Vector2|number} [size
|
|
3205
|
+
* @param {Vector2|number} [size] - Size of tile in pixels
|
|
3179
3206
|
* @param {number} [textureIndex] - Texture index to use
|
|
3180
3207
|
* @param {number} [padding] - How many pixels padding around tiles
|
|
3181
3208
|
* @return {TileInfo}
|
|
@@ -3185,7 +3212,7 @@ let drawCount;
|
|
|
3185
3212
|
* tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
|
|
3186
3213
|
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
3187
3214
|
* @memberof Draw */
|
|
3188
|
-
function tile(pos=new Vector2, size=
|
|
3215
|
+
function tile(pos=new Vector2, size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding)
|
|
3189
3216
|
{
|
|
3190
3217
|
if (headlessMode)
|
|
3191
3218
|
return new TileInfo;
|
|
@@ -3224,13 +3251,13 @@ function tile(pos=new Vector2, size=tileSizeDefault, textureIndex=0, padding=0)
|
|
|
3224
3251
|
class TileInfo
|
|
3225
3252
|
{
|
|
3226
3253
|
/** Create a tile info object
|
|
3227
|
-
* @param {Vector2} [pos=(0,0)]
|
|
3228
|
-
* @param {Vector2} [size
|
|
3229
|
-
* @param {number} [textureIndex]
|
|
3230
|
-
* @param {number} [padding]
|
|
3231
|
-
* @param {number} [
|
|
3254
|
+
* @param {Vector2} [pos=(0,0)] - Top left corner of tile in pixels
|
|
3255
|
+
* @param {Vector2} [size] - Size of tile in pixels
|
|
3256
|
+
* @param {number} [textureIndex] - Texture index to use
|
|
3257
|
+
* @param {number} [padding] - How many pixels padding around tiles
|
|
3258
|
+
* @param {number} [bleed] - How many pixels smaller to draw tiles
|
|
3232
3259
|
*/
|
|
3233
|
-
constructor(pos=vec2(), size=
|
|
3260
|
+
constructor(pos=vec2(), size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding, bleed=tileDefaultBleed)
|
|
3234
3261
|
{
|
|
3235
3262
|
/** @property {Vector2} - Top left corner of tile in pixels */
|
|
3236
3263
|
this.pos = pos.copy();
|
|
@@ -3243,7 +3270,7 @@ class TileInfo
|
|
|
3243
3270
|
/** @property {TextureInfo} - The texture info for this tile */
|
|
3244
3271
|
this.textureInfo = textureInfos[this.textureIndex];
|
|
3245
3272
|
/** @property {number} - Shrinks tile by this many pixels to prevent neighbors bleeding */
|
|
3246
|
-
this.
|
|
3273
|
+
this.bleed = bleed;
|
|
3247
3274
|
}
|
|
3248
3275
|
|
|
3249
3276
|
/** Returns a copy of this tile offset by a vector
|
|
@@ -3251,7 +3278,7 @@ class TileInfo
|
|
|
3251
3278
|
* @return {TileInfo}
|
|
3252
3279
|
*/
|
|
3253
3280
|
offset(offset)
|
|
3254
|
-
{ return new TileInfo(this.pos.add(offset), this.size, this.textureIndex, this.padding, this.
|
|
3281
|
+
{ return new TileInfo(this.pos.add(offset), this.size, this.textureIndex, this.padding, this.bleed); }
|
|
3255
3282
|
|
|
3256
3283
|
/** Returns a copy of this tile offset by a number of animation frames
|
|
3257
3284
|
* @param {number} frame - Offset to apply in animation frames
|
|
@@ -3274,7 +3301,7 @@ class TileInfo
|
|
|
3274
3301
|
this.size = textureInfo.size.copy();
|
|
3275
3302
|
this.textureInfo = textureInfo;
|
|
3276
3303
|
// do not use padding or bleed
|
|
3277
|
-
this.
|
|
3304
|
+
this.bleed = this.padding = 0;
|
|
3278
3305
|
return this;
|
|
3279
3306
|
}
|
|
3280
3307
|
}
|
|
@@ -3340,7 +3367,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
3340
3367
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3341
3368
|
|
|
3342
3369
|
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
3343
|
-
const
|
|
3370
|
+
const bleed = tileInfo?.bleed ?? 0;
|
|
3344
3371
|
if (useWebGL && glEnable)
|
|
3345
3372
|
{
|
|
3346
3373
|
ASSERT(!!glContext, 'WebGL is not enabled!');
|
|
@@ -3355,13 +3382,13 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
3355
3382
|
const w = tileInfo.size.x * sizeInverse.x;
|
|
3356
3383
|
const h = tileInfo.size.y * sizeInverse.y;
|
|
3357
3384
|
glSetTexture(textureInfo.glTexture);
|
|
3358
|
-
if (
|
|
3385
|
+
if (bleed)
|
|
3359
3386
|
{
|
|
3360
|
-
const
|
|
3361
|
-
const
|
|
3387
|
+
const bleedX = sizeInverse.x*bleed;
|
|
3388
|
+
const bleedY = sizeInverse.y*bleed;
|
|
3362
3389
|
glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
|
|
3363
|
-
x +
|
|
3364
|
-
x -
|
|
3390
|
+
x + bleedX, y + bleedY,
|
|
3391
|
+
x - bleedX + w, y - bleedY + h,
|
|
3365
3392
|
color.rgbaInt(), additiveColor && additiveColor.rgbaInt());
|
|
3366
3393
|
}
|
|
3367
3394
|
else
|
|
@@ -3389,7 +3416,7 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
|
3389
3416
|
// calculate uvs and render
|
|
3390
3417
|
const x = tileInfo.pos.x, y = tileInfo.pos.y;
|
|
3391
3418
|
const w = tileInfo.size.x, h = tileInfo.size.y;
|
|
3392
|
-
drawImageColor(context, textureInfo.image, x, y, w, h, -.5, -.5, 1, 1, color, additiveColor,
|
|
3419
|
+
drawImageColor(context, textureInfo.image, x, y, w, h, -.5, -.5, 1, 1, color, additiveColor, bleed);
|
|
3393
3420
|
}
|
|
3394
3421
|
else
|
|
3395
3422
|
{
|
|
@@ -3754,26 +3781,7 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
3754
3781
|
drawTextScreen(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, angle, context);
|
|
3755
3782
|
}
|
|
3756
3783
|
|
|
3757
|
-
/** Draw text
|
|
3758
|
-
* Automatically splits new lines into rows
|
|
3759
|
-
* @param {string|number} text
|
|
3760
|
-
* @param {Vector2} pos
|
|
3761
|
-
* @param {number} [size]
|
|
3762
|
-
* @param {Color} [color=(1,1,1,1)]
|
|
3763
|
-
* @param {number} [lineWidth]
|
|
3764
|
-
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3765
|
-
* @param {CanvasTextAlign} [textAlign='center']
|
|
3766
|
-
* @param {string} [font=fontDefault]
|
|
3767
|
-
* @param {string} [fontStyle]
|
|
3768
|
-
* @param {number} [maxWidth]
|
|
3769
|
-
* @param {number} [angle]
|
|
3770
|
-
* @memberof Draw */
|
|
3771
|
-
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, angle=0)
|
|
3772
|
-
{
|
|
3773
|
-
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, angle, overlayContext);
|
|
3774
|
-
}
|
|
3775
|
-
|
|
3776
|
-
/** Draw text on overlay canvas in screen space
|
|
3784
|
+
/** Draw text in screen space
|
|
3777
3785
|
* Automatically splits new lines into rows
|
|
3778
3786
|
* @param {string|number} text
|
|
3779
3787
|
* @param {Vector2} pos
|
|
@@ -3786,9 +3794,9 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
3786
3794
|
* @param {string} [fontStyle]
|
|
3787
3795
|
* @param {number} [maxWidth]
|
|
3788
3796
|
* @param {number} [angle]
|
|
3789
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=
|
|
3797
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=mainContext]
|
|
3790
3798
|
* @memberof Draw */
|
|
3791
|
-
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=
|
|
3799
|
+
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=mainContext)
|
|
3792
3800
|
{
|
|
3793
3801
|
ASSERT(isString(text), 'text must be a string');
|
|
3794
3802
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -3832,16 +3840,16 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
3832
3840
|
* @memberof Draw */
|
|
3833
3841
|
function screenToWorld(screenPos)
|
|
3834
3842
|
{
|
|
3843
|
+
ASSERT(isVector2(screenPos), 'screenPos must be a vec2');
|
|
3844
|
+
|
|
3835
3845
|
let x = (screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale;
|
|
3836
3846
|
let y = (screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale;
|
|
3837
3847
|
if (cameraAngle)
|
|
3838
3848
|
{
|
|
3839
3849
|
// apply camera rotation
|
|
3840
3850
|
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
3841
|
-
const
|
|
3842
|
-
|
|
3843
|
-
x = rotatedX;
|
|
3844
|
-
y = rotatedY;
|
|
3851
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
3852
|
+
x = xr; y = yr;
|
|
3845
3853
|
}
|
|
3846
3854
|
return new Vector2(x + cameraPos.x, y + cameraPos.y);
|
|
3847
3855
|
}
|
|
@@ -3852,16 +3860,16 @@ function screenToWorld(screenPos)
|
|
|
3852
3860
|
* @memberof Draw */
|
|
3853
3861
|
function worldToScreen(worldPos)
|
|
3854
3862
|
{
|
|
3863
|
+
ASSERT(isVector2(worldPos), 'worldPos must be a vec2');
|
|
3864
|
+
|
|
3855
3865
|
let x = worldPos.x - cameraPos.x;
|
|
3856
3866
|
let y = worldPos.y - cameraPos.y;
|
|
3857
3867
|
if (cameraAngle)
|
|
3858
3868
|
{
|
|
3859
3869
|
// apply inverse camera rotation
|
|
3860
3870
|
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
3861
|
-
const
|
|
3862
|
-
|
|
3863
|
-
x = rotatedX;
|
|
3864
|
-
y = rotatedY;
|
|
3871
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
3872
|
+
x = xr; y = yr;
|
|
3865
3873
|
}
|
|
3866
3874
|
return new Vector2
|
|
3867
3875
|
(
|
|
@@ -3876,16 +3884,16 @@ function worldToScreen(worldPos)
|
|
|
3876
3884
|
* @memberof Draw */
|
|
3877
3885
|
function screenToWorldDelta(screenDelta)
|
|
3878
3886
|
{
|
|
3887
|
+
ASSERT(isVector2(screenDelta), 'screenDelta must be a vec2');
|
|
3888
|
+
|
|
3879
3889
|
let x = screenDelta.x / cameraScale;
|
|
3880
3890
|
let y = screenDelta.y / -cameraScale;
|
|
3881
3891
|
if (cameraAngle)
|
|
3882
3892
|
{
|
|
3883
3893
|
// apply camera rotation
|
|
3884
3894
|
const c = cos(-cameraAngle), s = sin(-cameraAngle);
|
|
3885
|
-
const
|
|
3886
|
-
|
|
3887
|
-
x = rotatedX;
|
|
3888
|
-
y = rotatedY;
|
|
3895
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
3896
|
+
x = xr; y = yr;
|
|
3889
3897
|
}
|
|
3890
3898
|
return new Vector2(x, y);
|
|
3891
3899
|
}
|
|
@@ -3896,16 +3904,16 @@ function screenToWorldDelta(screenDelta)
|
|
|
3896
3904
|
* @memberof Draw */
|
|
3897
3905
|
function worldToScreenDelta(worldDelta)
|
|
3898
3906
|
{
|
|
3907
|
+
ASSERT(isVector2(worldDelta), 'worldDelta must be a vec2');
|
|
3908
|
+
|
|
3899
3909
|
let x = worldDelta.x;
|
|
3900
3910
|
let y = worldDelta.y;
|
|
3901
3911
|
if (cameraAngle)
|
|
3902
3912
|
{
|
|
3903
3913
|
// apply inverse camera rotation
|
|
3904
3914
|
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
3905
|
-
const
|
|
3906
|
-
|
|
3907
|
-
x = rotatedX;
|
|
3908
|
-
y = rotatedY;
|
|
3915
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
3916
|
+
x = xr; y = yr;
|
|
3909
3917
|
}
|
|
3910
3918
|
return new Vector2(x * cameraScale, y * -cameraScale);
|
|
3911
3919
|
}
|
|
@@ -3918,6 +3926,10 @@ function worldToScreenDelta(worldDelta)
|
|
|
3918
3926
|
* @memberof Draw */
|
|
3919
3927
|
function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
|
|
3920
3928
|
{
|
|
3929
|
+
ASSERT(isVector2(screenPos), 'screenPos must be a vec2');
|
|
3930
|
+
ASSERT(isVector2(screenSize), 'screenSize must be a vec2');
|
|
3931
|
+
ASSERT(isNumber(screenAngle), 'screenAngle must be a number');
|
|
3932
|
+
|
|
3921
3933
|
return [
|
|
3922
3934
|
screenToWorld(screenPos),
|
|
3923
3935
|
screenSize.scale(1/cameraScale),
|
|
@@ -3933,6 +3945,10 @@ function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
|
|
|
3933
3945
|
* @memberof Draw */
|
|
3934
3946
|
function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
|
|
3935
3947
|
{
|
|
3948
|
+
ASSERT(isVector2(worldPos), 'worldPos must be a vec2');
|
|
3949
|
+
ASSERT(isVector2(worldSize), 'worldSize must be a vec2');
|
|
3950
|
+
ASSERT(isNumber(worldAngle), 'worldAngle must be a number');
|
|
3951
|
+
|
|
3936
3952
|
return [
|
|
3937
3953
|
worldToScreen(worldPos),
|
|
3938
3954
|
worldSize.scale(cameraScale),
|
|
@@ -3945,8 +3961,8 @@ function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
|
|
|
3945
3961
|
* @memberof Draw */
|
|
3946
3962
|
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
3947
3963
|
|
|
3948
|
-
/** Check if a point or circle is on screen
|
|
3949
|
-
* If size is a Vector2, uses the
|
|
3964
|
+
/** Check if a box, point, or circle is on screen with a circle test
|
|
3965
|
+
* If size is a Vector2, uses the length as diameter
|
|
3950
3966
|
* This can be used to cull offscreen objects from render or update
|
|
3951
3967
|
* @param {Vector2} pos - world space position
|
|
3952
3968
|
* @param {Vector2|number} size - world space size or diameter
|
|
@@ -3954,12 +3970,30 @@ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
|
3954
3970
|
* @memberof Draw */
|
|
3955
3971
|
function isOnScreen(pos, size=0)
|
|
3956
3972
|
{
|
|
3957
|
-
pos
|
|
3973
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3974
|
+
ASSERT(isVector2(size) || isNumber(size), 'size must be a vec2 or number');
|
|
3975
|
+
|
|
3976
|
+
// optimized circle on screen test
|
|
3977
|
+
// pos = worldToScreen(pos);
|
|
3978
|
+
let x = pos.x - cameraPos.x;
|
|
3979
|
+
let y = pos.y - cameraPos.y;
|
|
3980
|
+
if (cameraAngle)
|
|
3981
|
+
{
|
|
3982
|
+
// apply inverse camera rotation
|
|
3983
|
+
const c = cos(cameraAngle), s = sin(cameraAngle);
|
|
3984
|
+
const xr = x * c - y * s, yr = x * s + y * c;
|
|
3985
|
+
x = xr; y = yr;
|
|
3986
|
+
}
|
|
3987
|
+
x *= cameraScale*2; y *= -cameraScale*2;
|
|
3988
|
+
|
|
3958
3989
|
if (size instanceof Vector2)
|
|
3959
|
-
size =
|
|
3960
|
-
size *= cameraScale
|
|
3961
|
-
|
|
3962
|
-
|
|
3990
|
+
size = size.length(); // use length of vector as diameter
|
|
3991
|
+
size *= cameraScale;
|
|
3992
|
+
|
|
3993
|
+
// check against screen bounds
|
|
3994
|
+
const w = mainCanvasSize.x, h = mainCanvasSize.y;
|
|
3995
|
+
return x + size > -w && x - size < w &&
|
|
3996
|
+
y + size > -h && y - size < h;
|
|
3963
3997
|
}
|
|
3964
3998
|
|
|
3965
3999
|
/** Enable normal or additive blend mode
|
|
@@ -3973,18 +4007,19 @@ function setBlendMode(additive=false, context)
|
|
|
3973
4007
|
context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
3974
4008
|
}
|
|
3975
4009
|
|
|
3976
|
-
/** Combines
|
|
3977
|
-
* This is necessary for things like
|
|
4010
|
+
/** Combines LittleJS canvases onto the main canvas
|
|
4011
|
+
* This is necessary for things like screenshots and video
|
|
3978
4012
|
* @memberof Draw */
|
|
3979
4013
|
function combineCanvases()
|
|
3980
4014
|
{
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
4015
|
+
const w = mainCanvasSize.x, h = mainCanvasSize.y;
|
|
4016
|
+
workCanvas.width = w;
|
|
4017
|
+
workCanvas.height = h;
|
|
4018
|
+
workContext.fillRect(0,0,w,h); // remove background alpha
|
|
4019
|
+
glCopyToContext(workContext);
|
|
4020
|
+
workContext.drawImage(mainCanvas, 0, 0);
|
|
4021
|
+
mainCanvas.width |= 0;
|
|
4022
|
+
mainContext.drawImage(workCanvas, 0, 0);
|
|
3988
4023
|
}
|
|
3989
4024
|
|
|
3990
4025
|
/** Helper function to draw an image with color and additive color applied
|
|
@@ -4001,18 +4036,18 @@ function combineCanvases()
|
|
|
4001
4036
|
* @param {number} dHeight
|
|
4002
4037
|
* @param {Color} color
|
|
4003
4038
|
* @param {Color} [additiveColor]
|
|
4004
|
-
* @param {number} [
|
|
4039
|
+
* @param {number} [bleed] - How many pixels to shrink the source, used to fix bleeding
|
|
4005
4040
|
* @memberof Draw */
|
|
4006
|
-
function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, color, additiveColor,
|
|
4041
|
+
function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight, color, additiveColor, bleed=0)
|
|
4007
4042
|
{
|
|
4008
4043
|
function isWhite(c) { return c.r >= 1 && c.g >= 1 && c.b >= 1; }
|
|
4009
4044
|
function isBlack(c) { return c.r <= 0 && c.g <= 0 && c.b <= 0 && c.a <= 0; }
|
|
4010
|
-
const sx2 =
|
|
4011
|
-
const sy2 =
|
|
4045
|
+
const sx2 = bleed;
|
|
4046
|
+
const sy2 = bleed;
|
|
4012
4047
|
sWidth = max(1,sWidth|0);
|
|
4013
4048
|
sHeight = max(1,sHeight|0);
|
|
4014
|
-
const sWidth2 = sWidth - 2*
|
|
4015
|
-
const sHeight2 = sHeight - 2*
|
|
4049
|
+
const sWidth2 = sWidth - 2*bleed;
|
|
4050
|
+
const sHeight2 = sHeight - 2*bleed;
|
|
4016
4051
|
if (!canvasColorTiles || (additiveColor ? isWhite(color.add(additiveColor)) && additiveColor.a <= 0 : isWhite(color)))
|
|
4017
4052
|
{
|
|
4018
4053
|
// white texture with no additive alpha, no need to tint
|
|
@@ -4023,12 +4058,12 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4023
4058
|
else
|
|
4024
4059
|
{
|
|
4025
4060
|
// copy to offscreen canvas
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4061
|
+
workReadCanvas.width = sWidth;
|
|
4062
|
+
workReadCanvas.height = sHeight;
|
|
4063
|
+
workReadContext.drawImage(image, sx|0, sy|0, sWidth, sHeight, 0, 0, sWidth, sHeight);
|
|
4029
4064
|
|
|
4030
4065
|
// tint image using offscreen work context
|
|
4031
|
-
const imageData =
|
|
4066
|
+
const imageData = workReadContext.getImageData(0, 0, sWidth, sHeight);
|
|
4032
4067
|
const data = imageData.data;
|
|
4033
4068
|
if (additiveColor && !isBlack(additiveColor))
|
|
4034
4069
|
{
|
|
@@ -4037,8 +4072,8 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4037
4072
|
const colorAdd = [additiveColor.r * 255, additiveColor.g * 255, additiveColor.b * 255, additiveColor.a * 255];
|
|
4038
4073
|
for (let i = 0; i < data.length; ++i)
|
|
4039
4074
|
data[i] = data[i] * colorMultiply[i&3] + colorAdd[i&3] |0;
|
|
4040
|
-
|
|
4041
|
-
context.drawImage(
|
|
4075
|
+
workReadContext.putImageData(imageData, 0, 0);
|
|
4076
|
+
context.drawImage(workReadCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
4042
4077
|
}
|
|
4043
4078
|
else
|
|
4044
4079
|
{
|
|
@@ -4049,9 +4084,9 @@ function drawImageColor(context, image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
|
|
|
4049
4084
|
data[i+1] *= color.g;
|
|
4050
4085
|
data[i+2] *= color.b;
|
|
4051
4086
|
}
|
|
4052
|
-
|
|
4087
|
+
workReadContext.putImageData(imageData, 0, 0);
|
|
4053
4088
|
context.globalAlpha = color.a;
|
|
4054
|
-
context.drawImage(
|
|
4089
|
+
context.drawImage(workReadCanvas, sx2, sy2, sWidth2, sHeight2, dx, dy, dWidth, dHeight);
|
|
4055
4090
|
context.globalAlpha = 1;
|
|
4056
4091
|
}
|
|
4057
4092
|
}
|
|
@@ -4113,7 +4148,7 @@ class FontImage
|
|
|
4113
4148
|
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1))
|
|
4114
4149
|
{
|
|
4115
4150
|
// load default font image
|
|
4116
|
-
if (!engineFontImage)
|
|
4151
|
+
if (!image && !engineFontImage)
|
|
4117
4152
|
{
|
|
4118
4153
|
engineFontImage = new Image;
|
|
4119
4154
|
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
@@ -4136,23 +4171,14 @@ class FontImage
|
|
|
4136
4171
|
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center, context);
|
|
4137
4172
|
}
|
|
4138
4173
|
|
|
4139
|
-
/** Draw text
|
|
4140
|
-
* @param {string|number} text
|
|
4141
|
-
* @param {Vector2} pos
|
|
4142
|
-
* @param {number} [scale]
|
|
4143
|
-
* @param {boolean} [center]
|
|
4144
|
-
*/
|
|
4145
|
-
drawTextOverlay(text, pos, scale=4, center)
|
|
4146
|
-
{ this.drawText(text, pos, scale, center, overlayContext); }
|
|
4147
|
-
|
|
4148
|
-
/** Draw text on overlay canvas in screen space using the image font
|
|
4174
|
+
/** Draw text in screen space using the image font
|
|
4149
4175
|
* @param {string|number} text
|
|
4150
4176
|
* @param {Vector2} pos
|
|
4151
4177
|
* @param {number} [scale]
|
|
4152
4178
|
* @param {boolean} [center]
|
|
4153
4179
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4154
4180
|
*/
|
|
4155
|
-
drawTextScreen(text, pos, scale=4, center=true, context=
|
|
4181
|
+
drawTextScreen(text, pos, scale=4, center=true, context=mainContext)
|
|
4156
4182
|
{
|
|
4157
4183
|
context.save();
|
|
4158
4184
|
const size = this.tileSize;
|
|
@@ -4423,6 +4449,16 @@ function gamepadConnected(gamepad=gamepadPrimary)
|
|
|
4423
4449
|
return !!inputData[gamepad+1];
|
|
4424
4450
|
}
|
|
4425
4451
|
|
|
4452
|
+
/** Returns how many control sticks the passed in gamepad has
|
|
4453
|
+
* @param {number} [gamepad]
|
|
4454
|
+
* @return {number}
|
|
4455
|
+
* @memberof Input */
|
|
4456
|
+
function gamepadStickCount(gamepad=gamepadPrimary)
|
|
4457
|
+
{
|
|
4458
|
+
ASSERT(isNumber(gamepad), 'gamepad must be a number');
|
|
4459
|
+
return gamepadStickData[gamepad]?.length ?? 0;
|
|
4460
|
+
}
|
|
4461
|
+
|
|
4426
4462
|
/** True if a touch device has been detected
|
|
4427
4463
|
* @memberof Input */
|
|
4428
4464
|
const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
@@ -4763,9 +4799,11 @@ function inputUpdate()
|
|
|
4763
4799
|
dpad.set(x, -y);
|
|
4764
4800
|
sticks[0] = dpad.clampLength(); // clamp to circle
|
|
4765
4801
|
}
|
|
4766
|
-
const rightTouchStick = touchGamepadSticks[1] ?? vec2();
|
|
4767
4802
|
if (touchGamepadButtonCount === 1)
|
|
4803
|
+
{
|
|
4804
|
+
const rightTouchStick = touchGamepadSticks[1] ?? vec2();
|
|
4768
4805
|
sticks[1] = applyDeadZones(rightTouchStick);
|
|
4806
|
+
}
|
|
4769
4807
|
|
|
4770
4808
|
// read virtual gamepad buttons
|
|
4771
4809
|
const data = inputData[1] ?? (inputData[1] = []);
|
|
@@ -4890,7 +4928,7 @@ function inputRender()
|
|
|
4890
4928
|
return;
|
|
4891
4929
|
|
|
4892
4930
|
// setup the canvas
|
|
4893
|
-
const context =
|
|
4931
|
+
const context = mainContext;
|
|
4894
4932
|
context.save();
|
|
4895
4933
|
context.globalAlpha = alpha*touchGamepadAlpha;
|
|
4896
4934
|
context.strokeStyle = '#fff';
|
|
@@ -5132,12 +5170,12 @@ class Sound
|
|
|
5132
5170
|
///////////////////////////////////////////////////////////////////////////////
|
|
5133
5171
|
|
|
5134
5172
|
/**
|
|
5135
|
-
* Sound Wave Object -
|
|
5136
|
-
* - this can be used to play wave, mp3, and ogg files
|
|
5173
|
+
* Sound Wave Object - Loads and stores an audio file for later use
|
|
5174
|
+
* - this can be used to load and play wave, mp3, and ogg files
|
|
5137
5175
|
* @extends Sound
|
|
5138
5176
|
* @memberof Audio
|
|
5139
5177
|
* @example
|
|
5140
|
-
* //
|
|
5178
|
+
* // load an audio asset file
|
|
5141
5179
|
* const sound_example = new SoundWave('sound.mp3');
|
|
5142
5180
|
*
|
|
5143
5181
|
* // play the sound
|
|
@@ -5857,8 +5895,7 @@ class CanvasLayer extends EngineObject
|
|
|
5857
5895
|
/** Destroy this canvas layer */
|
|
5858
5896
|
destroy()
|
|
5859
5897
|
{
|
|
5860
|
-
if (this.destroyed)
|
|
5861
|
-
return;
|
|
5898
|
+
if (this.destroyed) return;
|
|
5862
5899
|
|
|
5863
5900
|
this.textureInfo.destroyWebGLTexture();
|
|
5864
5901
|
super.destroy();
|
|
@@ -6090,8 +6127,8 @@ class TileLayer extends CanvasLayer
|
|
|
6090
6127
|
if (!this.context) return;
|
|
6091
6128
|
|
|
6092
6129
|
// save current render settings
|
|
6093
|
-
/** @type {[HTMLCanvasElement|OffscreenCanvas, CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
6094
|
-
this.savedRenderSettings = [drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale];
|
|
6130
|
+
/** @type {[HTMLCanvasElement|OffscreenCanvas, CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number, Color]} */
|
|
6131
|
+
this.savedRenderSettings = [drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor];
|
|
6095
6132
|
|
|
6096
6133
|
// set the draw canvas and context to this layer
|
|
6097
6134
|
// use camera settings to match this layer's canvas
|
|
@@ -6100,6 +6137,7 @@ class TileLayer extends CanvasLayer
|
|
|
6100
6137
|
cameraPos = this.size.scale(.5);
|
|
6101
6138
|
const tileSize = this.tileInfo ? this.tileInfo.size : vec2(1);
|
|
6102
6139
|
cameraScale = tileSize.x;
|
|
6140
|
+
canvasClearColor = CLEAR_BLACK;
|
|
6103
6141
|
mainCanvasSize = this.size.multiply(tileSize);
|
|
6104
6142
|
if (clear)
|
|
6105
6143
|
{
|
|
@@ -6109,7 +6147,7 @@ class TileLayer extends CanvasLayer
|
|
|
6109
6147
|
}
|
|
6110
6148
|
|
|
6111
6149
|
// disable smoothing for pixel art
|
|
6112
|
-
|
|
6150
|
+
drawContext.imageSmoothingEnabled = !tilesPixelated;
|
|
6113
6151
|
|
|
6114
6152
|
// setup gl rendering if enabled
|
|
6115
6153
|
glPreRender();
|
|
@@ -6125,7 +6163,7 @@ class TileLayer extends CanvasLayer
|
|
|
6125
6163
|
//debugSaveCanvas(this.canvas);
|
|
6126
6164
|
|
|
6127
6165
|
// set stuff back to normal
|
|
6128
|
-
[drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale] = this.savedRenderSettings;
|
|
6166
|
+
[drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor] = this.savedRenderSettings;
|
|
6129
6167
|
}
|
|
6130
6168
|
|
|
6131
6169
|
/** Draw the tile at a given position in the tile grid
|
|
@@ -6410,9 +6448,9 @@ class ParticleEmitter extends EngineObject
|
|
|
6410
6448
|
/** @property {Color} - Color at start of life 2, randomized between start colors */
|
|
6411
6449
|
this.colorStartB = colorStartB.copy();
|
|
6412
6450
|
/** @property {Color} - Color at end of life 1, randomized between end colors */
|
|
6413
|
-
this.colorEndA
|
|
6451
|
+
this.colorEndA = colorEndA.copy();
|
|
6414
6452
|
/** @property {Color} - Color at end of life 2, randomized between end colors */
|
|
6415
|
-
this.colorEndB
|
|
6453
|
+
this.colorEndB = colorEndB.copy();
|
|
6416
6454
|
/** @property {boolean} - Should color be randomized linearly or across each component */
|
|
6417
6455
|
this.randomColorLinear = randomColorLinear;
|
|
6418
6456
|
|
|
@@ -6496,8 +6534,10 @@ class ParticleEmitter extends EngineObject
|
|
|
6496
6534
|
if (debugParticles)
|
|
6497
6535
|
{
|
|
6498
6536
|
// show emitter bounds
|
|
6499
|
-
|
|
6500
|
-
|
|
6537
|
+
if (typeof this.emitSize === 'number')
|
|
6538
|
+
debugCircle(this.pos, this.emitSize/2, '#0f0');
|
|
6539
|
+
else
|
|
6540
|
+
debugRect(this.pos, this.emitSize, '#0f0', 0, this.angle);
|
|
6501
6541
|
}
|
|
6502
6542
|
}
|
|
6503
6543
|
|
|
@@ -6507,8 +6547,8 @@ class ParticleEmitter extends EngineObject
|
|
|
6507
6547
|
{
|
|
6508
6548
|
// spawn a particle
|
|
6509
6549
|
let pos = typeof this.emitSize === 'number' ? // check if number was used
|
|
6510
|
-
randInCircle(this.emitSize/2)
|
|
6511
|
-
: vec2(rand(-.5,.5), rand(-.5,.5))
|
|
6550
|
+
randInCircle(this.emitSize/2) // circle emitter
|
|
6551
|
+
: vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
|
|
6512
6552
|
.multiply(this.emitSize).rotate(this.angle)
|
|
6513
6553
|
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
6514
6554
|
if (!this.localSpace)
|
|
@@ -6836,10 +6876,10 @@ class Medal
|
|
|
6836
6876
|
*/
|
|
6837
6877
|
render(hidePercent=0)
|
|
6838
6878
|
{
|
|
6839
|
-
const context =
|
|
6879
|
+
const context = mainContext;
|
|
6840
6880
|
const width = min(medalDisplaySize.x, mainCanvas.width);
|
|
6841
6881
|
const height = medalDisplaySize.y;
|
|
6842
|
-
const x =
|
|
6882
|
+
const x = mainCanvas.width - width;
|
|
6843
6883
|
const y = -height*hidePercent;
|
|
6844
6884
|
const backgroundColor = hsl(0,0,.9);
|
|
6845
6885
|
|
|
@@ -6880,7 +6920,7 @@ class Medal
|
|
|
6880
6920
|
{
|
|
6881
6921
|
// draw the image or icon
|
|
6882
6922
|
if (this.image)
|
|
6883
|
-
|
|
6923
|
+
mainContext.drawImage(this.image, pos.x-size/2, pos.y-size/2, size, size);
|
|
6884
6924
|
else
|
|
6885
6925
|
drawTextScreen(this.icon, pos, size*.7, BLACK);
|
|
6886
6926
|
}
|
|
@@ -6901,7 +6941,7 @@ class Medal
|
|
|
6901
6941
|
* @namespace WebGL
|
|
6902
6942
|
*/
|
|
6903
6943
|
|
|
6904
|
-
/** The WebGL canvas which appears
|
|
6944
|
+
/** The WebGL canvas which appears below the main canvas
|
|
6905
6945
|
* @type {HTMLCanvasElement}
|
|
6906
6946
|
* @memberof WebGL */
|
|
6907
6947
|
let glCanvas;
|
|
@@ -6931,7 +6971,7 @@ const gl_MAX_POLY_VERTEXES = gl_ARRAY_BUFFER_SIZE / gl_POLY_VERTEX_BYTE_STRIDE |
|
|
|
6931
6971
|
///////////////////////////////////////////////////////////////////////////////
|
|
6932
6972
|
|
|
6933
6973
|
// Initialize WebGL, called automatically by the engine
|
|
6934
|
-
function glInit()
|
|
6974
|
+
function glInit(rootElement)
|
|
6935
6975
|
{
|
|
6936
6976
|
// keep set of texture infos so they can be restored if context is lost
|
|
6937
6977
|
glTextureInfos = new Set;
|
|
@@ -6955,8 +6995,7 @@ function glInit()
|
|
|
6955
6995
|
return;
|
|
6956
6996
|
}
|
|
6957
6997
|
|
|
6958
|
-
// attach the WebGL canvas
|
|
6959
|
-
const rootElement = mainCanvas.parentElement;
|
|
6998
|
+
// attach the WebGL canvas;
|
|
6960
6999
|
rootElement.appendChild(glCanvas);
|
|
6961
7000
|
|
|
6962
7001
|
// startup webgl
|
|
@@ -7174,6 +7213,9 @@ function glClearCanvas()
|
|
|
7174
7213
|
glCanvas.width = drawCanvas.width;
|
|
7175
7214
|
glCanvas.height = drawCanvas.height;
|
|
7176
7215
|
glContext.viewport(0, 0, glCanvas.width, glCanvas.height);
|
|
7216
|
+
const color = canvasClearColor;
|
|
7217
|
+
if (color.a > 0)
|
|
7218
|
+
glContext.clearColor(color.r, color.g, color.b, color.a);
|
|
7177
7219
|
glContext.clear(glContext.COLOR_BUFFER_BIT);
|
|
7178
7220
|
}
|
|
7179
7221
|
|
|
@@ -7403,7 +7445,6 @@ function glDraw(x, y, sizeX, sizeY, angle=0, uv0X=0, uv0Y=0, uv1X=1, uv1Y=1, rgb
|
|
|
7403
7445
|
glFlush();
|
|
7404
7446
|
glSetInstancedMode();
|
|
7405
7447
|
|
|
7406
|
-
glPolyMode = false;
|
|
7407
7448
|
let offset = glBatchCount++ * gl_INDICES_PER_INSTANCE;
|
|
7408
7449
|
glPositionData[offset++] = x;
|
|
7409
7450
|
glPositionData[offset++] = y;
|
|
@@ -7924,13 +7965,12 @@ class PostProcessPlugin
|
|
|
7924
7965
|
{
|
|
7925
7966
|
/** Create global post processing shader
|
|
7926
7967
|
* @param {string} shaderCode
|
|
7927
|
-
* @param {boolean} [includeOverlay]
|
|
7928
7968
|
* @param {boolean} [includeMainCanvas]
|
|
7929
7969
|
* @example
|
|
7930
7970
|
* // create the post process plugin object
|
|
7931
7971
|
* new PostProcessPlugin(shaderCode);
|
|
7932
7972
|
*/
|
|
7933
|
-
constructor(shaderCode,
|
|
7973
|
+
constructor(shaderCode, includeMainCanvas=true)
|
|
7934
7974
|
{
|
|
7935
7975
|
ASSERT(!postProcess, 'Post process already initialized');
|
|
7936
7976
|
postProcess = this;
|
|
@@ -8002,17 +8042,19 @@ class PostProcessPlugin
|
|
|
8002
8042
|
// clear out the buffer
|
|
8003
8043
|
glFlush();
|
|
8004
8044
|
|
|
8005
|
-
|
|
8045
|
+
// setup texture
|
|
8046
|
+
glContext.activeTexture(glContext.TEXTURE0);
|
|
8047
|
+
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
8048
|
+
if (includeMainCanvas)
|
|
8006
8049
|
{
|
|
8007
|
-
// copy
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
}
|
|
8050
|
+
// copy main canvas to work canvas
|
|
8051
|
+
workCanvas.width = mainCanvasSize.x;
|
|
8052
|
+
workCanvas.height = mainCanvasSize.y;
|
|
8053
|
+
glCopyToContext(workContext);
|
|
8054
|
+
workContext.drawImage(mainCanvas, 0, 0);
|
|
8055
|
+
|
|
8056
|
+
// copy work canvas to texture
|
|
8057
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, workCanvas);
|
|
8016
8058
|
}
|
|
8017
8059
|
|
|
8018
8060
|
// setup shader program to draw a quad
|
|
@@ -8021,14 +8063,6 @@ class PostProcessPlugin
|
|
|
8021
8063
|
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, 1);
|
|
8022
8064
|
glContext.disable(glContext.BLEND);
|
|
8023
8065
|
|
|
8024
|
-
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
8025
|
-
glContext.activeTexture(glContext.TEXTURE0);
|
|
8026
|
-
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
8027
|
-
if (includeMainCanvas || includeOverlay)
|
|
8028
|
-
{
|
|
8029
|
-
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, mainCanvas);
|
|
8030
|
-
}
|
|
8031
|
-
|
|
8032
8066
|
// set vertex position attribute
|
|
8033
8067
|
const vertexByteStride = 8;
|
|
8034
8068
|
const pLocation = glContext.getAttribLocation(postProcess.shader, 'p');
|
|
@@ -8257,12 +8291,14 @@ class UISystemPlugin
|
|
|
8257
8291
|
* // create the ui plugin object
|
|
8258
8292
|
* new UISystemPlugin;
|
|
8259
8293
|
*/
|
|
8260
|
-
constructor(context=
|
|
8294
|
+
constructor(context=mainContext)
|
|
8261
8295
|
{
|
|
8262
8296
|
ASSERT(!uiSystem, 'UI system already initialized');
|
|
8263
8297
|
uiSystem = this;
|
|
8264
8298
|
|
|
8265
8299
|
// default settings
|
|
8300
|
+
/** @property {boolean} - Activate when mouse is pressed down instead of clicked */
|
|
8301
|
+
this.activateOnPress = false;
|
|
8266
8302
|
/** @property {Color} - Default fill color for UI elements */
|
|
8267
8303
|
this.defaultColor = WHITE;
|
|
8268
8304
|
/** @property {Color} - Default outline color for UI elements */
|
|
@@ -8303,8 +8339,6 @@ class UISystemPlugin
|
|
|
8303
8339
|
// navigation properties
|
|
8304
8340
|
/** @property {UIObject} - Object currently selected by navigation (gamepad or keyboard) */
|
|
8305
8341
|
this.navigationObject = undefined;
|
|
8306
|
-
/** @property {number} - Gamepad index to use for UI navigation */
|
|
8307
|
-
this.navigationGamepadIndex = 0;
|
|
8308
8342
|
/** @property {Timer} - Cooldown timer for navigation inputs */
|
|
8309
8343
|
this.navigationTimer = new Timer(undefined, true);
|
|
8310
8344
|
/** @property {number} - Time between navigation inputs in seconds */
|
|
@@ -8712,9 +8746,8 @@ class UISystemPlugin
|
|
|
8712
8746
|
const both = uiSystem.navigationDirection === 2;
|
|
8713
8747
|
if (isUsingGamepad)
|
|
8714
8748
|
{
|
|
8715
|
-
const
|
|
8716
|
-
const
|
|
8717
|
-
const dpad = gamepadDpad(gamepad);
|
|
8749
|
+
const stick = gamepadStick(0, gamepadPrimary);
|
|
8750
|
+
const dpad = gamepadDpad(gamepadPrimary);
|
|
8718
8751
|
if (both)
|
|
8719
8752
|
return -(stick.y || dpad.y) || (stick.x || dpad.x);
|
|
8720
8753
|
return vertical ? -(stick.y || dpad.y) : (stick.x || dpad.x);
|
|
@@ -8740,9 +8773,8 @@ class UISystemPlugin
|
|
|
8740
8773
|
const vertical = uiSystem.navigationDirection === 1;
|
|
8741
8774
|
if (isUsingGamepad)
|
|
8742
8775
|
{
|
|
8743
|
-
const
|
|
8744
|
-
const
|
|
8745
|
-
const dpad = gamepadDpad(gamepad);
|
|
8776
|
+
const stick = gamepadStick(0, gamepadPrimary);
|
|
8777
|
+
const dpad = gamepadDpad(gamepadPrimary);
|
|
8746
8778
|
return !vertical ? (stick.y || dpad.y) : (stick.x || dpad.x);
|
|
8747
8779
|
}
|
|
8748
8780
|
const back = !vertical ? 'ArrowUp' : 'ArrowLeft';
|
|
@@ -8754,8 +8786,7 @@ class UISystemPlugin
|
|
|
8754
8786
|
* @return {boolean} */
|
|
8755
8787
|
getNavigationWasPressed()
|
|
8756
8788
|
{
|
|
8757
|
-
|
|
8758
|
-
return isUsingGamepad ? gamepadWasPressed(0, gamepad) :
|
|
8789
|
+
return isUsingGamepad ? gamepadWasPressed(0, gamepadPrimary) :
|
|
8759
8790
|
keyWasPressed('Space') || keyWasPressed('Enter');
|
|
8760
8791
|
}
|
|
8761
8792
|
|
|
@@ -8919,13 +8950,15 @@ class UIObject
|
|
|
8919
8950
|
uiSystem.uiObjects.push(this);
|
|
8920
8951
|
}
|
|
8921
8952
|
|
|
8922
|
-
/** Add a child UIObject to this object
|
|
8923
|
-
* @param {UIObject} child
|
|
8953
|
+
/** Add a child UIObject to this object, returns child for chaining
|
|
8954
|
+
* @param {UIObject} child
|
|
8955
|
+
* @return {UIObject} The child object added */
|
|
8924
8956
|
addChild(child)
|
|
8925
8957
|
{
|
|
8926
8958
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
8927
8959
|
this.children.push(child);
|
|
8928
8960
|
child.parent = this;
|
|
8961
|
+
return child;
|
|
8929
8962
|
}
|
|
8930
8963
|
|
|
8931
8964
|
/** Remove a child UIObject from this object
|
|
@@ -8998,8 +9031,16 @@ class UIObject
|
|
|
8998
9031
|
if (uiSystem.activeObject && !isActive)
|
|
8999
9032
|
uiSystem.activeObject.onRelease();
|
|
9000
9033
|
uiSystem.activeObject = this;
|
|
9034
|
+
|
|
9035
|
+
if (uiSystem.activateOnPress)
|
|
9036
|
+
{
|
|
9037
|
+
this.onClick();
|
|
9038
|
+
if (!this.soundPress && this.soundClick)
|
|
9039
|
+
this.soundClick.play();
|
|
9040
|
+
}
|
|
9001
9041
|
}
|
|
9002
9042
|
}
|
|
9043
|
+
if (!uiSystem.activateOnPress)
|
|
9003
9044
|
if (!mouseDown && this.isActiveObject() && this.interactive)
|
|
9004
9045
|
{
|
|
9005
9046
|
this.onClick();
|
|
@@ -11439,33 +11480,33 @@ async function box2dInit()
|
|
|
11439
11480
|
color = getDebugColor(color);
|
|
11440
11481
|
point1 = box2d.vec2FromPointer(point1);
|
|
11441
11482
|
point2 = box2d.vec2FromPointer(point2);
|
|
11442
|
-
drawLine(point1, point2, debugLineWidth, color, vec2(), 0, false
|
|
11483
|
+
drawLine(point1, point2, debugLineWidth, color, vec2(), 0, false);
|
|
11443
11484
|
};
|
|
11444
11485
|
debugDraw.DrawPolygon = function(vertices, vertexCount, color)
|
|
11445
11486
|
{
|
|
11446
11487
|
color = getDebugColor(color);
|
|
11447
11488
|
const points = getPointsList(vertices, vertexCount);
|
|
11448
|
-
drawPoly(points, CLEAR_WHITE, debugLineWidth, color, vec2(), 0, false
|
|
11489
|
+
drawPoly(points, CLEAR_WHITE, debugLineWidth, color, vec2(), 0, false);
|
|
11449
11490
|
};
|
|
11450
11491
|
debugDraw.DrawSolidPolygon = function(vertices, vertexCount, color)
|
|
11451
11492
|
{
|
|
11452
11493
|
color = getDebugColor(color);
|
|
11453
11494
|
const points = getPointsList(vertices, vertexCount);
|
|
11454
|
-
drawPoly(points, color, 0, color, vec2(), 0, false
|
|
11495
|
+
drawPoly(points, color, 0, color, vec2(), 0, false);
|
|
11455
11496
|
};
|
|
11456
11497
|
debugDraw.DrawCircle = function(center, radius, color)
|
|
11457
11498
|
{
|
|
11458
11499
|
color = getDebugColor(color);
|
|
11459
11500
|
center = box2d.vec2FromPointer(center);
|
|
11460
|
-
drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false
|
|
11501
|
+
drawCircle(center, radius*2, CLEAR_WHITE, debugLineWidth, color, false);
|
|
11461
11502
|
};
|
|
11462
11503
|
debugDraw.DrawSolidCircle = function(center, radius, axis, color)
|
|
11463
11504
|
{
|
|
11464
11505
|
color = getDebugColor(color);
|
|
11465
11506
|
center = box2d.vec2FromPointer(center);
|
|
11466
11507
|
axis = box2d.vec2FromPointer(axis).scale(radius);
|
|
11467
|
-
drawCircle(center, radius*2, color, debugLineWidth, color, false
|
|
11468
|
-
drawLine(vec2(), axis, debugLineWidth, color, center, 0, false
|
|
11508
|
+
drawCircle(center, radius*2, color, debugLineWidth, color, false);
|
|
11509
|
+
drawLine(vec2(), axis, debugLineWidth, color, center, 0, false);
|
|
11469
11510
|
};
|
|
11470
11511
|
debugDraw.DrawTransform = function(transform)
|
|
11471
11512
|
{
|
|
@@ -11474,8 +11515,8 @@ async function box2dInit()
|
|
|
11474
11515
|
const angle = -transform.get_q().GetAngle();
|
|
11475
11516
|
const p1 = vec2(1,0), c1 = rgb(.75,0,0,.8);
|
|
11476
11517
|
const p2 = vec2(0,1), c2 = rgb(0,.75,0,.8);
|
|
11477
|
-
drawLine(vec2(), p1, debugLineWidth, c1, pos, angle, false
|
|
11478
|
-
drawLine(vec2(), p2, debugLineWidth, c2, pos, angle, false
|
|
11518
|
+
drawLine(vec2(), p1, debugLineWidth, c1, pos, angle, false);
|
|
11519
|
+
drawLine(vec2(), p2, debugLineWidth, c2, pos, angle, false);
|
|
11479
11520
|
}
|
|
11480
11521
|
|
|
11481
11522
|
debugDraw.AppendFlags(box2d.instance.b2Draw.e_shapeBit);
|
|
@@ -11495,8 +11536,8 @@ async function box2dInit()
|
|
|
11495
11536
|
|
|
11496
11537
|
///////////////////////////////////////////////////////////////////////////////
|
|
11497
11538
|
|
|
11498
|
-
/** Draw a scalable nine-slice UI element to the
|
|
11499
|
-
* This function can not apply color because it draws using the
|
|
11539
|
+
/** Draw a scalable nine-slice UI element to the main canvas in screen space
|
|
11540
|
+
* This function can not apply color because it draws using the 2d context
|
|
11500
11541
|
* @param {Vector2} pos - Screen space position
|
|
11501
11542
|
* @param {Vector2} size - Screen space size
|
|
11502
11543
|
* @param {TileInfo} startTile - Starting tile for the nine-slice pattern
|
|
@@ -11506,7 +11547,7 @@ async function box2dInit()
|
|
|
11506
11547
|
* @memberof DrawUtilities */
|
|
11507
11548
|
function drawNineSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
11508
11549
|
{
|
|
11509
|
-
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true
|
|
11550
|
+
drawNineSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true);
|
|
11510
11551
|
}
|
|
11511
11552
|
|
|
11512
11553
|
/** Draw a scalable nine-slice UI element in world space
|
|
@@ -11555,8 +11596,8 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
11555
11596
|
}
|
|
11556
11597
|
}
|
|
11557
11598
|
|
|
11558
|
-
/** Draw a scalable three-slice UI element to the
|
|
11559
|
-
* This function can not apply color because it draws using the
|
|
11599
|
+
/** Draw a scalable three-slice UI element to the main canvas in screen space
|
|
11600
|
+
* This function can not apply color because it draws using the 2d context
|
|
11560
11601
|
* @param {Vector2} pos - Screen space position
|
|
11561
11602
|
* @param {Vector2} size - Screen space size
|
|
11562
11603
|
* @param {TileInfo} startTile - Starting tile for the three-slice pattern
|
|
@@ -11566,7 +11607,7 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
11566
11607
|
* @memberof DrawUtilities */
|
|
11567
11608
|
function drawThreeSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2, angle=0)
|
|
11568
11609
|
{
|
|
11569
|
-
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true
|
|
11610
|
+
drawThreeSlice(pos, size, startTile, WHITE, borderSize, BLACK, extraSpace, angle, false, true);
|
|
11570
11611
|
}
|
|
11571
11612
|
|
|
11572
11613
|
/** Draw a scalable three-slice UI element in world space
|