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.d.ts
CHANGED
|
@@ -133,6 +133,7 @@ declare module "littlejsengine" {
|
|
|
133
133
|
* @memberof Engine */
|
|
134
134
|
export function engineInit(gameInit: GameInitCallback, gameUpdate: GameCallback, gameUpdatePost: GameCallback, gameRender: GameCallback, gameRenderPost: GameCallback, imageSources?: Array<string>, rootElement?: HTMLElement): Promise<void>;
|
|
135
135
|
/** Update each engine object, remove destroyed objects, and update time
|
|
136
|
+
* can be called manually if objects need to be updated outside of main loop
|
|
136
137
|
* @memberof Engine */
|
|
137
138
|
export function engineObjectsUpdate(): void;
|
|
138
139
|
/** Destroy and remove all objects
|
|
@@ -348,6 +349,18 @@ declare module "littlejsengine" {
|
|
|
348
349
|
* @default Vector2(1920,1080)
|
|
349
350
|
* @memberof Settings */
|
|
350
351
|
export let canvasMaxSize: Vector2;
|
|
352
|
+
/** Minimum aspect ratio of the canvas (width/height), unused if 0
|
|
353
|
+
* Can be used with canvasMaxAspect to limit aspect ratio
|
|
354
|
+
* @type {number}
|
|
355
|
+
* @default
|
|
356
|
+
* @memberof Settings */
|
|
357
|
+
export let canvasMinAspect: number;
|
|
358
|
+
/** Maximum aspect ratio of the canvas (width/height), unused if 0
|
|
359
|
+
* Can be used with canvasMinAspect to limit aspect ratio
|
|
360
|
+
* @type {number}
|
|
361
|
+
* @default
|
|
362
|
+
* @memberof Settings */
|
|
363
|
+
export let canvasMaxAspect: number;
|
|
351
364
|
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
352
365
|
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
353
366
|
* @type {Vector2}
|
|
@@ -557,6 +570,14 @@ declare module "littlejsengine" {
|
|
|
557
570
|
* @param {Vector2} size
|
|
558
571
|
* @memberof Settings */
|
|
559
572
|
export function setCanvasMaxSize(size: Vector2): void;
|
|
573
|
+
/** Set minimum aspect ratio of the canvas (width/height), unused if 0
|
|
574
|
+
* @param {number} aspect
|
|
575
|
+
* @memberof Settings */
|
|
576
|
+
export function setCanvasMinAspect(aspect: number): void;
|
|
577
|
+
/** Set maximum aspect ratio of the canvas (width/height), unused if 0
|
|
578
|
+
* @param {number} aspect
|
|
579
|
+
* @memberof Settings */
|
|
580
|
+
export function setCanvasMaxAspect(aspect: number): void;
|
|
560
581
|
/** Set fixed size of the canvas
|
|
561
582
|
* @param {Vector2} size
|
|
562
583
|
* @memberof Settings */
|
|
@@ -3210,8 +3231,6 @@ declare module "littlejsengine" {
|
|
|
3210
3231
|
nativeHeight: number;
|
|
3211
3232
|
/** @property {UIObject} - Object currently selected by navigation (gamepad or keyboard) */
|
|
3212
3233
|
navigationObject: any;
|
|
3213
|
-
/** @property {number} - Gamepad index to use for UI navigation */
|
|
3214
|
-
navigationGamepadIndex: number;
|
|
3215
3234
|
/** @property {Timer} - Cooldown timer for navigation inputs */
|
|
3216
3235
|
navigationTimer: Timer;
|
|
3217
3236
|
/** @property {number} - Time between navigation inputs in seconds */
|
package/dist/littlejs.esm.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.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 Debug System
|
|
@@ -2716,6 +2738,20 @@ let canvasClearColor = CLEAR_BLACK;
|
|
|
2716
2738
|
* @memberof Settings */
|
|
2717
2739
|
let canvasMaxSize = vec2(1920, 1080);
|
|
2718
2740
|
|
|
2741
|
+
/** Minimum aspect ratio of the canvas (width/height), unused if 0
|
|
2742
|
+
* Can be used with canvasMaxAspect to limit aspect ratio
|
|
2743
|
+
* @type {number}
|
|
2744
|
+
* @default
|
|
2745
|
+
* @memberof Settings */
|
|
2746
|
+
let canvasMinAspect = 0;
|
|
2747
|
+
|
|
2748
|
+
/** Maximum aspect ratio of the canvas (width/height), unused if 0
|
|
2749
|
+
* Can be used with canvasMinAspect to limit aspect ratio
|
|
2750
|
+
* @type {number}
|
|
2751
|
+
* @default
|
|
2752
|
+
* @memberof Settings */
|
|
2753
|
+
let canvasMaxAspect = 0;
|
|
2754
|
+
|
|
2719
2755
|
/** Fixed size of the canvas, if enabled canvas size never changes
|
|
2720
2756
|
* - you may also need to set mainCanvasSize if using screen space coords in startup
|
|
2721
2757
|
* @type {Vector2}
|
|
@@ -3012,6 +3048,16 @@ function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
|
|
|
3012
3048
|
* @memberof Settings */
|
|
3013
3049
|
function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
|
|
3014
3050
|
|
|
3051
|
+
/** Set minimum aspect ratio of the canvas (width/height), unused if 0
|
|
3052
|
+
* @param {number} aspect
|
|
3053
|
+
* @memberof Settings */
|
|
3054
|
+
function setCanvasMinAspect(aspect) { canvasMinAspect = aspect; }
|
|
3055
|
+
|
|
3056
|
+
/** Set maximum aspect ratio of the canvas (width/height), unused if 0
|
|
3057
|
+
* @param {number} aspect
|
|
3058
|
+
* @memberof Settings */
|
|
3059
|
+
function setCanvasMaxAspect(aspect) { canvasMaxAspect = aspect; }
|
|
3060
|
+
|
|
3015
3061
|
/** Set fixed size of the canvas
|
|
3016
3062
|
* @param {Vector2} size
|
|
3017
3063
|
* @memberof Settings */
|
|
@@ -5100,6 +5146,16 @@ function gamepadConnected(gamepad=gamepadPrimary)
|
|
|
5100
5146
|
return !!inputData[gamepad+1];
|
|
5101
5147
|
}
|
|
5102
5148
|
|
|
5149
|
+
/** Returns how many control sticks the passed in gamepad has
|
|
5150
|
+
* @param {number} [gamepad]
|
|
5151
|
+
* @return {number}
|
|
5152
|
+
* @memberof Input */
|
|
5153
|
+
function gamepadStickCount(gamepad=gamepadPrimary)
|
|
5154
|
+
{
|
|
5155
|
+
ASSERT(isNumber(gamepad), 'gamepad must be a number');
|
|
5156
|
+
return gamepadStickData[gamepad]?.length ?? 0;
|
|
5157
|
+
}
|
|
5158
|
+
|
|
5103
5159
|
/** True if a touch device has been detected
|
|
5104
5160
|
* @memberof Input */
|
|
5105
5161
|
const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
@@ -5440,9 +5496,11 @@ function inputUpdate()
|
|
|
5440
5496
|
dpad.set(x, -y);
|
|
5441
5497
|
sticks[0] = dpad.clampLength(); // clamp to circle
|
|
5442
5498
|
}
|
|
5443
|
-
const rightTouchStick = touchGamepadSticks[1] ?? vec2();
|
|
5444
5499
|
if (touchGamepadButtonCount === 1)
|
|
5500
|
+
{
|
|
5501
|
+
const rightTouchStick = touchGamepadSticks[1] ?? vec2();
|
|
5445
5502
|
sticks[1] = applyDeadZones(rightTouchStick);
|
|
5503
|
+
}
|
|
5446
5504
|
|
|
5447
5505
|
// read virtual gamepad buttons
|
|
5448
5506
|
const data = inputData[1] ?? (inputData[1] = []);
|
|
@@ -6534,8 +6592,7 @@ class CanvasLayer extends EngineObject
|
|
|
6534
6592
|
/** Destroy this canvas layer */
|
|
6535
6593
|
destroy()
|
|
6536
6594
|
{
|
|
6537
|
-
if (this.destroyed)
|
|
6538
|
-
return;
|
|
6595
|
+
if (this.destroyed) return;
|
|
6539
6596
|
|
|
6540
6597
|
this.textureInfo.destroyWebGLTexture();
|
|
6541
6598
|
super.destroy();
|
|
@@ -7087,9 +7144,9 @@ class ParticleEmitter extends EngineObject
|
|
|
7087
7144
|
/** @property {Color} - Color at start of life 2, randomized between start colors */
|
|
7088
7145
|
this.colorStartB = colorStartB.copy();
|
|
7089
7146
|
/** @property {Color} - Color at end of life 1, randomized between end colors */
|
|
7090
|
-
this.colorEndA
|
|
7147
|
+
this.colorEndA = colorEndA.copy();
|
|
7091
7148
|
/** @property {Color} - Color at end of life 2, randomized between end colors */
|
|
7092
|
-
this.colorEndB
|
|
7149
|
+
this.colorEndB = colorEndB.copy();
|
|
7093
7150
|
/** @property {boolean} - Should color be randomized linearly or across each component */
|
|
7094
7151
|
this.randomColorLinear = randomColorLinear;
|
|
7095
7152
|
|
|
@@ -7173,8 +7230,10 @@ class ParticleEmitter extends EngineObject
|
|
|
7173
7230
|
if (debugParticles)
|
|
7174
7231
|
{
|
|
7175
7232
|
// show emitter bounds
|
|
7176
|
-
|
|
7177
|
-
|
|
7233
|
+
if (typeof this.emitSize === 'number')
|
|
7234
|
+
debugCircle(this.pos, this.emitSize/2, '#0f0');
|
|
7235
|
+
else
|
|
7236
|
+
debugRect(this.pos, this.emitSize, '#0f0', 0, this.angle);
|
|
7178
7237
|
}
|
|
7179
7238
|
}
|
|
7180
7239
|
|
|
@@ -7184,8 +7243,8 @@ class ParticleEmitter extends EngineObject
|
|
|
7184
7243
|
{
|
|
7185
7244
|
// spawn a particle
|
|
7186
7245
|
let pos = typeof this.emitSize === 'number' ? // check if number was used
|
|
7187
|
-
randInCircle(this.emitSize/2)
|
|
7188
|
-
: vec2(rand(-.5,.5), rand(-.5,.5))
|
|
7246
|
+
randInCircle(this.emitSize/2) // circle emitter
|
|
7247
|
+
: vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
|
|
7189
7248
|
.multiply(this.emitSize).rotate(this.angle)
|
|
7190
7249
|
let angle = rand(this.particleConeAngle, -this.particleConeAngle);
|
|
7191
7250
|
if (!this.localSpace)
|
|
@@ -8980,8 +9039,6 @@ class UISystemPlugin
|
|
|
8980
9039
|
// navigation properties
|
|
8981
9040
|
/** @property {UIObject} - Object currently selected by navigation (gamepad or keyboard) */
|
|
8982
9041
|
this.navigationObject = undefined;
|
|
8983
|
-
/** @property {number} - Gamepad index to use for UI navigation */
|
|
8984
|
-
this.navigationGamepadIndex = 0;
|
|
8985
9042
|
/** @property {Timer} - Cooldown timer for navigation inputs */
|
|
8986
9043
|
this.navigationTimer = new Timer(undefined, true);
|
|
8987
9044
|
/** @property {number} - Time between navigation inputs in seconds */
|
|
@@ -9389,9 +9446,8 @@ class UISystemPlugin
|
|
|
9389
9446
|
const both = uiSystem.navigationDirection === 2;
|
|
9390
9447
|
if (isUsingGamepad)
|
|
9391
9448
|
{
|
|
9392
|
-
const
|
|
9393
|
-
const
|
|
9394
|
-
const dpad = gamepadDpad(gamepad);
|
|
9449
|
+
const stick = gamepadStick(0, gamepadPrimary);
|
|
9450
|
+
const dpad = gamepadDpad(gamepadPrimary);
|
|
9395
9451
|
if (both)
|
|
9396
9452
|
return -(stick.y || dpad.y) || (stick.x || dpad.x);
|
|
9397
9453
|
return vertical ? -(stick.y || dpad.y) : (stick.x || dpad.x);
|
|
@@ -9417,9 +9473,8 @@ class UISystemPlugin
|
|
|
9417
9473
|
const vertical = uiSystem.navigationDirection === 1;
|
|
9418
9474
|
if (isUsingGamepad)
|
|
9419
9475
|
{
|
|
9420
|
-
const
|
|
9421
|
-
const
|
|
9422
|
-
const dpad = gamepadDpad(gamepad);
|
|
9476
|
+
const stick = gamepadStick(0, gamepadPrimary);
|
|
9477
|
+
const dpad = gamepadDpad(gamepadPrimary);
|
|
9423
9478
|
return !vertical ? (stick.y || dpad.y) : (stick.x || dpad.x);
|
|
9424
9479
|
}
|
|
9425
9480
|
const back = !vertical ? 'ArrowUp' : 'ArrowLeft';
|
|
@@ -9431,8 +9486,7 @@ class UISystemPlugin
|
|
|
9431
9486
|
* @return {boolean} */
|
|
9432
9487
|
getNavigationWasPressed()
|
|
9433
9488
|
{
|
|
9434
|
-
|
|
9435
|
-
return isUsingGamepad ? gamepadWasPressed(0, gamepad) :
|
|
9489
|
+
return isUsingGamepad ? gamepadWasPressed(0, gamepadPrimary) :
|
|
9436
9490
|
keyWasPressed('Space') || keyWasPressed('Enter');
|
|
9437
9491
|
}
|
|
9438
9492
|
|
|
@@ -12352,6 +12406,8 @@ export
|
|
|
12352
12406
|
canvasColorTiles,
|
|
12353
12407
|
canvasClearColor,
|
|
12354
12408
|
canvasMaxSize,
|
|
12409
|
+
canvasMinAspect,
|
|
12410
|
+
canvasMaxAspect,
|
|
12355
12411
|
canvasFixedSize,
|
|
12356
12412
|
canvasPixelated,
|
|
12357
12413
|
overlayCanvasPixelated,
|
|
@@ -12395,6 +12451,8 @@ export
|
|
|
12395
12451
|
setCanvasColorTiles,
|
|
12396
12452
|
setCanvasClearColor,
|
|
12397
12453
|
setCanvasMaxSize,
|
|
12454
|
+
setCanvasMinAspect,
|
|
12455
|
+
setCanvasMaxAspect,
|
|
12398
12456
|
setCanvasFixedSize,
|
|
12399
12457
|
setCanvasPixelated,
|
|
12400
12458
|
setOverlayCanvasPixelated,
|