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.
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
33
33
  * @type {string}
34
34
  * @default
35
35
  * @memberof Engine */
36
- const engineVersion = '1.15.8';
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
- // clear canvas and set fixed size
312
- mainCanvas.width = canvasFixedSize.x;
313
- mainCanvas.height = canvasFixedSize.y;
314
-
315
- // fit to window by adding space on top or bottom if necessary
316
- const aspect = innerWidth / innerHeight;
317
- const fixedAspect = mainCanvas.width / mainCanvas.height;
318
- (glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
319
- (glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
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
- // clear canvas and set size to same as window
324
- mainCanvas.width = min(innerWidth, canvasMaxSize.x);
325
- mainCanvas.height = min(innerHeight, canvasMaxSize.y);
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';// compatibility for ios
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
- // set canvases
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) // all objects
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) // bounding box test
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 // circle test
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 = colorEndA.copy();
6470
+ this.colorEndA = colorEndA.copy();
6414
6471
  /** @property {Color} - Color at end of life 2, randomized between end colors */
6415
- this.colorEndB = colorEndB.copy();
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
- const emitSize = typeof this.emitSize === 'number' ? vec2(this.emitSize) : this.emitSize;
6500
- debugRect(this.pos, emitSize, '#0f0', 0, this.angle);
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) // circle emitter
6511
- : vec2(rand(-.5,.5), rand(-.5,.5)) // box emitter
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 gamepad = this.navigationGamepadIndex;
8716
- const stick = gamepadStick(0, gamepad);
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 gamepad = this.navigationGamepadIndex;
8744
- const stick = gamepadStick(0, gamepad);
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
- const gamepad = this.navigationGamepadIndex;
8758
- return isUsingGamepad ? gamepadWasPressed(0, gamepad) :
8812
+ return isUsingGamepad ? gamepadWasPressed(0, gamepadPrimary) :
8759
8813
  keyWasPressed('Space') || keyWasPressed('Enter');
8760
8814
  }
8761
8815
 
@@ -104,7 +104,7 @@ function gameUpdate()
104
104
  }
105
105
 
106
106
  // controls
107
- if (LJS.mouseIsDown(1) || LJS.mouseIsDown('KeyZ'))
107
+ if (LJS.mouseIsDown(1) || LJS.keyIsDown('KeyZ'))
108
108
  {
109
109
  const isSet = repeatSpawnTimer.isSet();
110
110
  if (!isSet || repeatSpawnTimer.elapsed())
@@ -7,7 +7,7 @@
7
7
  </head><body>
8
8
 
9
9
  <!-- LittleJS Engine -->
10
- <script src=../../dist/littlejs.js?1.15.8></script>
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.8></script>
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.8></script>
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>
@@ -13,7 +13,7 @@ function gameInit()
13
13
  canvasClearColor = hsl(0,0,.8);
14
14
 
15
15
  // example text
16
- uiMenu.addChild(new UIText(vec2(-80,-120), vec2(500, 80),
16
+ uiMenu.addChild(new UIText(vec2(-80,-120), vec2(400, 80),
17
17
  'LittleJS UI\nSystem Demo'));
18
18
 
19
19
  // example image