littlejsengine 1.17.5 → 1.17.11
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/LICENSE +10 -10
- package/dist/littlejs.d.ts +189 -97
- package/dist/littlejs.esm.js +637 -477
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +633 -476
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +631 -474
- package/examples/electron/index.html +2 -2
- package/examples/games.jpg +0 -0
- package/examples/index.html +2 -2
- package/examples/logo.png +0 -0
- package/examples/logo2.png +0 -0
- package/examples/particles/index.html +1 -1
- package/examples/platformer/gameEffects.js +3 -3
- package/examples/screenshot.jpg +0 -0
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/colors.js +1 -1
- package/examples/shorts/fontImage.js +1 -1
- package/examples/shorts/fps.js +1 -1
- package/examples/shorts/music.js +3 -2
- package/examples/shorts/musicPlayer.js +2 -1
- package/examples/shorts/parallax.js +2 -2
- package/examples/shorts/tiltedView.js +1 -1
- package/examples/starter/index.html +2 -2
- package/package.json +1 -1
- package/plugins/box2d.js +6 -7
- package/plugins/pluginExport.js +2 -1
- package/plugins/postProcess.js +1 -1
- package/plugins/uiSystem.js +33 -21
- package/reference.md +6 -6
- package/src/engine.js +106 -152
- package/src/engineAudio.js +5 -5
- package/src/engineDebug.js +1 -1
- package/src/engineDraw.js +36 -14
- package/src/engineExport.js +2 -0
- package/src/engineInput.js +1 -1
- package/src/engineMath.js +6 -4
- package/src/engineObject.js +24 -24
- package/src/engineParticles.js +241 -118
- package/src/engineTileLayer.js +66 -48
- package/src/engineUtilities.js +105 -78
- package/plugins/Box2D_License.txt +0 -17
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.17.
|
|
33
|
+
const engineVersion = '1.17.11';
|
|
34
34
|
|
|
35
35
|
/** Frames per second to update
|
|
36
36
|
* @type {number}
|
|
@@ -393,33 +393,11 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
393
393
|
workReadContext = workReadCanvas.getContext('2d', { willReadFrequently: true });
|
|
394
394
|
|
|
395
395
|
// create promises for loading images
|
|
396
|
-
const promises = imageSources.map((src,
|
|
397
|
-
new Promise(resolve =>
|
|
398
|
-
{
|
|
399
|
-
ASSERT(isString(src), 'imageSources must be an array of strings');
|
|
400
|
-
|
|
401
|
-
const image = new Image;
|
|
402
|
-
image.onerror = image.onload = ()=>
|
|
403
|
-
{
|
|
404
|
-
const textureInfo = new TextureInfo(image);
|
|
405
|
-
textureInfos[textureIndex] = textureInfo;
|
|
406
|
-
resolve();
|
|
407
|
-
}
|
|
408
|
-
image.crossOrigin = 'anonymous';
|
|
409
|
-
image.src = src;
|
|
410
|
-
})
|
|
411
|
-
);
|
|
396
|
+
const promises = imageSources.map((src, i)=> loadTexture(i, src));
|
|
412
397
|
|
|
398
|
+
// no images to load
|
|
413
399
|
if (!imageSources.length)
|
|
414
|
-
|
|
415
|
-
// no images to load
|
|
416
|
-
promises.push(new Promise(resolve =>
|
|
417
|
-
{
|
|
418
|
-
const textureInfo = new TextureInfo(new Image);
|
|
419
|
-
textureInfos[0] = textureInfo;
|
|
420
|
-
resolve();
|
|
421
|
-
}));
|
|
422
|
-
}
|
|
400
|
+
promises.push(loadTexture(0));
|
|
423
401
|
|
|
424
402
|
// load engine font image
|
|
425
403
|
promises.push(fontImageInit());
|
|
@@ -461,13 +439,13 @@ function engineObjectsUpdate()
|
|
|
461
439
|
engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
|
|
462
440
|
|
|
463
441
|
// recursive object update
|
|
464
|
-
function
|
|
442
|
+
function updateChildObject(o)
|
|
465
443
|
{
|
|
466
444
|
if (o.destroyed) return;
|
|
467
445
|
|
|
468
446
|
o.update();
|
|
469
447
|
for (const child of o.children)
|
|
470
|
-
|
|
448
|
+
updateChildObject(child);
|
|
471
449
|
}
|
|
472
450
|
for (const o of engineObjects)
|
|
473
451
|
{
|
|
@@ -477,7 +455,7 @@ function engineObjectsUpdate()
|
|
|
477
455
|
o.update();
|
|
478
456
|
o.updatePhysics();
|
|
479
457
|
for (const child of o.children)
|
|
480
|
-
|
|
458
|
+
updateChildObject(child);
|
|
481
459
|
o.updateTransforms();
|
|
482
460
|
}
|
|
483
461
|
|
|
@@ -486,11 +464,14 @@ function engineObjectsUpdate()
|
|
|
486
464
|
}
|
|
487
465
|
|
|
488
466
|
/** Destroy and remove all objects
|
|
467
|
+
* - This can be used to clear out all objects when restarting a level
|
|
468
|
+
* - Objects can override their destroy function to do cleanup or stick around
|
|
469
|
+
* @param {boolean} [immediate] - should attached effects be allowed to die off?
|
|
489
470
|
* @memberof Engine */
|
|
490
|
-
function engineObjectsDestroy()
|
|
471
|
+
function engineObjectsDestroy(immediate=true)
|
|
491
472
|
{
|
|
492
473
|
for (const o of engineObjects)
|
|
493
|
-
o.parent || o.destroy();
|
|
474
|
+
o.parent || o.destroy(immediate);
|
|
494
475
|
engineObjects = engineObjects.filter(o=>!o.destroyed);
|
|
495
476
|
}
|
|
496
477
|
|
|
@@ -565,164 +546,137 @@ function engineObjectsRaycast(start, end, objects=engineObjects)
|
|
|
565
546
|
///////////////////////////////////////////////////////////////////////////////
|
|
566
547
|
function drawEngineLogo(t)
|
|
567
548
|
{
|
|
549
|
+
const blackAndWhite = 0;
|
|
550
|
+
const showName = 1;
|
|
551
|
+
|
|
568
552
|
// LittleJS Logo and Splash Screen
|
|
569
553
|
const x = mainContext;
|
|
570
554
|
const w = mainCanvas.width = innerWidth;
|
|
571
555
|
const h = mainCanvas.height = innerHeight;
|
|
572
|
-
|
|
573
556
|
{
|
|
574
557
|
// background
|
|
575
558
|
const p3 = percent(t, 1, .8);
|
|
576
559
|
const p4 = percent(t, 0, .5);
|
|
577
|
-
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.
|
|
560
|
+
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.6);
|
|
578
561
|
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
579
562
|
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
580
563
|
x.save();
|
|
581
564
|
x.fillStyle = g;
|
|
582
565
|
x.fillRect(0,0,w,h);
|
|
583
566
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
567
|
+
const gradient = (X1,Y1,X2,Y2,C,S=1)=>
|
|
568
|
+
{
|
|
569
|
+
if (C >= 0)
|
|
570
|
+
{
|
|
571
|
+
if (blackAndWhite)
|
|
572
|
+
x.fillStyle = '#fff';
|
|
573
|
+
else
|
|
574
|
+
{
|
|
575
|
+
const g = x.fillStyle = x.createLinearGradient(X1,Y1,X2,Y2);
|
|
576
|
+
g.addColorStop(0,color(C,2));
|
|
577
|
+
g.addColorStop(1,color(C,1));
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
else
|
|
581
|
+
x.fillStyle = '#000';
|
|
582
|
+
C >= -1 ? (x.fill(), S && x.stroke()) : x.stroke();
|
|
583
|
+
}
|
|
584
|
+
const circle = (X,Y,R,A=0,B=2*PI,C,S)=>
|
|
587
585
|
{
|
|
588
586
|
x.beginPath();
|
|
589
|
-
x.
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const line = (X, Y, Z, W)=>
|
|
587
|
+
x.arc(X,Y,R,p*A,p*B);
|
|
588
|
+
gradient(X,Y-R,X,Y+R,C,S);
|
|
589
|
+
}
|
|
590
|
+
const rect = (X,Y,W,H,C)=>
|
|
594
591
|
{
|
|
595
592
|
x.beginPath();
|
|
596
|
-
x.
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
593
|
+
x.rect(X,Y,W,H*p);
|
|
594
|
+
gradient(X,Y+H,X+W,Y,C);
|
|
595
|
+
}
|
|
596
|
+
const poly = (points,C,Y,H)=>
|
|
601
597
|
{
|
|
602
|
-
const D = (A+B)/2, E = p*(B-A)/2;
|
|
603
598
|
x.beginPath();
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
x.
|
|
607
|
-
|
|
608
|
-
}
|
|
609
|
-
const color = (c
|
|
610
|
-
|
|
599
|
+
for (const p of points)
|
|
600
|
+
x.lineTo(p.x, p.y);
|
|
601
|
+
x.closePath();
|
|
602
|
+
gradient(0, Y, 0, Y+H,C);
|
|
603
|
+
}
|
|
604
|
+
const color = (c,l)=> l?`hsl(${[.95,.56,.13][c%3]*360} 99%${[0,50,75][l]}%`:'#000';
|
|
605
|
+
|
|
606
|
+
// center and fit tos screen
|
|
611
607
|
const alpha = wave(1,1,t);
|
|
612
608
|
const p = percent(alpha, .1, .5);
|
|
613
|
-
|
|
614
|
-
// setup
|
|
609
|
+
const size = min(6, min(w,h)/99);
|
|
615
610
|
x.translate(w/2,h/2);
|
|
616
|
-
const size = min(6, min(w,h)/99); // fit to screen
|
|
617
611
|
x.scale(size,size);
|
|
618
612
|
x.translate(-40,-35);
|
|
613
|
+
p < 1 && x.setLineDash([99*p,99]);
|
|
619
614
|
x.lineJoin = x.lineCap = 'round';
|
|
620
615
|
x.lineWidth = .1 + p*1.9;
|
|
616
|
+
//x.strokeStyle='#fff7';
|
|
617
|
+
|
|
618
|
+
if (showName)
|
|
619
|
+
{
|
|
620
|
+
// engine name text
|
|
621
|
+
const Y = 54;
|
|
622
|
+
const s = 'LittleJS';
|
|
623
|
+
x.font = '900 15.5px arial';
|
|
624
|
+
x.lineWidth = .1+p*3.9;
|
|
625
|
+
x.textAlign = 'center';
|
|
626
|
+
x.textBaseline = 'top';
|
|
627
|
+
rect(11,Y+1,59,8*p,-1);
|
|
628
|
+
x.beginPath();
|
|
621
629
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
rect(11,16,14,8,color(1,3));
|
|
638
|
-
rect(25,16,-14,24);
|
|
639
|
-
|
|
640
|
-
// cab window
|
|
641
|
-
rect(15,29,6,-9,color(2,2));
|
|
642
|
-
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
643
|
-
rect(21,21,-6,9);
|
|
644
|
-
|
|
645
|
-
// little stack
|
|
646
|
-
rect(37,14,9,6,color(3,2));
|
|
647
|
-
rect(37,14,4.5,6,color(3,3));
|
|
648
|
-
rect(37,14,9,6);
|
|
630
|
+
let w2 = 0;
|
|
631
|
+
for (let i=0;i<s.length;++i)
|
|
632
|
+
w2 += x.measureText(s[i]).width;
|
|
633
|
+
for (let j=2;j--;)
|
|
634
|
+
for (let i=0,X=40-w2/2;i<s.length;++i)
|
|
635
|
+
{
|
|
636
|
+
const w = x.measureText(s[i]).width, X2 = X+w/2;
|
|
637
|
+
gradient(X2,Y,X2+2,Y+13,i>5?1:0);
|
|
638
|
+
x[j?'strokeText':'fillText'](s[i],X2,Y+.5,17*p);
|
|
639
|
+
X += w;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
x.lineWidth = .1 + p*1.9;
|
|
643
|
+
rect(3,Y,73,0); // bottom
|
|
644
|
+
}
|
|
649
645
|
|
|
646
|
+
rect(7,15,26,-7,0); // cab top
|
|
647
|
+
rect(25,15,8,25,-1); // cab front
|
|
648
|
+
rect(10,40,15,-25,1); // cab back
|
|
649
|
+
rect(14,21,7,9,2); // cab window
|
|
650
|
+
rect(38,20,6,-6,2); // little stack
|
|
651
|
+
|
|
650
652
|
// big stack
|
|
651
|
-
rect(
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
rect(
|
|
655
|
-
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
656
|
-
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
657
|
-
circle(55,2,11.4,.5,PI-.5);
|
|
658
|
-
rect(45,7,20,-7,color(0,2));
|
|
659
|
-
rect(45,-1,20,4,color(0,3));
|
|
660
|
-
rect(45,-1,20,8);
|
|
653
|
+
rect(49,20,10,-6,0);
|
|
654
|
+
const stackPoints = [vec2(44,8),vec2(64,8),vec2(59,8+6*p),vec2(49,8+6*p)];
|
|
655
|
+
poly(stackPoints,2,8,6*p);
|
|
656
|
+
rect(44,8,20,-7,0);
|
|
661
657
|
|
|
662
658
|
// engine
|
|
663
|
-
for (let i=5;
|
|
664
|
-
|
|
665
|
-
// stagger radius to fix slight seam
|
|
666
|
-
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
667
|
-
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
668
|
-
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
669
|
-
}
|
|
659
|
+
for (let i=5;i--;) circle(59-i*6*p,30,10,0,2*PI,1,0);
|
|
660
|
+
circle(59,30,4,0,7,2); // light
|
|
670
661
|
|
|
671
662
|
// engine outline
|
|
672
|
-
|
|
673
|
-
circle(
|
|
674
|
-
circle(
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
//
|
|
678
|
-
circle(60,30,4,PI,3*PI,color(3,2));
|
|
679
|
-
circle(60,30,4,PI,2*PI,color(3,3));
|
|
680
|
-
circle(60,30,4,PI,3*PI);
|
|
681
|
-
|
|
682
|
-
// front brush
|
|
683
|
-
for (let i=6; i--;)
|
|
684
|
-
{
|
|
685
|
-
x.beginPath();
|
|
686
|
-
x.lineTo(53,54);
|
|
687
|
-
x.lineTo(53,40);
|
|
688
|
-
x.lineTo(53+(1+i*2.9)*p,40);
|
|
689
|
-
x.lineTo(53+(4+i*3.5)*p,54);
|
|
690
|
-
x.fillStyle = color(0,i%2+2);
|
|
691
|
-
x.fill();
|
|
692
|
-
i%2 && x.stroke();
|
|
693
|
-
}
|
|
663
|
+
rect(35,20,24,0); // top
|
|
664
|
+
circle(59,30,10); // front
|
|
665
|
+
circle(47,30,10,PI/2,PI*3/2); // middle
|
|
666
|
+
circle(35,30,10,PI/2,PI*3/2); // back
|
|
667
|
+
rect(7,40,13,7,-1); // bottom back
|
|
668
|
+
rect(17,40,43,14,-1); // bottom center
|
|
694
669
|
|
|
695
670
|
// wheels
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
for (let i=
|
|
700
|
-
for (let j=2; j--;)
|
|
701
|
-
{
|
|
702
|
-
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
703
|
-
x.stroke();
|
|
704
|
-
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
705
|
-
x.stroke();
|
|
706
|
-
}
|
|
707
|
-
line(6,40,68,40); // center
|
|
708
|
-
line(77,54,4,54); // bottom
|
|
709
|
-
|
|
710
|
-
// draw engine name
|
|
711
|
-
const s = engineName;
|
|
712
|
-
x.font = '900 16px arial';
|
|
713
|
-
x.textAlign = 'center';
|
|
714
|
-
x.textBaseline = 'top';
|
|
715
|
-
x.lineWidth = .1+p*3.9;
|
|
716
|
-
let w2 = 0;
|
|
717
|
-
for (let i=0; i<s.length; ++i)
|
|
718
|
-
w2 += x.measureText(s[i]).width;
|
|
719
|
-
for (let j=2; j--;)
|
|
720
|
-
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
671
|
+
for (let i=3;i--;) for (let j=2;j--;) circle(17+15*i,47,j?7:1,0,2*PI,2);
|
|
672
|
+
|
|
673
|
+
// cowcatcher
|
|
674
|
+
for (let i=2;i--;)
|
|
721
675
|
{
|
|
722
|
-
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
X += w;
|
|
676
|
+
let w=6, s=7, o=53+w*p*i
|
|
677
|
+
const points = [vec2(o+s,54),vec2(o,40),vec2(o+w*p,40),vec2(o+s+w*p,54)];
|
|
678
|
+
poly(points,0,40,14);
|
|
726
679
|
}
|
|
680
|
+
|
|
727
681
|
x.restore();
|
|
728
682
|
}
|
package/src/engineAudio.js
CHANGED
|
@@ -204,7 +204,7 @@ class Sound
|
|
|
204
204
|
|
|
205
205
|
/** Loads a sound from a URL and decodes it into sample data.
|
|
206
206
|
* @param {string} filename
|
|
207
|
-
* @return {Promise
|
|
207
|
+
* @return {Promise} */
|
|
208
208
|
async loadSound(filename)
|
|
209
209
|
{
|
|
210
210
|
const response = await fetch(filename);
|
|
@@ -262,7 +262,7 @@ class Sound
|
|
|
262
262
|
* // Control the individual instance
|
|
263
263
|
* instance.setVolume(.5);
|
|
264
264
|
* instance.pause();
|
|
265
|
-
* instance.
|
|
265
|
+
* instance.resume();
|
|
266
266
|
* instance.stop();
|
|
267
267
|
*/
|
|
268
268
|
class SoundInstance
|
|
@@ -376,7 +376,7 @@ class SoundInstance
|
|
|
376
376
|
this.startTime = undefined;
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
/**
|
|
379
|
+
/** Resume this sound instance */
|
|
380
380
|
resume()
|
|
381
381
|
{
|
|
382
382
|
if (!this.isPaused()) return;
|
|
@@ -390,8 +390,8 @@ class SoundInstance
|
|
|
390
390
|
*/
|
|
391
391
|
isPlaying() { return !!this.source; }
|
|
392
392
|
|
|
393
|
-
/** Check if this instance is paused
|
|
394
|
-
* @return {boolean} - True if
|
|
393
|
+
/** Check if this instance is paused or stopped (not currently playing)
|
|
394
|
+
* @return {boolean} - True if not playing
|
|
395
395
|
*/
|
|
396
396
|
isPaused() { return !this.isPlaying(); }
|
|
397
397
|
|
package/src/engineDebug.js
CHANGED
|
@@ -158,7 +158,7 @@ function debugPoint(pos, color, time, angle, screenSpace=false)
|
|
|
158
158
|
function debugLine(posA, posB, color, width=.1, time, screenSpace=false)
|
|
159
159
|
{
|
|
160
160
|
ASSERT(isVector2(posA), 'posA must be a vec2');
|
|
161
|
-
ASSERT(isVector2(posB), 'posB must be
|
|
161
|
+
ASSERT(isVector2(posB), 'posB must be a vec2');
|
|
162
162
|
ASSERT(isNumber(width), 'width must be a number');
|
|
163
163
|
|
|
164
164
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
package/src/engineDraw.js
CHANGED
|
@@ -190,7 +190,7 @@ class TileInfo
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
|
-
* Returns a tile info for an index using this tile as
|
|
193
|
+
* Returns a tile info for an index using this tile as reference
|
|
194
194
|
* @param {Vector2|number} [index=0]
|
|
195
195
|
* @return {TileInfo}
|
|
196
196
|
*/
|
|
@@ -729,6 +729,31 @@ function drawTextScreen(text, pos, size, color=WHITE, lineWidth=0, lineColor=BLA
|
|
|
729
729
|
///////////////////////////////////////////////////////////////////////////////
|
|
730
730
|
// Drawing utilities
|
|
731
731
|
|
|
732
|
+
/** Load a texture at a specific index
|
|
733
|
+
* @param {number} textureIndex - Index to store the texture at
|
|
734
|
+
* @param {string} [src] - Image source path
|
|
735
|
+
* @return {Promise} Promise that resolves when texture is loaded
|
|
736
|
+
* @memberof Draw */
|
|
737
|
+
async function loadTexture(textureIndex, src)
|
|
738
|
+
{
|
|
739
|
+
ASSERT(isNumber(textureIndex), 'textureIndex must be a number');
|
|
740
|
+
ASSERT(!textureInfos[textureIndex], 'textureIndex is already loaded!');
|
|
741
|
+
ASSERT(!src || isString(src), 'image src must be a string');
|
|
742
|
+
|
|
743
|
+
const image = new Image;
|
|
744
|
+
if (src)
|
|
745
|
+
{
|
|
746
|
+
await new Promise(resolve =>
|
|
747
|
+
{
|
|
748
|
+
image.onerror = image.onload = resolve;
|
|
749
|
+
image.crossOrigin = 'anonymous';
|
|
750
|
+
image.src = src;
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
textureInfos[textureIndex] = new TextureInfo(image);
|
|
755
|
+
}
|
|
756
|
+
|
|
732
757
|
/** Convert from screen to world space coordinates
|
|
733
758
|
* @param {Vector2} screenPos
|
|
734
759
|
* @return {Vector2}
|
|
@@ -1020,7 +1045,7 @@ let engineFontImage;
|
|
|
1020
1045
|
class FontImage
|
|
1021
1046
|
{
|
|
1022
1047
|
/** Create an image font
|
|
1023
|
-
* @param {TileInfo} tileInfo - Tile info of first
|
|
1048
|
+
* @param {TileInfo} tileInfo - Tile info of first character in font
|
|
1024
1049
|
*/
|
|
1025
1050
|
constructor(tileInfo)
|
|
1026
1051
|
{
|
|
@@ -1109,21 +1134,18 @@ class FontImage
|
|
|
1109
1134
|
}
|
|
1110
1135
|
|
|
1111
1136
|
// load engine font, called automatically on startup
|
|
1112
|
-
function fontImageInit()
|
|
1137
|
+
async function fontImageInit()
|
|
1113
1138
|
{
|
|
1114
|
-
|
|
1139
|
+
const image = new Image;
|
|
1140
|
+
await new Promise(resolve =>
|
|
1115
1141
|
{
|
|
1116
|
-
|
|
1117
|
-
const image = new Image;
|
|
1118
|
-
image.onerror = image.onload = ()=>
|
|
1119
|
-
{
|
|
1120
|
-
const tilePos=vec2(), tileSize=vec2(8), padding=1, bleed=0;
|
|
1121
|
-
const textureInfo = new TextureInfo(image);
|
|
1122
|
-
const tileInfo = new TileInfo(tilePos, tileSize, textureInfo, padding, bleed);
|
|
1123
|
-
engineFontImage = new FontImage(tileInfo);
|
|
1124
|
-
resolve();
|
|
1125
|
-
}
|
|
1142
|
+
image.onerror = image.onload = resolve;
|
|
1126
1143
|
image.crossOrigin = 'anonymous';
|
|
1127
1144
|
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAeAQMAAABnrVXaAAAABlBMVEUAAAD///+l2Z/dAAAAAXRSTlMAQObYZgAAAjpJREFUOMu9kzFu2zAUhn+CAROgqrk+B2l0BWYxMjlXeYaAtFtbdA1sGgHqRQfI0CNkSG5AwYB0BQ8d5Bsomwah6CPVeGg6tEPzAxLwyI+P78cP4u9lNO9OoMKnLMOobG5020/yaj/MrRcCGh1gBbyiLTPJEYaIiom5KM9Jq7KgynMGtb6L4GL4MF2H4LQKCXTvDVw2I4MsgZT7QLExdiutH+D08VOP3INXRrWX1/mmpbkNgAPYRVANb4xpcegYvhiNbIXauQICEjBuYLfMakaakWQeXxiZ0VDtuJCKs3ztMV59QtsHJNcRxDzfdL21ty3PrfIcXTN+E+GFAv6T5nbT9jd50/WFxb5ksdAv49qS6ouymG66ji08UMT6moykYLAo+V0j23GN4m829ZySAD5K7QsBfQTvOG8eE+gTeGYRAmnNAubN3hf5Zv9tJWDHp/VTuaSm7SN4fyINQqaNO3RMVxvpSPXnOChnRNvFcGY0gnwiPswYwTKVPE0zVtX3mTEIOoFzaqLrGuJaV+Uqumb71fVk/VoOH3cdLNQP/FHi8hV0CQNoqBZsUPlLPMsdCJro9QAaQQ0woDy9BJm0eTxCFnO9srcYlhNVlfR2EyTrph1uUtbUtAJifwRgrKuYdXVHeb0YI3QpawohQHkloI3J5FuVwI5ORxC9k2Tuz9Ir1IjgeIPGMHYkAZe2RuYkmWFmt3gGbTPOmBUWVTmRmHtGrfpzG/yuQNOKa6gBB/WA9khitPgl6/GP+gl2Af6tCbvaygAAAABJRU5ErkJggg==';
|
|
1128
1145
|
});
|
|
1146
|
+
|
|
1147
|
+
const tilePos=vec2(), tileSize=vec2(8), padding=1, bleed=0;
|
|
1148
|
+
const textureInfo = new TextureInfo(image);
|
|
1149
|
+
const tileInfo = new TileInfo(tilePos, tileSize, textureInfo, padding, bleed);
|
|
1150
|
+
engineFontImage = new FontImage(tileInfo);
|
|
1129
1151
|
}
|
package/src/engineExport.js
CHANGED
package/src/engineInput.js
CHANGED
|
@@ -769,7 +769,7 @@ function inputRender()
|
|
|
769
769
|
}
|
|
770
770
|
}
|
|
771
771
|
|
|
772
|
-
// center position for right
|
|
772
|
+
// center position for right touch pad face buttons
|
|
773
773
|
function touchGamepadButtonCenter()
|
|
774
774
|
{
|
|
775
775
|
const center = mainCanvasSize.subtract(vec2(touchGamepadSize));
|
package/src/engineMath.js
CHANGED
|
@@ -267,7 +267,9 @@ function wave(frequency=1, amplitude=1, t=time, offset=0)
|
|
|
267
267
|
function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
268
268
|
|
|
269
269
|
/**
|
|
270
|
-
* Check if object
|
|
270
|
+
* Check if object can be converted to a string (has a toString method)
|
|
271
|
+
* - Returns true for strings, numbers, and most objects
|
|
272
|
+
* - Returns false for null and undefined
|
|
271
273
|
* @param {any} s
|
|
272
274
|
* @return {boolean}
|
|
273
275
|
* @memberof Math */
|
|
@@ -344,7 +346,7 @@ function lineTest(posStart, posEnd, testFunction, normal)
|
|
|
344
346
|
if (stepX < 0)
|
|
345
347
|
hitPos.x -= e;
|
|
346
348
|
}
|
|
347
|
-
if (stepY < 0)
|
|
349
|
+
else if (stepY < 0)
|
|
348
350
|
hitPos.y -= e;
|
|
349
351
|
|
|
350
352
|
// set normal
|
|
@@ -1051,7 +1053,7 @@ class Color
|
|
|
1051
1053
|
* @memberof Math */
|
|
1052
1054
|
const WHITE = debugProtectConstant(rgb());
|
|
1053
1055
|
|
|
1054
|
-
/** Color - Clear White #
|
|
1056
|
+
/** Color - Clear White #ffffff00 with 0 alpha
|
|
1055
1057
|
* @type {Color}
|
|
1056
1058
|
* @memberof Math */
|
|
1057
1059
|
const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
|
|
@@ -1061,7 +1063,7 @@ const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
|
|
|
1061
1063
|
* @memberof Math */
|
|
1062
1064
|
const BLACK = debugProtectConstant(rgb(0,0,0));
|
|
1063
1065
|
|
|
1064
|
-
/** Color - Clear Black #
|
|
1066
|
+
/** Color - Clear Black #00000000 with 0 alpha
|
|
1065
1067
|
* @type {Color}
|
|
1066
1068
|
* @memberof Math */
|
|
1067
1069
|
const CLEAR_BLACK = debugProtectConstant(rgb(0,0,0,0));
|
package/src/engineObject.js
CHANGED
|
@@ -66,17 +66,19 @@ class EngineObject
|
|
|
66
66
|
this.additiveColor = undefined;
|
|
67
67
|
/** @property {boolean} - Should it flip along y axis when rendered */
|
|
68
68
|
this.mirror = false;
|
|
69
|
+
/** @property {boolean} - Has object been destroyed? */
|
|
70
|
+
this.destroyed = false;
|
|
69
71
|
|
|
70
72
|
// physical properties
|
|
71
|
-
/** @property {number}
|
|
73
|
+
/** @property {number} - How heavy the object is, static if 0 */
|
|
72
74
|
this.mass = objectDefaultMass;
|
|
73
|
-
/** @property {number}
|
|
75
|
+
/** @property {number} - How much to slow down velocity each frame (0-1) */
|
|
74
76
|
this.damping = objectDefaultDamping;
|
|
75
|
-
/** @property {number}
|
|
77
|
+
/** @property {number} - How much to slow down rotation each frame (0-1) */
|
|
76
78
|
this.angleDamping = objectDefaultAngleDamping;
|
|
77
|
-
/** @property {number}
|
|
79
|
+
/** @property {number} - How bouncy the object is when colliding (0-1) */
|
|
78
80
|
this.restitution = objectDefaultRestitution;
|
|
79
|
-
/** @property {number}
|
|
81
|
+
/** @property {number} - How much friction to apply when sliding (0-1) */
|
|
80
82
|
this.friction = objectDefaultFriction;
|
|
81
83
|
/** @property {number} - How much to scale gravity by for this object */
|
|
82
84
|
this.gravityScale = 1;
|
|
@@ -285,10 +287,10 @@ class EngineObject
|
|
|
285
287
|
if (!tileCollisionTest(oldPos, this.size, this))
|
|
286
288
|
{
|
|
287
289
|
// test which side we bounced off (or both if a corner)
|
|
288
|
-
const
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
if (
|
|
290
|
+
const isBlockedX = tileCollisionTest(vec2(this.pos.x, oldPos.y), this.size, this);
|
|
291
|
+
const isBlockedY = tileCollisionTest(vec2(oldPos.x, this.pos.y), this.size, this);
|
|
292
|
+
const restitution = max(this.restitution, hitLayer.restitution);
|
|
293
|
+
if (isBlockedX)
|
|
292
294
|
{
|
|
293
295
|
// try to move up a tiny bit
|
|
294
296
|
const epsilon = 1e-3;
|
|
@@ -304,16 +306,12 @@ class EngineObject
|
|
|
304
306
|
return;
|
|
305
307
|
}
|
|
306
308
|
|
|
307
|
-
// move to previous position and bounce
|
|
309
|
+
// move to previous X position and bounce
|
|
308
310
|
this.pos.x = oldPos.x;
|
|
309
|
-
this.velocity.x *= -
|
|
311
|
+
this.velocity.x *= -restitution;
|
|
310
312
|
}
|
|
311
|
-
if (
|
|
313
|
+
if (isBlockedY || !isBlockedX)
|
|
312
314
|
{
|
|
313
|
-
// bounce velocity
|
|
314
|
-
const restitution = max(this.restitution, hitLayer.restitution);
|
|
315
|
-
this.velocity.y *= -restitution;
|
|
316
|
-
|
|
317
315
|
if (wasFalling)
|
|
318
316
|
{
|
|
319
317
|
// adjust position to slightly away from nearest tile
|
|
@@ -329,10 +327,12 @@ class EngineObject
|
|
|
329
327
|
}
|
|
330
328
|
else
|
|
331
329
|
{
|
|
332
|
-
// move to previous position
|
|
330
|
+
// move to previous Y position
|
|
333
331
|
this.pos.y = oldPos.y;
|
|
334
332
|
this.groundObject = undefined;
|
|
335
333
|
}
|
|
334
|
+
// bounce velocity
|
|
335
|
+
this.velocity.y *= -restitution;
|
|
336
336
|
}
|
|
337
337
|
debugPhysics && debugRect(this.pos, this.size, '#f00');
|
|
338
338
|
}
|
|
@@ -350,19 +350,19 @@ class EngineObject
|
|
|
350
350
|
drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
-
/** Destroy this object, destroy its children, detach its parent, and mark it for removal
|
|
354
|
-
|
|
353
|
+
/** Destroy this object, destroy its children, detach its parent, and mark it for removal
|
|
354
|
+
* @param {boolean} [immediate] - should attached effects be allowed to die off? */
|
|
355
|
+
destroy(immediate=false)
|
|
355
356
|
{
|
|
356
|
-
if (this.destroyed)
|
|
357
|
-
return;
|
|
357
|
+
if (this.destroyed) return;
|
|
358
358
|
|
|
359
359
|
// disconnect from parent and destroy children
|
|
360
|
-
this.destroyed =
|
|
360
|
+
this.destroyed = true;
|
|
361
361
|
this.parent?.removeChild(this);
|
|
362
362
|
for (const child of this.children)
|
|
363
363
|
{
|
|
364
364
|
child.parent = undefined;
|
|
365
|
-
child.destroy();
|
|
365
|
+
child.destroy(immediate);
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
368
|
|
|
@@ -461,7 +461,7 @@ class EngineObject
|
|
|
461
461
|
}
|
|
462
462
|
|
|
463
463
|
/** Check if overlapping another engine object
|
|
464
|
-
* Collisions are
|
|
464
|
+
* Collisions are resolved to prevent overlaps
|
|
465
465
|
* @param {EngineObject} object
|
|
466
466
|
* @return {boolean} */
|
|
467
467
|
isOverlappingObject(object)
|