littlejsengine 1.15.3 → 1.15.9
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 +178 -55
- package/dist/littlejs.esm.js +1400 -746
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1390 -745
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1343 -714
- package/examples/box2d/game.js +1 -1
- package/examples/electron/index.html +2 -2
- package/examples/index.html +4 -3
- package/examples/particles/index.html +22 -6
- package/examples/platformer/game.js +7 -0
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/box2dPool.js +9 -10
- package/examples/shorts/empty.js +1 -1
- package/examples/shorts/input.js +30 -25
- package/examples/shorts/tileLayer.js +2 -2
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/uiSystem.js +26 -15
- package/examples/starter/index.html +2 -2
- package/examples/uiSystem/game.js +26 -17
- package/package.json +1 -1
- package/plugins/box2d.js +1 -1
- package/plugins/desktop.ini +2 -0
- package/plugins/pluginExport.js +2 -0
- package/plugins/uiSystem.js +476 -91
- package/src/engine.js +215 -193
- package/src/engineDebug.js +48 -32
- package/src/engineDraw.js +25 -8
- package/src/engineExport.js +8 -1
- package/src/engineInput.js +537 -380
- package/src/engineObject.js +24 -22
- package/src/engineParticles.js +8 -6
- package/src/engineSettings.js +35 -6
- package/src/engineTileLayer.js +3 -4
- package/src/engineUtilities.js +17 -1
package/src/engine.js
CHANGED
|
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
|
|
|
30
30
|
* @type {string}
|
|
31
31
|
* @default
|
|
32
32
|
* @memberof Engine */
|
|
33
|
-
const engineVersion = '1.15.
|
|
33
|
+
const engineVersion = '1.15.9';
|
|
34
34
|
|
|
35
35
|
/** Frames per second to update
|
|
36
36
|
* @type {number}
|
|
@@ -276,7 +276,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
276
276
|
o.destroyed || o.render();
|
|
277
277
|
gameRenderPost();
|
|
278
278
|
pluginList.forEach(plugin=>plugin.render?.());
|
|
279
|
-
|
|
279
|
+
inputRender();
|
|
280
280
|
debugRender();
|
|
281
281
|
glFlush();
|
|
282
282
|
debugVideoCaptureUpdate();
|
|
@@ -305,23 +305,48 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
305
305
|
|
|
306
306
|
if (canvasFixedSize.x)
|
|
307
307
|
{
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
308
|
+
// set canvas fixed size
|
|
309
|
+
mainCanvasSize = canvasFixedSize.copy();
|
|
310
|
+
|
|
311
|
+
// fit to window using css width and height
|
|
312
|
+
const innerAspect = innerWidth / innerHeight;
|
|
313
|
+
const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
|
|
314
|
+
const w = innerAspect < fixedAspect ? '100%' : '';
|
|
315
|
+
const h = innerAspect < fixedAspect ? '' : '100%';
|
|
316
|
+
overlayCanvas.style.width = mainCanvas.style.width = w;
|
|
317
|
+
overlayCanvas.style.height = mainCanvas.style.height = h;
|
|
318
|
+
if (glCanvas)
|
|
319
|
+
{
|
|
320
|
+
glCanvas.style.width = w;
|
|
321
|
+
glCanvas.style.height = h;
|
|
322
|
+
}
|
|
317
323
|
}
|
|
318
324
|
else
|
|
319
325
|
{
|
|
320
|
-
//
|
|
321
|
-
|
|
322
|
-
|
|
326
|
+
// get main canvas size based on window size
|
|
327
|
+
mainCanvasSize.x = min(innerWidth, canvasMaxSize.x);
|
|
328
|
+
mainCanvasSize.y = min(innerHeight, canvasMaxSize.y);
|
|
329
|
+
|
|
330
|
+
// responsive aspect ratio with native resolution
|
|
331
|
+
const innerAspect = innerWidth / innerHeight;
|
|
332
|
+
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
333
|
+
{
|
|
334
|
+
// full height
|
|
335
|
+
const w = mainCanvasSize.y * canvasMaxAspect | 0;
|
|
336
|
+
mainCanvasSize.x = min(w, canvasMaxSize.x);
|
|
337
|
+
}
|
|
338
|
+
else if (innerAspect < canvasMinAspect)
|
|
339
|
+
{
|
|
340
|
+
// full width
|
|
341
|
+
const h = mainCanvasSize.x / canvasMinAspect | 0;
|
|
342
|
+
mainCanvasSize.y = min(h, canvasMaxSize.y);
|
|
343
|
+
}
|
|
323
344
|
}
|
|
324
345
|
|
|
346
|
+
// clear main and overlay canvas and set size
|
|
347
|
+
overlayCanvas.width = mainCanvas.width = mainCanvasSize.x;
|
|
348
|
+
overlayCanvas.height = mainCanvas.height = mainCanvasSize.y;
|
|
349
|
+
|
|
325
350
|
// apply the clear color to main canvas
|
|
326
351
|
if (canvasClearColor.a > 0)
|
|
327
352
|
{
|
|
@@ -330,13 +355,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
330
355
|
mainContext.fillStyle = BLACK.toString();
|
|
331
356
|
}
|
|
332
357
|
|
|
333
|
-
// clear overlay canvas and set size
|
|
334
|
-
overlayCanvas.width = mainCanvas.width;
|
|
335
|
-
overlayCanvas.height = mainCanvas.height;
|
|
336
|
-
|
|
337
|
-
// save canvas size
|
|
338
|
-
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
339
|
-
|
|
340
358
|
// set default line join and cap
|
|
341
359
|
const lineJoin = 'round', lineCap = 'round';
|
|
342
360
|
mainContext.lineJoin = overlayContext.lineJoin = lineJoin;
|
|
@@ -360,7 +378,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
360
378
|
'user-select:none;' + // prevent hold to select
|
|
361
379
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
362
380
|
'touch-action:none;' + // prevent mobile pinch to resize
|
|
363
|
-
'-webkit-touch-callout:none'
|
|
381
|
+
'-webkit-touch-callout:none'; // compatibility for ios
|
|
364
382
|
rootElement.style.cssText = styleRoot;
|
|
365
383
|
drawCanvas = mainCanvas = document.createElement('canvas');
|
|
366
384
|
rootElement.appendChild(mainCanvas);
|
|
@@ -377,7 +395,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
377
395
|
rootElement.appendChild(overlayCanvas);
|
|
378
396
|
overlayContext = overlayCanvas.getContext('2d');
|
|
379
397
|
|
|
380
|
-
//
|
|
398
|
+
// setup canvases
|
|
399
|
+
// transform way is still more reliable then flexbox or grid
|
|
381
400
|
const styleCanvas = 'position:absolute;'+ // allow canvases to overlap
|
|
382
401
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center on screen
|
|
383
402
|
mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
@@ -441,9 +460,176 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
441
460
|
// wait for all the promises to finish
|
|
442
461
|
await Promise.all(promises);
|
|
443
462
|
return startEngine();
|
|
463
|
+
|
|
464
|
+
///////////////////////////////////////////////////////////////////////////
|
|
465
|
+
// LittleJS Splash Screen
|
|
466
|
+
function drawEngineSplashScreen(t)
|
|
467
|
+
{
|
|
468
|
+
const x = overlayContext;
|
|
469
|
+
const w = overlayCanvas.width = innerWidth;
|
|
470
|
+
const h = overlayCanvas.height = innerHeight;
|
|
471
|
+
|
|
472
|
+
{
|
|
473
|
+
// background
|
|
474
|
+
const p3 = percent(t, 1, .8);
|
|
475
|
+
const p4 = percent(t, 0, .5);
|
|
476
|
+
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
477
|
+
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
478
|
+
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
479
|
+
x.save();
|
|
480
|
+
x.fillStyle = g;
|
|
481
|
+
x.fillRect(0,0,w,h);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// draw LittleJS logo...
|
|
485
|
+
const rect = (X, Y, W, H, C)=>
|
|
486
|
+
{
|
|
487
|
+
x.beginPath();
|
|
488
|
+
x.rect(X,Y,W,C?H*p:H);
|
|
489
|
+
x.fillStyle = C;
|
|
490
|
+
C ? x.fill() : x.stroke();
|
|
491
|
+
};
|
|
492
|
+
const line = (X, Y, Z, W)=>
|
|
493
|
+
{
|
|
494
|
+
x.beginPath();
|
|
495
|
+
x.lineTo(X,Y);
|
|
496
|
+
x.lineTo(Z,W);
|
|
497
|
+
x.stroke();
|
|
498
|
+
};
|
|
499
|
+
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
500
|
+
{
|
|
501
|
+
const D = (A+B)/2, E = p*(B-A)/2;
|
|
502
|
+
x.beginPath();
|
|
503
|
+
F && x.lineTo(X,Y);
|
|
504
|
+
x.arc(X,Y,R,D-E,D+E);
|
|
505
|
+
x.fillStyle = C;
|
|
506
|
+
C ? x.fill() : x.stroke();
|
|
507
|
+
};
|
|
508
|
+
const color = (c=0, l=0) =>
|
|
509
|
+
hsl([.98,.3,.57,.14][c%4],.8,[0,.3,.5,.8,.9][l]).toString();
|
|
510
|
+
const alpha = wave(1,1,t);
|
|
511
|
+
const p = percent(alpha, .1, .5);
|
|
512
|
+
|
|
513
|
+
// setup
|
|
514
|
+
x.translate(w/2,h/2);
|
|
515
|
+
const size = min(6, min(w,h)/99); // fit to screen
|
|
516
|
+
x.scale(size,size);
|
|
517
|
+
x.translate(-40,-35);
|
|
518
|
+
x.lineJoin = x.lineCap = 'round';
|
|
519
|
+
x.lineWidth = .1 + p*1.9;
|
|
520
|
+
|
|
521
|
+
// drawing effect
|
|
522
|
+
const p2 = percent(alpha,.1,1);
|
|
523
|
+
x.setLineDash([99*p2,99]);
|
|
524
|
+
|
|
525
|
+
// cab top
|
|
526
|
+
rect(7,16,18,-8,color(2,2));
|
|
527
|
+
rect(7,8,18,4,color(2,3));
|
|
528
|
+
rect(25,8,8,8,color(2,1));
|
|
529
|
+
rect(25,8,-18,8);
|
|
530
|
+
rect(25,8,8,8);
|
|
531
|
+
|
|
532
|
+
// cab
|
|
533
|
+
rect(25,16,7,23,color());
|
|
534
|
+
rect(11,39,14,-23,color(1,1));
|
|
535
|
+
rect(11,16,14,18,color(1,2));
|
|
536
|
+
rect(11,16,14,8,color(1,3));
|
|
537
|
+
rect(25,16,-14,24);
|
|
538
|
+
|
|
539
|
+
// cab window
|
|
540
|
+
rect(15,29,6,-9,color(2,2));
|
|
541
|
+
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
542
|
+
rect(21,21,-6,9);
|
|
543
|
+
|
|
544
|
+
// little stack
|
|
545
|
+
rect(37,14,9,6,color(3,2));
|
|
546
|
+
rect(37,14,4.5,6,color(3,3));
|
|
547
|
+
rect(37,14,9,6);
|
|
548
|
+
|
|
549
|
+
// big stack
|
|
550
|
+
rect(50,20,10,-8,color(0,1));
|
|
551
|
+
rect(50,20,6.5,-8,color(0,2));
|
|
552
|
+
rect(50,20,3.5,-8,color(0,3));
|
|
553
|
+
rect(50,20,10,-8);
|
|
554
|
+
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
555
|
+
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
556
|
+
circle(55,2,11.4,.5,PI-.5);
|
|
557
|
+
rect(45,7,20,-7,color(0,2));
|
|
558
|
+
rect(45,-1,20,4,color(0,3));
|
|
559
|
+
rect(45,-1,20,8);
|
|
560
|
+
|
|
561
|
+
// engine
|
|
562
|
+
for (let i=5; i--;)
|
|
563
|
+
{
|
|
564
|
+
// stagger radius to fix slight seam
|
|
565
|
+
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
566
|
+
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
567
|
+
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// engine outline
|
|
571
|
+
circle(36,30,10,PI/2,PI*3/2);
|
|
572
|
+
circle(48,30,10,PI/2,PI*3/2);
|
|
573
|
+
circle(60,30,10);
|
|
574
|
+
line(36,20,60,20);
|
|
575
|
+
|
|
576
|
+
// engine front light
|
|
577
|
+
circle(60,30,4,PI,3*PI,color(3,2));
|
|
578
|
+
circle(60,30,4,PI,2*PI,color(3,3));
|
|
579
|
+
circle(60,30,4,PI,3*PI);
|
|
580
|
+
|
|
581
|
+
// front brush
|
|
582
|
+
for (let i=6; i--;)
|
|
583
|
+
{
|
|
584
|
+
x.beginPath();
|
|
585
|
+
x.lineTo(53,54);
|
|
586
|
+
x.lineTo(53,40);
|
|
587
|
+
x.lineTo(53+(1+i*2.9)*p,40);
|
|
588
|
+
x.lineTo(53+(4+i*3.5)*p,54);
|
|
589
|
+
x.fillStyle = color(0,i%2+2);
|
|
590
|
+
x.fill();
|
|
591
|
+
i%2 && x.stroke();
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// wheels
|
|
595
|
+
rect(6,40,5,5);
|
|
596
|
+
rect(6,40,5,5,color());
|
|
597
|
+
rect(15,54,38,-14,color());
|
|
598
|
+
for (let i=3; i--;)
|
|
599
|
+
for (let j=2; j--;)
|
|
600
|
+
{
|
|
601
|
+
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
602
|
+
x.stroke();
|
|
603
|
+
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
604
|
+
x.stroke();
|
|
605
|
+
}
|
|
606
|
+
line(6,40,68,40); // center
|
|
607
|
+
line(77,54,4,54); // bottom
|
|
608
|
+
|
|
609
|
+
// draw engine name
|
|
610
|
+
const s = engineName;
|
|
611
|
+
x.font = '900 16px arial';
|
|
612
|
+
x.textAlign = 'center';
|
|
613
|
+
x.textBaseline = 'top';
|
|
614
|
+
x.lineWidth = .1+p*3.9;
|
|
615
|
+
let w2 = 0;
|
|
616
|
+
for (let i=0; i<s.length; ++i)
|
|
617
|
+
w2 += x.measureText(s[i]).width;
|
|
618
|
+
for (let j=2; j--;)
|
|
619
|
+
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
620
|
+
{
|
|
621
|
+
x.fillStyle = color(i,2);
|
|
622
|
+
const w = x.measureText(s[i]).width;
|
|
623
|
+
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
624
|
+
X += w;
|
|
625
|
+
}
|
|
626
|
+
x.restore();
|
|
627
|
+
}
|
|
628
|
+
///////////////////////////////////////////////////////////////////////////
|
|
444
629
|
}
|
|
445
630
|
|
|
446
631
|
/** Update each engine object, remove destroyed objects, and update time
|
|
632
|
+
* can be called manually if objects need to be updated outside of main loop
|
|
447
633
|
* @memberof Engine */
|
|
448
634
|
function engineObjectsUpdate()
|
|
449
635
|
{
|
|
@@ -495,18 +681,21 @@ function engineObjectsDestroy()
|
|
|
495
681
|
function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
496
682
|
{
|
|
497
683
|
const collectedObjects = [];
|
|
498
|
-
if (!pos)
|
|
684
|
+
if (!pos)
|
|
499
685
|
{
|
|
686
|
+
// all objects
|
|
500
687
|
for (const o of objects)
|
|
501
688
|
collectedObjects.push(o);
|
|
502
689
|
}
|
|
503
|
-
else if (size instanceof Vector2)
|
|
690
|
+
else if (size instanceof Vector2)
|
|
504
691
|
{
|
|
692
|
+
// bounding box test
|
|
505
693
|
for (const o of objects)
|
|
506
694
|
o.isOverlapping(pos, size) && collectedObjects.push(o);
|
|
507
695
|
}
|
|
508
|
-
else
|
|
696
|
+
else
|
|
509
697
|
{
|
|
698
|
+
// circle test
|
|
510
699
|
const sizeSquared = size*size;
|
|
511
700
|
for (const o of objects)
|
|
512
701
|
pos.distanceSquared(o.pos) < sizeSquared && collectedObjects.push(o);
|
|
@@ -549,171 +738,4 @@ function engineObjectsRaycast(start, end, objects=engineObjects)
|
|
|
549
738
|
|
|
550
739
|
debugRaycast && debugLine(start, end, hitObjects.length ? '#f00' : '#00f', .02);
|
|
551
740
|
return hitObjects;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
555
|
-
// LittleJS splash screen and logo
|
|
556
|
-
|
|
557
|
-
function drawEngineSplashScreen(t)
|
|
558
|
-
{
|
|
559
|
-
const x = overlayContext;
|
|
560
|
-
const w = overlayCanvas.width = innerWidth;
|
|
561
|
-
const h = overlayCanvas.height = innerHeight;
|
|
562
|
-
|
|
563
|
-
{
|
|
564
|
-
// background
|
|
565
|
-
const p3 = percent(t, 1, .8);
|
|
566
|
-
const p4 = percent(t, 0, .5);
|
|
567
|
-
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
568
|
-
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
569
|
-
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
570
|
-
x.save();
|
|
571
|
-
x.fillStyle = g;
|
|
572
|
-
x.fillRect(0,0,w,h);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
// draw LittleJS logo...
|
|
576
|
-
const rect = (X, Y, W, H, C)=>
|
|
577
|
-
{
|
|
578
|
-
x.beginPath();
|
|
579
|
-
x.rect(X,Y,W,C?H*p:H);
|
|
580
|
-
x.fillStyle = C;
|
|
581
|
-
C ? x.fill() : x.stroke();
|
|
582
|
-
};
|
|
583
|
-
const line = (X, Y, Z, W)=>
|
|
584
|
-
{
|
|
585
|
-
x.beginPath();
|
|
586
|
-
x.lineTo(X,Y);
|
|
587
|
-
x.lineTo(Z,W);
|
|
588
|
-
x.stroke();
|
|
589
|
-
};
|
|
590
|
-
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
591
|
-
{
|
|
592
|
-
const D = (A+B)/2, E = p*(B-A)/2;
|
|
593
|
-
x.beginPath();
|
|
594
|
-
F && x.lineTo(X,Y);
|
|
595
|
-
x.arc(X,Y,R,D-E,D+E);
|
|
596
|
-
x.fillStyle = C;
|
|
597
|
-
C ? x.fill() : x.stroke();
|
|
598
|
-
};
|
|
599
|
-
const color = (c=0, l=0) =>
|
|
600
|
-
hsl([.98,.3,.57,.14][c%4]-10,.8,[0,.3,.5,.8,.9][l]).toString();
|
|
601
|
-
const alpha = wave(1,1,t);
|
|
602
|
-
const p = percent(alpha, .1, .5);
|
|
603
|
-
|
|
604
|
-
// setup
|
|
605
|
-
x.translate(w/2,h/2);
|
|
606
|
-
const size = min(6, min(w,h)/99); // fit to screen
|
|
607
|
-
x.scale(size,size);
|
|
608
|
-
x.translate(-40,-35);
|
|
609
|
-
x.lineJoin = x.lineCap = 'round';
|
|
610
|
-
x.lineWidth = .1 + p*1.9;
|
|
611
|
-
|
|
612
|
-
// drawing effect
|
|
613
|
-
const p2 = percent(alpha,.1,1);
|
|
614
|
-
x.setLineDash([99*p2,99]);
|
|
615
|
-
|
|
616
|
-
// cab top
|
|
617
|
-
rect(7,16,18,-8,color(2,2));
|
|
618
|
-
rect(7,8,18,4,color(2,3));
|
|
619
|
-
rect(25,8,8,8,color(2,1));
|
|
620
|
-
rect(25,8,-18,8);
|
|
621
|
-
rect(25,8,8,8);
|
|
622
|
-
|
|
623
|
-
// cab
|
|
624
|
-
rect(25,16,7,23,color());
|
|
625
|
-
rect(11,39,14,-23,color(1,1));
|
|
626
|
-
rect(11,16,14,18,color(1,2));
|
|
627
|
-
rect(11,16,14,8,color(1,3));
|
|
628
|
-
rect(25,16,-14,24);
|
|
629
|
-
|
|
630
|
-
// cab window
|
|
631
|
-
rect(15,29,6,-9,color(2,2));
|
|
632
|
-
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
633
|
-
rect(21,21,-6,9);
|
|
634
|
-
|
|
635
|
-
// little stack
|
|
636
|
-
rect(37,14,9,6,color(3,2));
|
|
637
|
-
rect(37,14,4.5,6,color(3,3));
|
|
638
|
-
rect(37,14,9,6);
|
|
639
|
-
|
|
640
|
-
// big stack
|
|
641
|
-
rect(50,20,10,-8,color(0,1));
|
|
642
|
-
rect(50,20,6.5,-8,color(0,2));
|
|
643
|
-
rect(50,20,3.5,-8,color(0,3));
|
|
644
|
-
rect(50,20,10,-8);
|
|
645
|
-
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
646
|
-
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
647
|
-
circle(55,2,11.4,.5,PI-.5);
|
|
648
|
-
rect(45,7,20,-7,color(0,2));
|
|
649
|
-
rect(45,-1,20,4,color(0,3));
|
|
650
|
-
rect(45,-1,20,8);
|
|
651
|
-
|
|
652
|
-
// engine
|
|
653
|
-
for (let i=5; i--;)
|
|
654
|
-
{
|
|
655
|
-
// stagger radius to fix slight seam
|
|
656
|
-
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
657
|
-
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
658
|
-
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// engine outline
|
|
662
|
-
circle(36,30,10,PI/2,PI*3/2);
|
|
663
|
-
circle(48,30,10,PI/2,PI*3/2);
|
|
664
|
-
circle(60,30,10);
|
|
665
|
-
line(36,20,60,20);
|
|
666
|
-
|
|
667
|
-
// engine front light
|
|
668
|
-
circle(60,30,4,PI,3*PI,color(3,2));
|
|
669
|
-
circle(60,30,4,PI,2*PI,color(3,3));
|
|
670
|
-
circle(60,30,4,PI,3*PI);
|
|
671
|
-
|
|
672
|
-
// front brush
|
|
673
|
-
for (let i=6; i--;)
|
|
674
|
-
{
|
|
675
|
-
x.beginPath();
|
|
676
|
-
x.lineTo(53,54);
|
|
677
|
-
x.lineTo(53,40);
|
|
678
|
-
x.lineTo(53+(1+i*2.9)*p,40);
|
|
679
|
-
x.lineTo(53+(4+i*3.5)*p,54);
|
|
680
|
-
x.fillStyle = color(0,i%2+2);
|
|
681
|
-
x.fill();
|
|
682
|
-
i%2 && x.stroke();
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// wheels
|
|
686
|
-
rect(6,40,5,5);
|
|
687
|
-
rect(6,40,5,5,color());
|
|
688
|
-
rect(15,54,38,-14,color());
|
|
689
|
-
for (let i=3; i--;)
|
|
690
|
-
for (let j=2; j--;)
|
|
691
|
-
{
|
|
692
|
-
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
693
|
-
x.stroke();
|
|
694
|
-
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
695
|
-
x.stroke();
|
|
696
|
-
}
|
|
697
|
-
line(6,40,68,40); // center
|
|
698
|
-
line(77,54,4,54); // bottom
|
|
699
|
-
|
|
700
|
-
// draw engine name
|
|
701
|
-
const s = engineName;
|
|
702
|
-
x.font = '900 16px arial';
|
|
703
|
-
x.textAlign = 'center';
|
|
704
|
-
x.textBaseline = 'top';
|
|
705
|
-
x.lineWidth = .1+p*3.9;
|
|
706
|
-
let w2 = 0;
|
|
707
|
-
for (let i=0; i<s.length; ++i)
|
|
708
|
-
w2 += x.measureText(s[i]).width;
|
|
709
|
-
for (let j=2; j--;)
|
|
710
|
-
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
711
|
-
{
|
|
712
|
-
x.fillStyle = color(i,2);
|
|
713
|
-
const w = x.measureText(s[i]).width;
|
|
714
|
-
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
715
|
-
X += w;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
x.restore();
|
|
719
741
|
}
|
package/src/engineDebug.js
CHANGED
|
@@ -194,7 +194,7 @@ function debugOverlap(posA, sizeA, posB, sizeB, color, time, screenSpace=false)
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
/** Draw a debug axis aligned bounding box in world space
|
|
197
|
-
* @param {string} text
|
|
197
|
+
* @param {string|number} text
|
|
198
198
|
* @param {Vector2} pos
|
|
199
199
|
* @param {number} [size]
|
|
200
200
|
* @param {Color|string} [color]
|
|
@@ -352,34 +352,46 @@ function debugRender()
|
|
|
352
352
|
debugTakeScreenshot = 0;
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
if (debugGamepads && gamepadsEnable
|
|
355
|
+
if (debugGamepads && gamepadsEnable)
|
|
356
356
|
{
|
|
357
|
-
//
|
|
358
|
-
const
|
|
359
|
-
|
|
357
|
+
// draw gamepads
|
|
358
|
+
const maxGamepads = 8;
|
|
359
|
+
let gamepadConnectedCount = 0;
|
|
360
|
+
for (let i = 0; i < maxGamepads; i++)
|
|
361
|
+
gamepadConnected(i) && gamepadConnectedCount++;
|
|
362
|
+
|
|
363
|
+
for (let i = 0; i < maxGamepads; i++)
|
|
360
364
|
{
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
if (!gamepadConnected(i))
|
|
366
|
+
continue;
|
|
367
|
+
|
|
368
|
+
const stickScale = 1;
|
|
369
|
+
const buttonScale = .2;
|
|
370
|
+
const cornerPos = cameraPos.add(vec2(-stickScale*2, ((gamepadConnectedCount-1)/2-i)*stickScale*3));
|
|
371
|
+
debugText(i, cornerPos.add(vec2(-stickScale, stickScale)), 1);
|
|
372
|
+
if (i === gamepadPrimary)
|
|
373
|
+
debugText('Main', cornerPos.add(vec2(-stickScale*2, 0)),1, '#0f0');
|
|
374
|
+
|
|
375
|
+
// read analog sticks
|
|
376
|
+
const stickCount = gamepadStickData[i].length;
|
|
377
|
+
for (let j = 0; j < stickCount; j++)
|
|
363
378
|
{
|
|
364
|
-
const
|
|
365
|
-
const
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
debugCircle(drawPos, buttonScale*2, pressed ? '#f00' : '#fff7', 0, true);
|
|
381
|
-
debugText(''+j, drawPos, .2);
|
|
382
|
-
}
|
|
379
|
+
const stick = gamepadStick(j, i);
|
|
380
|
+
const drawPos = cornerPos.add(vec2(j*stickScale*2, 0));
|
|
381
|
+
const stickPos = drawPos.add(stick.scale(stickScale));
|
|
382
|
+
debugCircle(drawPos, stickScale*2, '#fff7',0,true);
|
|
383
|
+
debugLine(drawPos, stickPos, '#f00');
|
|
384
|
+
debugText(j, drawPos, .3);
|
|
385
|
+
debugPoint(stickPos, '#f00');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const buttonCount = inputData[i+1].length;
|
|
389
|
+
for (let j = 0; j < buttonCount; j++)
|
|
390
|
+
{
|
|
391
|
+
const drawPos = cornerPos.add(vec2(j*buttonScale*2, -stickScale-buttonScale*2));
|
|
392
|
+
const pressed = gamepadIsDown(j, i);
|
|
393
|
+
debugCircle(drawPos, buttonScale*2, pressed ? '#f00' : '#fff7', 0, true);
|
|
394
|
+
debugText(j, drawPos, .3);
|
|
383
395
|
}
|
|
384
396
|
}
|
|
385
397
|
}
|
|
@@ -561,14 +573,18 @@ function debugRender()
|
|
|
561
573
|
mousePressed && overlayContext.fillText('Mouse: ' + mousePressed, x, y += h);
|
|
562
574
|
keysPressed && overlayContext.fillText('Keys: ' + keysPressed, x, y += h);
|
|
563
575
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
for (const i in inputData[1])
|
|
576
|
+
// show gamepad buttons
|
|
577
|
+
for (let i = 1; i < inputData.length; i++)
|
|
567
578
|
{
|
|
568
|
-
|
|
569
|
-
|
|
579
|
+
let buttonsPressed = '';
|
|
580
|
+
if (inputData[i])
|
|
581
|
+
for (const j in inputData[i])
|
|
582
|
+
{
|
|
583
|
+
if (keyIsDown(j, i))
|
|
584
|
+
buttonsPressed += j + ' ' ;
|
|
585
|
+
}
|
|
586
|
+
buttonsPressed && overlayContext.fillText(`Gamepad ${i-1}: ` + buttonsPressed, x, y += h);
|
|
570
587
|
}
|
|
571
|
-
buttonsPressed && overlayContext.fillText('Gamepad: ' + buttonsPressed, x, y += h);
|
|
572
588
|
}
|
|
573
589
|
else
|
|
574
590
|
{
|
package/src/engineDraw.js
CHANGED
|
@@ -151,7 +151,7 @@ class TileInfo
|
|
|
151
151
|
this.padding = padding;
|
|
152
152
|
/** @property {TextureInfo} - The texture info for this tile */
|
|
153
153
|
this.textureInfo = textureInfos[this.textureIndex];
|
|
154
|
-
/** @property {
|
|
154
|
+
/** @property {number} - Shrinks tile by this many pixels to prevent neighbors bleeding */
|
|
155
155
|
this.bleedScale = bleedScale;
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -638,7 +638,7 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
|
|
|
638
638
|
|
|
639
639
|
/** Draw text on main canvas in world space
|
|
640
640
|
* Automatically splits new lines into rows
|
|
641
|
-
* @param {string} text
|
|
641
|
+
* @param {string|number} text
|
|
642
642
|
* @param {Vector2} pos
|
|
643
643
|
* @param {number} [size]
|
|
644
644
|
* @param {Color} [color=(1,1,1,1)]
|
|
@@ -665,7 +665,7 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
665
665
|
|
|
666
666
|
/** Draw text on overlay canvas in world space
|
|
667
667
|
* Automatically splits new lines into rows
|
|
668
|
-
* @param {string} text
|
|
668
|
+
* @param {string|number} text
|
|
669
669
|
* @param {Vector2} pos
|
|
670
670
|
* @param {number} [size]
|
|
671
671
|
* @param {Color} [color=(1,1,1,1)]
|
|
@@ -684,7 +684,7 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
684
684
|
|
|
685
685
|
/** Draw text on overlay canvas in screen space
|
|
686
686
|
* Automatically splits new lines into rows
|
|
687
|
-
* @param {string} text
|
|
687
|
+
* @param {string|number} text
|
|
688
688
|
* @param {Vector2} pos
|
|
689
689
|
* @param {number} [size]
|
|
690
690
|
* @param {Color} [color=(1,1,1,1)]
|
|
@@ -849,11 +849,28 @@ function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
|
|
|
849
849
|
];
|
|
850
850
|
}
|
|
851
851
|
|
|
852
|
-
/** Get the camera
|
|
852
|
+
/** Get the size of the camera window in world space
|
|
853
853
|
* @return {Vector2}
|
|
854
854
|
* @memberof Draw */
|
|
855
855
|
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
856
856
|
|
|
857
|
+
/** Check if a point or circle is on screen
|
|
858
|
+
* If size is a Vector2, uses the largest dimension as diameter
|
|
859
|
+
* This can be used to cull offscreen objects from render or update
|
|
860
|
+
* @param {Vector2} pos - world space position
|
|
861
|
+
* @param {Vector2|number} size - world space size or diameter
|
|
862
|
+
* @return {boolean}
|
|
863
|
+
* @memberof Draw */
|
|
864
|
+
function isOnScreen(pos, size=0)
|
|
865
|
+
{
|
|
866
|
+
pos = worldToScreen(pos);
|
|
867
|
+
if (size instanceof Vector2)
|
|
868
|
+
size = max(size.x, size.y); // use largest dimension
|
|
869
|
+
size *= cameraScale/2;
|
|
870
|
+
return pos.x + size > 0 && pos.x - size < mainCanvasSize.x &&
|
|
871
|
+
pos.y + size > 0 && pos.y - size < mainCanvasSize.y;
|
|
872
|
+
}
|
|
873
|
+
|
|
857
874
|
/** Enable normal or additive blend mode
|
|
858
875
|
* @param {boolean} [additive]
|
|
859
876
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
@@ -1017,7 +1034,7 @@ class FontImage
|
|
|
1017
1034
|
}
|
|
1018
1035
|
|
|
1019
1036
|
/** Draw text in world space using the image font
|
|
1020
|
-
* @param {string} text
|
|
1037
|
+
* @param {string|number} text
|
|
1021
1038
|
* @param {Vector2} pos
|
|
1022
1039
|
* @param {number} [scale=.25]
|
|
1023
1040
|
* @param {boolean} [center]
|
|
@@ -1029,7 +1046,7 @@ class FontImage
|
|
|
1029
1046
|
}
|
|
1030
1047
|
|
|
1031
1048
|
/** Draw text on overlay canvas in world space using the image font
|
|
1032
|
-
* @param {string} text
|
|
1049
|
+
* @param {string|number} text
|
|
1033
1050
|
* @param {Vector2} pos
|
|
1034
1051
|
* @param {number} [scale]
|
|
1035
1052
|
* @param {boolean} [center]
|
|
@@ -1038,7 +1055,7 @@ class FontImage
|
|
|
1038
1055
|
{ this.drawText(text, pos, scale, center, overlayContext); }
|
|
1039
1056
|
|
|
1040
1057
|
/** Draw text on overlay canvas in screen space using the image font
|
|
1041
|
-
* @param {string} text
|
|
1058
|
+
* @param {string|number} text
|
|
1042
1059
|
* @param {Vector2} pos
|
|
1043
1060
|
* @param {number} [scale]
|
|
1044
1061
|
* @param {boolean} [center]
|