littlejsengine 1.15.8 → 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 +21 -2
- package/dist/littlejs.esm.js +269 -211
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +265 -211
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +265 -211
- package/examples/box2d/game.js +1 -1
- package/examples/electron/index.html +2 -2
- package/examples/platformer/game.js +7 -0
- package/examples/shorts/base.html +1 -2
- package/examples/shorts/uiSystem.js +1 -1
- package/examples/starter/index.html +2 -3
- package/examples/uiSystem/game.js +4 -7
- package/package.json +1 -1
- package/src/engine.js +214 -25
- package/src/engineBuild.js +0 -1
- package/src/engineExport.js +4 -0
- package/src/engineInput.js +13 -1
- package/src/engineParticles.js +8 -6
- package/src/engineSettings.js +24 -0
- package/src/engineTileLayer.js +1 -2
- 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.15.
|
|
36
|
+
const engineVersion = '1.15.9';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -308,23 +308,48 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
308
308
|
|
|
309
309
|
if (canvasFixedSize.x)
|
|
310
310
|
{
|
|
311
|
-
//
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
311
|
+
// set canvas fixed size
|
|
312
|
+
mainCanvasSize = canvasFixedSize.copy();
|
|
313
|
+
|
|
314
|
+
// fit to window using css width and height
|
|
315
|
+
const innerAspect = innerWidth / innerHeight;
|
|
316
|
+
const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
|
|
317
|
+
const w = innerAspect < fixedAspect ? '100%' : '';
|
|
318
|
+
const h = innerAspect < fixedAspect ? '' : '100%';
|
|
319
|
+
overlayCanvas.style.width = mainCanvas.style.width = w;
|
|
320
|
+
overlayCanvas.style.height = mainCanvas.style.height = h;
|
|
321
|
+
if (glCanvas)
|
|
322
|
+
{
|
|
323
|
+
glCanvas.style.width = w;
|
|
324
|
+
glCanvas.style.height = h;
|
|
325
|
+
}
|
|
320
326
|
}
|
|
321
327
|
else
|
|
322
328
|
{
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
|
|
329
|
+
// get main canvas size based on window size
|
|
330
|
+
mainCanvasSize.x = min(innerWidth, canvasMaxSize.x);
|
|
331
|
+
mainCanvasSize.y = min(innerHeight, canvasMaxSize.y);
|
|
332
|
+
|
|
333
|
+
// responsive aspect ratio with native resolution
|
|
334
|
+
const innerAspect = innerWidth / innerHeight;
|
|
335
|
+
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
336
|
+
{
|
|
337
|
+
// full height
|
|
338
|
+
const w = mainCanvasSize.y * canvasMaxAspect | 0;
|
|
339
|
+
mainCanvasSize.x = min(w, canvasMaxSize.x);
|
|
340
|
+
}
|
|
341
|
+
else if (innerAspect < canvasMinAspect)
|
|
342
|
+
{
|
|
343
|
+
// full width
|
|
344
|
+
const h = mainCanvasSize.x / canvasMinAspect | 0;
|
|
345
|
+
mainCanvasSize.y = min(h, canvasMaxSize.y);
|
|
346
|
+
}
|
|
326
347
|
}
|
|
327
348
|
|
|
349
|
+
// clear main and overlay canvas and set size
|
|
350
|
+
overlayCanvas.width = mainCanvas.width = mainCanvasSize.x;
|
|
351
|
+
overlayCanvas.height = mainCanvas.height = mainCanvasSize.y;
|
|
352
|
+
|
|
328
353
|
// apply the clear color to main canvas
|
|
329
354
|
if (canvasClearColor.a > 0)
|
|
330
355
|
{
|
|
@@ -333,13 +358,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
333
358
|
mainContext.fillStyle = BLACK.toString();
|
|
334
359
|
}
|
|
335
360
|
|
|
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
361
|
// set default line join and cap
|
|
344
362
|
const lineJoin = 'round', lineCap = 'round';
|
|
345
363
|
mainContext.lineJoin = overlayContext.lineJoin = lineJoin;
|
|
@@ -363,7 +381,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
363
381
|
'user-select:none;' + // prevent hold to select
|
|
364
382
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
365
383
|
'touch-action:none;' + // prevent mobile pinch to resize
|
|
366
|
-
'-webkit-touch-callout:none'
|
|
384
|
+
'-webkit-touch-callout:none'; // compatibility for ios
|
|
367
385
|
rootElement.style.cssText = styleRoot;
|
|
368
386
|
drawCanvas = mainCanvas = document.createElement('canvas');
|
|
369
387
|
rootElement.appendChild(mainCanvas);
|
|
@@ -380,7 +398,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
380
398
|
rootElement.appendChild(overlayCanvas);
|
|
381
399
|
overlayContext = overlayCanvas.getContext('2d');
|
|
382
400
|
|
|
383
|
-
//
|
|
401
|
+
// setup canvases
|
|
402
|
+
// transform way is still more reliable then flexbox or grid
|
|
384
403
|
const styleCanvas = 'position:absolute;'+ // allow canvases to overlap
|
|
385
404
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center on screen
|
|
386
405
|
mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
@@ -444,9 +463,176 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
444
463
|
// wait for all the promises to finish
|
|
445
464
|
await Promise.all(promises);
|
|
446
465
|
return startEngine();
|
|
466
|
+
|
|
467
|
+
///////////////////////////////////////////////////////////////////////////
|
|
468
|
+
// LittleJS Splash Screen
|
|
469
|
+
function drawEngineSplashScreen(t)
|
|
470
|
+
{
|
|
471
|
+
const x = overlayContext;
|
|
472
|
+
const w = overlayCanvas.width = innerWidth;
|
|
473
|
+
const h = overlayCanvas.height = innerHeight;
|
|
474
|
+
|
|
475
|
+
{
|
|
476
|
+
// background
|
|
477
|
+
const p3 = percent(t, 1, .8);
|
|
478
|
+
const p4 = percent(t, 0, .5);
|
|
479
|
+
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
480
|
+
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
481
|
+
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
482
|
+
x.save();
|
|
483
|
+
x.fillStyle = g;
|
|
484
|
+
x.fillRect(0,0,w,h);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// draw LittleJS logo...
|
|
488
|
+
const rect = (X, Y, W, H, C)=>
|
|
489
|
+
{
|
|
490
|
+
x.beginPath();
|
|
491
|
+
x.rect(X,Y,W,C?H*p:H);
|
|
492
|
+
x.fillStyle = C;
|
|
493
|
+
C ? x.fill() : x.stroke();
|
|
494
|
+
};
|
|
495
|
+
const line = (X, Y, Z, W)=>
|
|
496
|
+
{
|
|
497
|
+
x.beginPath();
|
|
498
|
+
x.lineTo(X,Y);
|
|
499
|
+
x.lineTo(Z,W);
|
|
500
|
+
x.stroke();
|
|
501
|
+
};
|
|
502
|
+
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
503
|
+
{
|
|
504
|
+
const D = (A+B)/2, E = p*(B-A)/2;
|
|
505
|
+
x.beginPath();
|
|
506
|
+
F && x.lineTo(X,Y);
|
|
507
|
+
x.arc(X,Y,R,D-E,D+E);
|
|
508
|
+
x.fillStyle = C;
|
|
509
|
+
C ? x.fill() : x.stroke();
|
|
510
|
+
};
|
|
511
|
+
const color = (c=0, l=0) =>
|
|
512
|
+
hsl([.98,.3,.57,.14][c%4],.8,[0,.3,.5,.8,.9][l]).toString();
|
|
513
|
+
const alpha = wave(1,1,t);
|
|
514
|
+
const p = percent(alpha, .1, .5);
|
|
515
|
+
|
|
516
|
+
// setup
|
|
517
|
+
x.translate(w/2,h/2);
|
|
518
|
+
const size = min(6, min(w,h)/99); // fit to screen
|
|
519
|
+
x.scale(size,size);
|
|
520
|
+
x.translate(-40,-35);
|
|
521
|
+
x.lineJoin = x.lineCap = 'round';
|
|
522
|
+
x.lineWidth = .1 + p*1.9;
|
|
523
|
+
|
|
524
|
+
// drawing effect
|
|
525
|
+
const p2 = percent(alpha,.1,1);
|
|
526
|
+
x.setLineDash([99*p2,99]);
|
|
527
|
+
|
|
528
|
+
// cab top
|
|
529
|
+
rect(7,16,18,-8,color(2,2));
|
|
530
|
+
rect(7,8,18,4,color(2,3));
|
|
531
|
+
rect(25,8,8,8,color(2,1));
|
|
532
|
+
rect(25,8,-18,8);
|
|
533
|
+
rect(25,8,8,8);
|
|
534
|
+
|
|
535
|
+
// cab
|
|
536
|
+
rect(25,16,7,23,color());
|
|
537
|
+
rect(11,39,14,-23,color(1,1));
|
|
538
|
+
rect(11,16,14,18,color(1,2));
|
|
539
|
+
rect(11,16,14,8,color(1,3));
|
|
540
|
+
rect(25,16,-14,24);
|
|
541
|
+
|
|
542
|
+
// cab window
|
|
543
|
+
rect(15,29,6,-9,color(2,2));
|
|
544
|
+
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
545
|
+
rect(21,21,-6,9);
|
|
546
|
+
|
|
547
|
+
// little stack
|
|
548
|
+
rect(37,14,9,6,color(3,2));
|
|
549
|
+
rect(37,14,4.5,6,color(3,3));
|
|
550
|
+
rect(37,14,9,6);
|
|
551
|
+
|
|
552
|
+
// big stack
|
|
553
|
+
rect(50,20,10,-8,color(0,1));
|
|
554
|
+
rect(50,20,6.5,-8,color(0,2));
|
|
555
|
+
rect(50,20,3.5,-8,color(0,3));
|
|
556
|
+
rect(50,20,10,-8);
|
|
557
|
+
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
558
|
+
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
559
|
+
circle(55,2,11.4,.5,PI-.5);
|
|
560
|
+
rect(45,7,20,-7,color(0,2));
|
|
561
|
+
rect(45,-1,20,4,color(0,3));
|
|
562
|
+
rect(45,-1,20,8);
|
|
563
|
+
|
|
564
|
+
// engine
|
|
565
|
+
for (let i=5; i--;)
|
|
566
|
+
{
|
|
567
|
+
// stagger radius to fix slight seam
|
|
568
|
+
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
569
|
+
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
570
|
+
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// engine outline
|
|
574
|
+
circle(36,30,10,PI/2,PI*3/2);
|
|
575
|
+
circle(48,30,10,PI/2,PI*3/2);
|
|
576
|
+
circle(60,30,10);
|
|
577
|
+
line(36,20,60,20);
|
|
578
|
+
|
|
579
|
+
// engine front light
|
|
580
|
+
circle(60,30,4,PI,3*PI,color(3,2));
|
|
581
|
+
circle(60,30,4,PI,2*PI,color(3,3));
|
|
582
|
+
circle(60,30,4,PI,3*PI);
|
|
583
|
+
|
|
584
|
+
// front brush
|
|
585
|
+
for (let i=6; i--;)
|
|
586
|
+
{
|
|
587
|
+
x.beginPath();
|
|
588
|
+
x.lineTo(53,54);
|
|
589
|
+
x.lineTo(53,40);
|
|
590
|
+
x.lineTo(53+(1+i*2.9)*p,40);
|
|
591
|
+
x.lineTo(53+(4+i*3.5)*p,54);
|
|
592
|
+
x.fillStyle = color(0,i%2+2);
|
|
593
|
+
x.fill();
|
|
594
|
+
i%2 && x.stroke();
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// wheels
|
|
598
|
+
rect(6,40,5,5);
|
|
599
|
+
rect(6,40,5,5,color());
|
|
600
|
+
rect(15,54,38,-14,color());
|
|
601
|
+
for (let i=3; i--;)
|
|
602
|
+
for (let j=2; j--;)
|
|
603
|
+
{
|
|
604
|
+
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
605
|
+
x.stroke();
|
|
606
|
+
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
607
|
+
x.stroke();
|
|
608
|
+
}
|
|
609
|
+
line(6,40,68,40); // center
|
|
610
|
+
line(77,54,4,54); // bottom
|
|
611
|
+
|
|
612
|
+
// draw engine name
|
|
613
|
+
const s = engineName;
|
|
614
|
+
x.font = '900 16px arial';
|
|
615
|
+
x.textAlign = 'center';
|
|
616
|
+
x.textBaseline = 'top';
|
|
617
|
+
x.lineWidth = .1+p*3.9;
|
|
618
|
+
let w2 = 0;
|
|
619
|
+
for (let i=0; i<s.length; ++i)
|
|
620
|
+
w2 += x.measureText(s[i]).width;
|
|
621
|
+
for (let j=2; j--;)
|
|
622
|
+
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
623
|
+
{
|
|
624
|
+
x.fillStyle = color(i,2);
|
|
625
|
+
const w = x.measureText(s[i]).width;
|
|
626
|
+
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
627
|
+
X += w;
|
|
628
|
+
}
|
|
629
|
+
x.restore();
|
|
630
|
+
}
|
|
631
|
+
///////////////////////////////////////////////////////////////////////////
|
|
447
632
|
}
|
|
448
633
|
|
|
449
634
|
/** Update each engine object, remove destroyed objects, and update time
|
|
635
|
+
* can be called manually if objects need to be updated outside of main loop
|
|
450
636
|
* @memberof Engine */
|
|
451
637
|
function engineObjectsUpdate()
|
|
452
638
|
{
|
|
@@ -498,18 +684,21 @@ function engineObjectsDestroy()
|
|
|
498
684
|
function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
499
685
|
{
|
|
500
686
|
const collectedObjects = [];
|
|
501
|
-
if (!pos)
|
|
687
|
+
if (!pos)
|
|
502
688
|
{
|
|
689
|
+
// all objects
|
|
503
690
|
for (const o of objects)
|
|
504
691
|
collectedObjects.push(o);
|
|
505
692
|
}
|
|
506
|
-
else if (size instanceof Vector2)
|
|
693
|
+
else if (size instanceof Vector2)
|
|
507
694
|
{
|
|
695
|
+
// bounding box test
|
|
508
696
|
for (const o of objects)
|
|
509
697
|
o.isOverlapping(pos, size) && collectedObjects.push(o);
|
|
510
698
|
}
|
|
511
|
-
else
|
|
699
|
+
else
|
|
512
700
|
{
|
|
701
|
+
// circle test
|
|
513
702
|
const sizeSquared = size*size;
|
|
514
703
|
for (const o of objects)
|
|
515
704
|
pos.distanceSquared(o.pos) < sizeSquared && collectedObjects.push(o);
|
|
@@ -552,173 +741,6 @@ function engineObjectsRaycast(start, end, objects=engineObjects)
|
|
|
552
741
|
|
|
553
742
|
debugRaycast && debugLine(start, end, hitObjects.length ? '#f00' : '#00f', .02);
|
|
554
743
|
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
744
|
}
|
|
723
745
|
/**
|
|
724
746
|
* LittleJS - Release Mode
|
|
@@ -2039,6 +2061,20 @@ let canvasClearColor = CLEAR_BLACK;
|
|
|
2039
2061
|
* @memberof Settings */
|
|
2040
2062
|
let canvasMaxSize = vec2(1920, 1080);
|
|
2041
2063
|
|
|
2064
|
+
/** Minimum aspect ratio of the canvas (width/height), unused if 0
|
|
2065
|
+
* Can be used with canvasMaxAspect to limit aspect ratio
|
|
2066
|
+
* @type {number}
|
|
2067
|
+
* @default
|
|
2068
|
+
* @memberof Settings */
|
|
2069
|
+
let canvasMinAspect = 0;
|
|
2070
|
+
|
|
2071
|
+
/** Maximum aspect ratio of the canvas (width/height), unused if 0
|
|
2072
|
+
* Can be used with canvasMinAspect to limit aspect ratio
|
|
2073
|
+
* @type {number}
|
|
2074
|
+
* @default
|
|
2075
|
+
* @memberof Settings */
|
|
2076
|
+
let canvasMaxAspect = 0;
|
|
2077
|
+
|
|
2042
2078
|
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
2043
2079
|
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
2044
2080
|
* @type {Vector2}
|
|
@@ -2335,6 +2371,16 @@ function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
|
|
|
2335
2371
|
* @memberof Settings */
|
|
2336
2372
|
function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
|
|
2337
2373
|
|
|
2374
|
+
/** Set minimum aspect ratio of the canvas (width/height), unused if 0
|
|
2375
|
+
* @param {number} aspect
|
|
2376
|
+
* @memberof Settings */
|
|
2377
|
+
function setCanvasMinAspect(aspect) { canvasMinAspect = aspect; }
|
|
2378
|
+
|
|
2379
|
+
/** Set maximum aspect ratio of the canvas (width/height), unused if 0
|
|
2380
|
+
* @param {number} aspect
|
|
2381
|
+
* @memberof Settings */
|
|
2382
|
+
function setCanvasMaxAspect(aspect) { canvasMaxAspect = aspect; }
|
|
2383
|
+
|
|
2338
2384
|
/** Set fixed size of the canvas
|
|
2339
2385
|
* @param {Vector2} size
|
|
2340
2386
|
* @memberof Settings */
|
|
@@ -4423,6 +4469,16 @@ function gamepadConnected(gamepad=gamepadPrimary)
|
|
|
4423
4469
|
return !!inputData[gamepad+1];
|
|
4424
4470
|
}
|
|
4425
4471
|
|
|
4472
|
+
/** Returns how many control sticks the passed in gamepad has
|
|
4473
|
+
* @param {number} [gamepad]
|
|
4474
|
+
* @return {number}
|
|
4475
|
+
* @memberof Input */
|
|
4476
|
+
function gamepadStickCount(gamepad=gamepadPrimary)
|
|
4477
|
+
{
|
|
4478
|
+
ASSERT(isNumber(gamepad), 'gamepad must be a number');
|
|
4479
|
+
return gamepadStickData[gamepad]?.length ?? 0;
|
|
4480
|
+
}
|
|
4481
|
+
|
|
4426
4482
|
/** True if a touch device has been detected
|
|
4427
4483
|
* @memberof Input */
|
|
4428
4484
|
const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
@@ -4763,9 +4819,11 @@ function inputUpdate()
|
|
|
4763
4819
|
dpad.set(x, -y);
|
|
4764
4820
|
sticks[0] = dpad.clampLength(); // clamp to circle
|
|
4765
4821
|
}
|
|
4766
|
-
const rightTouchStick = touchGamepadSticks[1] ?? vec2();
|
|
4767
4822
|
if (touchGamepadButtonCount === 1)
|
|
4823
|
+
{
|
|
4824
|
+
const rightTouchStick = touchGamepadSticks[1] ?? vec2();
|
|
4768
4825
|
sticks[1] = applyDeadZones(rightTouchStick);
|
|
4826
|
+
}
|
|
4769
4827
|
|
|
4770
4828
|
// read virtual gamepad buttons
|
|
4771
4829
|
const data = inputData[1] ?? (inputData[1] = []);
|
|
@@ -5857,8 +5915,7 @@ class CanvasLayer extends EngineObject
|
|
|
5857
5915
|
/** Destroy this canvas layer */
|
|
5858
5916
|
destroy()
|
|
5859
5917
|
{
|
|
5860
|
-
if (this.destroyed)
|
|
5861
|
-
return;
|
|
5918
|
+
if (this.destroyed) return;
|
|
5862
5919
|
|
|
5863
5920
|
this.textureInfo.destroyWebGLTexture();
|
|
5864
5921
|
super.destroy();
|
|
@@ -6410,9 +6467,9 @@ class ParticleEmitter extends EngineObject
|
|
|
6410
6467
|
/** @property {Color} - Color at start of life 2, randomized between start colors */
|
|
6411
6468
|
this.colorStartB = colorStartB.copy();
|
|
6412
6469
|
/** @property {Color} - Color at end of life 1, randomized between end colors */
|
|
6413
|
-
this.colorEndA
|
|
6470
|
+
this.colorEndA = colorEndA.copy();
|
|
6414
6471
|
/** @property {Color} - Color at end of life 2, randomized between end colors */
|
|
6415
|
-
this.colorEndB
|
|
6472
|
+
this.colorEndB = colorEndB.copy();
|
|
6416
6473
|
/** @property {boolean} - Should color be randomized linearly or across each component */
|
|
6417
6474
|
this.randomColorLinear = randomColorLinear;
|
|
6418
6475
|
|
|
@@ -6496,8 +6553,10 @@ class ParticleEmitter extends EngineObject
|
|
|
6496
6553
|
if (debugParticles)
|
|
6497
6554
|
{
|
|
6498
6555
|
// show emitter bounds
|
|
6499
|
-
|
|
6500
|
-
|
|
6556
|
+
if (typeof this.emitSize === 'number')
|
|
6557
|
+
debugCircle(this.pos, this.emitSize/2, '#0f0');
|
|
6558
|
+
else
|
|
6559
|
+
debugRect(this.pos, this.emitSize, '#0f0', 0, this.angle);
|
|
6501
6560
|
}
|
|
6502
6561
|
}
|
|
6503
6562
|
|
|
@@ -6507,8 +6566,8 @@ class ParticleEmitter extends EngineObject
|
|
|
6507
6566
|
{
|
|
6508
6567
|
// spawn a particle
|
|
6509
6568
|
let pos = typeof this.emitSize === 'number' ? // check if number was used
|
|
6510
|
-
randInCircle(this.emitSize/2)
|
|
6511
|
-
: vec2(rand(-.5,.5), rand(-.5,.5))
|
|
6569
|
+
randInCircle(this.emitSize/2) // circle emitter
|
|
6570
|
+
: vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
|
|
6512
6571
|
.multiply(this.emitSize).rotate(this.angle)
|
|
6513
6572
|
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
6514
6573
|
if (!this.localSpace)
|
|
@@ -8303,8 +8362,6 @@ class UISystemPlugin
|
|
|
8303
8362
|
// navigation properties
|
|
8304
8363
|
/** @property {UIObject} - Object currently selected by navigation (gamepad or keyboard) */
|
|
8305
8364
|
this.navigationObject = undefined;
|
|
8306
|
-
/** @property {number} - Gamepad index to use for UI navigation */
|
|
8307
|
-
this.navigationGamepadIndex = 0;
|
|
8308
8365
|
/** @property {Timer} - Cooldown timer for navigation inputs */
|
|
8309
8366
|
this.navigationTimer = new Timer(undefined, true);
|
|
8310
8367
|
/** @property {number} - Time between navigation inputs in seconds */
|
|
@@ -8712,9 +8769,8 @@ class UISystemPlugin
|
|
|
8712
8769
|
const both = uiSystem.navigationDirection === 2;
|
|
8713
8770
|
if (isUsingGamepad)
|
|
8714
8771
|
{
|
|
8715
|
-
const
|
|
8716
|
-
const
|
|
8717
|
-
const dpad = gamepadDpad(gamepad);
|
|
8772
|
+
const stick = gamepadStick(0, gamepadPrimary);
|
|
8773
|
+
const dpad = gamepadDpad(gamepadPrimary);
|
|
8718
8774
|
if (both)
|
|
8719
8775
|
return -(stick.y || dpad.y) || (stick.x || dpad.x);
|
|
8720
8776
|
return vertical ? -(stick.y || dpad.y) : (stick.x || dpad.x);
|
|
@@ -8740,9 +8796,8 @@ class UISystemPlugin
|
|
|
8740
8796
|
const vertical = uiSystem.navigationDirection === 1;
|
|
8741
8797
|
if (isUsingGamepad)
|
|
8742
8798
|
{
|
|
8743
|
-
const
|
|
8744
|
-
const
|
|
8745
|
-
const dpad = gamepadDpad(gamepad);
|
|
8799
|
+
const stick = gamepadStick(0, gamepadPrimary);
|
|
8800
|
+
const dpad = gamepadDpad(gamepadPrimary);
|
|
8746
8801
|
return !vertical ? (stick.y || dpad.y) : (stick.x || dpad.x);
|
|
8747
8802
|
}
|
|
8748
8803
|
const back = !vertical ? 'ArrowUp' : 'ArrowLeft';
|
|
@@ -8754,8 +8809,7 @@ class UISystemPlugin
|
|
|
8754
8809
|
* @return {boolean} */
|
|
8755
8810
|
getNavigationWasPressed()
|
|
8756
8811
|
{
|
|
8757
|
-
|
|
8758
|
-
return isUsingGamepad ? gamepadWasPressed(0, gamepad) :
|
|
8812
|
+
return isUsingGamepad ? gamepadWasPressed(0, gamepadPrimary) :
|
|
8759
8813
|
keyWasPressed('Space') || keyWasPressed('Enter');
|
|
8760
8814
|
}
|
|
8761
8815
|
|
package/examples/box2d/game.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
9
|
<!-- LittleJS Engine -->
|
|
10
|
-
<script src=../../dist/littlejs.js?1.15.
|
|
10
|
+
<script src=../../dist/littlejs.js?1.15.9></script>
|
|
11
11
|
|
|
12
12
|
<!-- Add your game scripts here -->
|
|
13
|
-
<script src=game.js?1.15.
|
|
13
|
+
<script src=game.js?1.15.9></script>
|
|
@@ -24,6 +24,13 @@ export function addToDeaths() { ++deaths; }
|
|
|
24
24
|
// enable touch gamepad on touch devices
|
|
25
25
|
LJS.setTouchGamepadEnable(true);
|
|
26
26
|
|
|
27
|
+
// limit canvas aspect ratios to support most modern HD devices
|
|
28
|
+
LJS.setCanvasMinAspect(.4);
|
|
29
|
+
LJS.setCanvasMaxAspect(2.5);
|
|
30
|
+
|
|
31
|
+
// limit size to to 4k HD
|
|
32
|
+
LJS.setCanvasMaxSize(vec2(3840, 2160));
|
|
33
|
+
|
|
27
34
|
///////////////////////////////////////////////////////////////////////////////
|
|
28
35
|
function loadLevel()
|
|
29
36
|
{
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<!DOCTYPE html><meta charset=utf-8><body>
|
|
2
|
-
<script src=../../dist/littlejs.js?1.15.
|
|
2
|
+
<script src=../../dist/littlejs.js?1.15.9></script>
|
|
3
3
|
<script src=../../dist/box2d.wasm.js></script>
|
|
4
4
|
|
|
5
5
|
<!-- LittleJS Engine Source
|
|
6
6
|
<script src=../../src/engine.js></script>
|
|
7
|
-
<script src=../../src/engineSplash.js></script>
|
|
8
7
|
<script src=../../src/engineDebug.js></script>
|
|
9
8
|
<script src=../../src/engineUtilities.js></script>
|
|
10
9
|
<script src=../../src/engineSettings.js></script>
|