littlejsengine 1.10.4 → 1.10.7
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/README.md +8 -78
- package/dist/littlejs.d.ts +31 -17
- package/dist/littlejs.esm.js +121 -55
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +121 -55
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +121 -55
- package/examples/box2d/game.js +15 -4
- package/examples/box2d/gameObjects.js +10 -9
- package/examples/box2d/index.html +5 -5
- package/examples/box2d/scenes.js +4 -4
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/game.js +3 -0
- package/examples/electron/index.html +2 -2
- package/examples/htmlMenu/index.html +3 -3
- package/examples/js13k/game.js +3 -0
- package/examples/js13k/index.html +13 -13
- package/examples/module/game.js +3 -0
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +4 -1
- package/examples/platformer/game.js +19 -15
- package/examples/platformer/gameCharacter.js +3 -3
- package/examples/platformer/gameLevel.js +1 -0
- package/examples/platformer/index.html +8 -8
- package/examples/platformer/tiles.png +0 -0
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/game.js +3 -0
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +1 -1
- package/examples/typescript/game.js +2 -0
- package/examples/typescript/game.ts +3 -0
- package/examples/typescript/index.html +1 -1
- package/examples/uiSystem/game.js +23 -16
- package/examples/uiSystem/index.html +3 -14
- package/package.json +1 -1
- package/plugins/postProcess.js +2 -9
- package/plugins/uiSystem.js +84 -24
- package/src/engine.js +11 -11
- package/src/engineAudio.js +7 -13
- package/src/engineDraw.js +1 -1
- package/src/engineInput.js +7 -3
- package/src/engineMedals.js +19 -5
- package/src/engineObject.js +8 -5
- package/src/engineTileLayer.js +8 -7
- package/src/engineUtilities.js +59 -10
- package/examples/js13k/build/index.html +0 -1
- package/examples/js13k/build/index.js +0 -1
- package/examples/js13k/build/tiles.png +0 -0
- package/examples/js13k/game - Copy.zip +0 -0
- package/examples/js13k/game.zip +0 -0
- package/examples/starter/build/index.html +0 -2
- package/examples/starter/build/index.js +0 -1
- package/examples/starter/build/tiles.png +0 -0
- package/examples/starter/game.zip +0 -0
- package/examples/typescript/build/build/littlejs.esm.js +0 -4327
- package/examples/typescript/build/dist/littlejs.esm.js +0 -4425
- package/examples/typescript/build/examples/typescript/build.js +0 -24
- package/examples/typescript/build/examples/typescript/game.js +0 -100
- package/examples/typescript/build/examples/typescript/test/build/littlejs.esm.js +0 -3934
- package/examples/typescript/build/examples/typescript/test/examples/typescript/build.js +0 -86
- package/examples/typescript/build/examples/typescript/test/examples/typescript/game.js +0 -92
package/dist/littlejs.release.js
CHANGED
|
@@ -239,7 +239,8 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
|
239
239
|
* @memberof Random */
|
|
240
240
|
function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valueB); }
|
|
241
241
|
|
|
242
|
-
/** Returns a floored random value the two values passed in
|
|
242
|
+
/** Returns a floored random value between the two values passed in
|
|
243
|
+
* The upper bound is exclusive. (If 2 is passed in, result will be 0 or 1)
|
|
243
244
|
* @param {Number} valueA
|
|
244
245
|
* @param {Number} [valueB]
|
|
245
246
|
* @return {Number}
|
|
@@ -368,18 +369,24 @@ class Vector2
|
|
|
368
369
|
* @param {Number} [y] - Y axis location */
|
|
369
370
|
constructor(x=0, y=0)
|
|
370
371
|
{
|
|
371
|
-
ASSERT(typeof x == 'number' && typeof y == 'number');
|
|
372
372
|
/** @property {Number} - X axis location */
|
|
373
373
|
this.x = x;
|
|
374
374
|
/** @property {Number} - Y axis location */
|
|
375
375
|
this.y = y;
|
|
376
|
+
ASSERT(this.isValid());
|
|
376
377
|
}
|
|
377
378
|
|
|
378
379
|
/** Sets values of this vector and returns self
|
|
379
380
|
* @param {Number} [x] - X axis location
|
|
380
381
|
* @param {Number} [y] - Y axis location
|
|
381
382
|
* @return {Vector2} */
|
|
382
|
-
set(x=0, y=0)
|
|
383
|
+
set(x=0, y=0)
|
|
384
|
+
{
|
|
385
|
+
this.x = x;
|
|
386
|
+
this.y = y;
|
|
387
|
+
ASSERT(this.isValid());
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
383
390
|
|
|
384
391
|
/** Returns a new vector that is a copy of this
|
|
385
392
|
* @return {Vector2} */
|
|
@@ -571,6 +578,14 @@ class Vector2
|
|
|
571
578
|
if (debug)
|
|
572
579
|
return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
|
|
573
580
|
}
|
|
581
|
+
|
|
582
|
+
/** Checks if this is a valid vector
|
|
583
|
+
* @return {Boolean} */
|
|
584
|
+
isValid()
|
|
585
|
+
{
|
|
586
|
+
return typeof this.x == 'number' && !isNaN(this.x)
|
|
587
|
+
&& typeof this.y == 'number' && !isNaN(this.y);
|
|
588
|
+
}
|
|
574
589
|
}
|
|
575
590
|
|
|
576
591
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -631,6 +646,7 @@ class Color
|
|
|
631
646
|
this.b = b;
|
|
632
647
|
/** @property {Number} - Alpha */
|
|
633
648
|
this.a = a;
|
|
649
|
+
ASSERT(this.isValid());
|
|
634
650
|
}
|
|
635
651
|
|
|
636
652
|
/** Sets values of this color and returns self
|
|
@@ -640,7 +656,14 @@ class Color
|
|
|
640
656
|
* @param {Number} [a] - alpha
|
|
641
657
|
* @return {Color} */
|
|
642
658
|
set(r=1, g=1, b=1, a=1)
|
|
643
|
-
{
|
|
659
|
+
{
|
|
660
|
+
this.r = r;
|
|
661
|
+
this.g = g;
|
|
662
|
+
this.b = b;
|
|
663
|
+
this.a = a;
|
|
664
|
+
ASSERT(this.isValid());
|
|
665
|
+
return this;
|
|
666
|
+
}
|
|
644
667
|
|
|
645
668
|
/** Returns a new color that is a copy of this
|
|
646
669
|
* @return {Color} */
|
|
@@ -723,6 +746,7 @@ class Color
|
|
|
723
746
|
this.g = f(p, q, h);
|
|
724
747
|
this.b = f(p, q, h - 1/3);
|
|
725
748
|
this.a = a;
|
|
749
|
+
ASSERT(this.isValid());
|
|
726
750
|
return this;
|
|
727
751
|
}
|
|
728
752
|
|
|
@@ -750,7 +774,6 @@ class Color
|
|
|
750
774
|
else if (b == max)
|
|
751
775
|
h = (r - g) / d + 4;
|
|
752
776
|
}
|
|
753
|
-
|
|
754
777
|
return [h / 6, s, l, a];
|
|
755
778
|
}
|
|
756
779
|
|
|
@@ -783,11 +806,27 @@ class Color
|
|
|
783
806
|
* @return {Color} */
|
|
784
807
|
setHex(hex)
|
|
785
808
|
{
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
809
|
+
ASSERT(typeof hex == 'string' && hex[0] == '#');
|
|
810
|
+
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
811
|
+
|
|
812
|
+
if (hex.length < 6)
|
|
813
|
+
{
|
|
814
|
+
const fromHex = (c)=> clamp(parseInt(hex[c],16)/15);
|
|
815
|
+
this.r = fromHex(1);
|
|
816
|
+
this.g = fromHex(2),
|
|
817
|
+
this.b = fromHex(3);
|
|
818
|
+
this.a = hex.length == 5 ? fromHex(4) : 1;
|
|
819
|
+
}
|
|
820
|
+
else
|
|
821
|
+
{
|
|
822
|
+
const fromHex = (c)=> clamp(parseInt(hex.slice(c,c+2),16)/255);
|
|
823
|
+
this.r = fromHex(1);
|
|
824
|
+
this.g = fromHex(3),
|
|
825
|
+
this.b = fromHex(5);
|
|
826
|
+
this.a = hex.length == 9 ? fromHex(7) : 1;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
ASSERT(this.isValid());
|
|
791
830
|
return this;
|
|
792
831
|
}
|
|
793
832
|
|
|
@@ -801,6 +840,16 @@ class Color
|
|
|
801
840
|
const a = clamp(this.a)*255<<24;
|
|
802
841
|
return r + g + b + a;
|
|
803
842
|
}
|
|
843
|
+
|
|
844
|
+
/** Checks if this is a valid color
|
|
845
|
+
* @return {Boolean} */
|
|
846
|
+
isValid()
|
|
847
|
+
{
|
|
848
|
+
return typeof this.r == 'number' && !isNaN(this.r)
|
|
849
|
+
&& typeof this.g == 'number' && !isNaN(this.g)
|
|
850
|
+
&& typeof this.b == 'number' && !isNaN(this.b)
|
|
851
|
+
&& typeof this.a == 'number' && !isNaN(this.a);
|
|
852
|
+
}
|
|
804
853
|
}
|
|
805
854
|
|
|
806
855
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1436,7 +1485,7 @@ class EngineObject
|
|
|
1436
1485
|
* @param {Color} [color=(1,1,1,1)] - Color to apply to tile when rendered
|
|
1437
1486
|
* @param {Number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
1438
1487
|
*/
|
|
1439
|
-
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color, renderOrder=0)
|
|
1488
|
+
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=new Color, renderOrder=0)
|
|
1440
1489
|
{
|
|
1441
1490
|
// set passed in params
|
|
1442
1491
|
ASSERT(isVector2(pos) && isVector2(size), 'ensure pos and size are vec2s');
|
|
@@ -1550,15 +1599,18 @@ class EngineObject
|
|
|
1550
1599
|
|
|
1551
1600
|
// apply physics
|
|
1552
1601
|
const oldPos = this.pos.copy();
|
|
1553
|
-
this.
|
|
1554
|
-
this.
|
|
1555
|
-
|
|
1602
|
+
this.velocity.x *= this.damping;
|
|
1603
|
+
this.velocity.y *= this.damping;
|
|
1604
|
+
if (this.mass) // dont apply gravity to static objects
|
|
1605
|
+
this.velocity.y += gravity * this.gravityScale;
|
|
1606
|
+
this.pos.x += this.velocity.x;
|
|
1607
|
+
this.pos.y += this.velocity.y;
|
|
1556
1608
|
this.angle += this.angleVelocity *= this.angleDamping;
|
|
1557
1609
|
|
|
1558
1610
|
// physics sanity checks
|
|
1559
1611
|
ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
|
|
1560
1612
|
ASSERT(this.damping >= 0 && this.damping <= 1);
|
|
1561
|
-
if (!enablePhysicsSolver || !this.mass) // dont do collision for
|
|
1613
|
+
if (!enablePhysicsSolver || !this.mass) // dont do collision for static objects
|
|
1562
1614
|
return;
|
|
1563
1615
|
|
|
1564
1616
|
const wasMovingDown = this.velocity.y < 0;
|
|
@@ -1919,7 +1971,7 @@ let drawCount;
|
|
|
1919
1971
|
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
1920
1972
|
* tile(5, 8) // a tile at index 5 using a tile size of 8
|
|
1921
1973
|
* tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
|
|
1922
|
-
* tile(vec2(4,8), vec2(30,10)) // a tile at
|
|
1974
|
+
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
1923
1975
|
* @memberof Draw
|
|
1924
1976
|
*/
|
|
1925
1977
|
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
|
|
@@ -2468,7 +2520,7 @@ function keyWasReleased(key, device=0)
|
|
|
2468
2520
|
|
|
2469
2521
|
/** Clears all input
|
|
2470
2522
|
* @memberof Input */
|
|
2471
|
-
function clearInput() { inputData = [[]]; }
|
|
2523
|
+
function clearInput() { inputData = [[]]; touchGamepadButtons = []; }
|
|
2472
2524
|
|
|
2473
2525
|
/** Returns true if mouse button is down
|
|
2474
2526
|
* @function
|
|
@@ -2633,6 +2685,7 @@ function inputInit()
|
|
|
2633
2685
|
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
2634
2686
|
onwheel = (e)=> mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
2635
2687
|
oncontextmenu = (e)=> false; // prevent right click menu
|
|
2688
|
+
onblur = (e) => clearInput(); // reset input when focus is lost
|
|
2636
2689
|
|
|
2637
2690
|
// init touch input
|
|
2638
2691
|
if (isTouchDevice && touchInputEnable)
|
|
@@ -2831,10 +2884,13 @@ function touchInputInit()
|
|
|
2831
2884
|
if (touching)
|
|
2832
2885
|
{
|
|
2833
2886
|
touchGamepadTimer.set();
|
|
2834
|
-
if (paused)
|
|
2887
|
+
if (paused && !wasTouching)
|
|
2835
2888
|
{
|
|
2836
2889
|
// touch anywhere to press start when paused
|
|
2837
2890
|
touchGamepadButtons[9] = 1;
|
|
2891
|
+
|
|
2892
|
+
// call default touch handler so normal touch events still work
|
|
2893
|
+
handleTouchDefault(e);
|
|
2838
2894
|
return;
|
|
2839
2895
|
}
|
|
2840
2896
|
}
|
|
@@ -2859,7 +2915,7 @@ function touchInputInit()
|
|
|
2859
2915
|
const button = touchPos.subtract(buttonCenter).direction();
|
|
2860
2916
|
touchGamepadButtons[button] = 1;
|
|
2861
2917
|
}
|
|
2862
|
-
else if (touchPos.distance(startCenter) < touchGamepadSize)
|
|
2918
|
+
else if (touchPos.distance(startCenter) < touchGamepadSize && !wasTouching)
|
|
2863
2919
|
{
|
|
2864
2920
|
// virtual start button in center
|
|
2865
2921
|
touchGamepadButtons[9] = 1;
|
|
@@ -2947,7 +3003,7 @@ function touchGamepadRender()
|
|
|
2947
3003
|
/** Audio context used by the engine
|
|
2948
3004
|
* @type {AudioContext}
|
|
2949
3005
|
* @memberof Audio */
|
|
2950
|
-
let audioContext;
|
|
3006
|
+
let audioContext = new AudioContext;
|
|
2951
3007
|
|
|
2952
3008
|
/** Master gain node for all audio to pass through
|
|
2953
3009
|
* @type {GainNode}
|
|
@@ -2958,14 +3014,10 @@ function audioInit()
|
|
|
2958
3014
|
{
|
|
2959
3015
|
if (!soundEnable || headlessMode) return;
|
|
2960
3016
|
|
|
2961
|
-
// create audio context
|
|
2962
|
-
audioContext = new AudioContext;
|
|
2963
|
-
|
|
2964
|
-
// create and connect gain node
|
|
2965
3017
|
// (createGain is more widely spported then GainNode construtor)
|
|
2966
3018
|
audioGainNode = audioContext.createGain();
|
|
2967
3019
|
audioGainNode.connect(audioContext.destination);
|
|
2968
|
-
|
|
3020
|
+
audioGainNode.gain.value = soundVolume; // set starting value
|
|
2969
3021
|
}
|
|
2970
3022
|
|
|
2971
3023
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -3000,12 +3052,15 @@ class Sound
|
|
|
3000
3052
|
|
|
3001
3053
|
/** @property {Number} - How much to randomize frequency each time sound plays */
|
|
3002
3054
|
this.randomness = 0;
|
|
3055
|
+
|
|
3056
|
+
/** @property {GainNode} - Gain node for this sound */
|
|
3057
|
+
this.gainNode = audioContext.createGain();
|
|
3003
3058
|
|
|
3004
3059
|
if (zzfxSound)
|
|
3005
3060
|
{
|
|
3006
3061
|
// generate zzfx sound now for fast playback
|
|
3007
3062
|
const defaultRandomness = .05;
|
|
3008
|
-
this.randomness = zzfxSound[1]
|
|
3063
|
+
this.randomness = zzfxSound[1] != undefined ? zzfxSound[1] : defaultRandomness;
|
|
3009
3064
|
zzfxSound[1] = 0; // generate without randomness
|
|
3010
3065
|
this.sampleChannels = [zzfxG(...zzfxSound)];
|
|
3011
3066
|
this.sampleRate = zzfxR;
|
|
@@ -3046,18 +3101,13 @@ class Sound
|
|
|
3046
3101
|
|
|
3047
3102
|
// play the sound
|
|
3048
3103
|
const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
3049
|
-
this.gainNode = audioContext.createGain();
|
|
3050
3104
|
return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
|
|
3051
3105
|
}
|
|
3052
3106
|
|
|
3053
3107
|
/** Set the sound volume
|
|
3054
3108
|
* @param {Number} [volume] - How much to scale volume by
|
|
3055
3109
|
*/
|
|
3056
|
-
setVolume(volume=1)
|
|
3057
|
-
{
|
|
3058
|
-
if (this.gainNode)
|
|
3059
|
-
this.gainNode.gain.value = volume;
|
|
3060
|
-
}
|
|
3110
|
+
setVolume(volume=1) { this.gainNode.gain.value = volume; }
|
|
3061
3111
|
|
|
3062
3112
|
/** Stop the last instance of this sound that was played */
|
|
3063
3113
|
stop()
|
|
@@ -3541,18 +3591,18 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
3541
3591
|
|
|
3542
3592
|
|
|
3543
3593
|
|
|
3544
|
-
/** The tile collision layer
|
|
3594
|
+
/** The tile collision layer grid, use setTileCollisionData and getTileCollisionData to access
|
|
3545
3595
|
* @type {Array}
|
|
3546
3596
|
* @memberof TileCollision */
|
|
3547
3597
|
let tileCollision = [];
|
|
3548
3598
|
|
|
3549
|
-
/** Size of the tile collision layer
|
|
3599
|
+
/** Size of the tile collision layer 2d grid
|
|
3550
3600
|
* @type {Vector2}
|
|
3551
3601
|
* @memberof TileCollision */
|
|
3552
3602
|
let tileCollisionSize = vec2();
|
|
3553
3603
|
|
|
3554
3604
|
/** Clear and initialize tile collision
|
|
3555
|
-
* @param {Vector2} size
|
|
3605
|
+
* @param {Vector2} size - width and height of tile collision 2d grid
|
|
3556
3606
|
* @memberof TileCollision */
|
|
3557
3607
|
function initTileCollision(size)
|
|
3558
3608
|
{
|
|
@@ -3562,7 +3612,7 @@ function initTileCollision(size)
|
|
|
3562
3612
|
tileCollision[i] = 0;
|
|
3563
3613
|
}
|
|
3564
3614
|
|
|
3565
|
-
/** Set tile collision data
|
|
3615
|
+
/** Set tile collision data for a given cell in the grid
|
|
3566
3616
|
* @param {Vector2} pos
|
|
3567
3617
|
* @param {Number} [data]
|
|
3568
3618
|
* @memberof TileCollision */
|
|
@@ -3571,7 +3621,7 @@ function setTileCollisionData(pos, data=0)
|
|
|
3571
3621
|
pos.arrayCheck(tileCollisionSize) && (tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
|
|
3572
3622
|
}
|
|
3573
3623
|
|
|
3574
|
-
/** Get tile collision data
|
|
3624
|
+
/** Get tile collision data for a given cell in the grid
|
|
3575
3625
|
* @param {Vector2} pos
|
|
3576
3626
|
* @return {Number}
|
|
3577
3627
|
* @memberof TileCollision */
|
|
@@ -3602,7 +3652,8 @@ function tileCollisionTest(pos, size=vec2(), object)
|
|
|
3602
3652
|
return false;
|
|
3603
3653
|
}
|
|
3604
3654
|
|
|
3605
|
-
/** Return the center of first tile hit
|
|
3655
|
+
/** Return the center of first tile hit, undefined if nothing was hit.
|
|
3656
|
+
* This does not return the exact intersection, but the center of the tile hit.
|
|
3606
3657
|
* @param {Vector2} posStart
|
|
3607
3658
|
* @param {Vector2} posEnd
|
|
3608
3659
|
* @param {EngineObject} [object]
|
|
@@ -3847,8 +3898,8 @@ class TileLayer extends EngineObject
|
|
|
3847
3898
|
const d = this.getData(layerPos);
|
|
3848
3899
|
if (d.tile != undefined)
|
|
3849
3900
|
{
|
|
3850
|
-
const pos = this.pos.add(layerPos).add(vec2(.5));
|
|
3851
3901
|
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3902
|
+
const pos = layerPos.add(vec2(.5));
|
|
3852
3903
|
const tileInfo = tile(d.tile, s, this.tileInfo.textureIndex);
|
|
3853
3904
|
drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
3854
3905
|
}
|
|
@@ -4277,7 +4328,7 @@ function medalsInit(saveName)
|
|
|
4277
4328
|
// check if medals are unlocked
|
|
4278
4329
|
medalsSaveName = saveName;
|
|
4279
4330
|
if (!debugMedals)
|
|
4280
|
-
medalsForEach(medal=> medal.unlocked =
|
|
4331
|
+
medalsForEach(medal=> medal.unlocked = !!localStorage[medal.storageKey()]);
|
|
4281
4332
|
|
|
4282
4333
|
// engine automatically renders medals
|
|
4283
4334
|
engineAddPlugin(undefined, medalsRender);
|
|
@@ -4340,14 +4391,28 @@ class Medal
|
|
|
4340
4391
|
constructor(id, name, description='', icon='🏆', src)
|
|
4341
4392
|
{
|
|
4342
4393
|
ASSERT(id >= 0 && !medals[id]);
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4394
|
+
|
|
4395
|
+
/** @property {Number} - The unique identifier of the medal */
|
|
4396
|
+
this.id = id;
|
|
4397
|
+
|
|
4398
|
+
/** @property {String} - Name of the medal */
|
|
4346
4399
|
this.name = name;
|
|
4400
|
+
|
|
4401
|
+
/** @property {String} - Description of the medal */
|
|
4347
4402
|
this.description = description;
|
|
4403
|
+
|
|
4404
|
+
/** @property {String} - Icon for the medal */
|
|
4348
4405
|
this.icon = icon;
|
|
4406
|
+
|
|
4407
|
+
/** @property {Boolean} - Is the medal unlocked? */
|
|
4408
|
+
this.unlocked = false;
|
|
4409
|
+
|
|
4410
|
+
// load the source image if provided
|
|
4349
4411
|
if (src)
|
|
4350
4412
|
(this.image = new Image).src = src;
|
|
4413
|
+
|
|
4414
|
+
// add this to list of medals
|
|
4415
|
+
medals[id] = this;
|
|
4351
4416
|
}
|
|
4352
4417
|
|
|
4353
4418
|
/** Unlocks a medal if not already unlocked */
|
|
@@ -4358,7 +4423,7 @@ class Medal
|
|
|
4358
4423
|
|
|
4359
4424
|
// save the medal
|
|
4360
4425
|
ASSERT(medalsSaveName, 'save name must be set');
|
|
4361
|
-
localStorage[this.storageKey()] = this.unlocked =
|
|
4426
|
+
localStorage[this.storageKey()] = this.unlocked = true;
|
|
4362
4427
|
medalsDisplayQueue.push(this);
|
|
4363
4428
|
}
|
|
4364
4429
|
|
|
@@ -4766,7 +4831,7 @@ const engineName = 'LittleJS';
|
|
|
4766
4831
|
* @type {String}
|
|
4767
4832
|
* @default
|
|
4768
4833
|
* @memberof Engine */
|
|
4769
|
-
const engineVersion = '1.10.
|
|
4834
|
+
const engineVersion = '1.10.7';
|
|
4770
4835
|
|
|
4771
4836
|
/** Frames per second to update
|
|
4772
4837
|
* @type {Number}
|
|
@@ -4829,6 +4894,8 @@ const pluginUpdateList = [], pluginRenderList = [];
|
|
|
4829
4894
|
* @memberof Engine */
|
|
4830
4895
|
function engineAddPlugin(updateFunction, renderFunction)
|
|
4831
4896
|
{
|
|
4897
|
+
ASSERT(!pluginUpdateList.includes(updateFunction));
|
|
4898
|
+
ASSERT(!pluginRenderList.includes(renderFunction));
|
|
4832
4899
|
updateFunction && pluginUpdateList.push(updateFunction);
|
|
4833
4900
|
renderFunction && pluginRenderList.push(renderFunction);
|
|
4834
4901
|
}
|
|
@@ -4837,12 +4904,12 @@ function engineAddPlugin(updateFunction, renderFunction)
|
|
|
4837
4904
|
// Main engine functions
|
|
4838
4905
|
|
|
4839
4906
|
/** Startup LittleJS engine with your callback functions
|
|
4840
|
-
* @param {Function} gameInit
|
|
4841
|
-
* @param {Function} gameUpdate
|
|
4842
|
-
* @param {Function} gameUpdatePost - Called after physics and objects are updated,
|
|
4843
|
-
* @param {Function} gameRender
|
|
4844
|
-
* @param {Function} gameRenderPost - Called after objects are rendered,
|
|
4845
|
-
* @param {Array} [imageSources=[]] -
|
|
4907
|
+
* @param {Function|function():Promise} gameInit - Called once after the engine starts up
|
|
4908
|
+
* @param {Function} gameUpdate - Called every frame before objects are updated
|
|
4909
|
+
* @param {Function} gameUpdatePost - Called after physics and objects are updated, even when paused
|
|
4910
|
+
* @param {Function} gameRender - Called before objects are rendered, for drawing the background
|
|
4911
|
+
* @param {Function} gameRenderPost - Called after objects are rendered, useful for drawing UI
|
|
4912
|
+
* @param {Array} [imageSources=[]] - List of images to load
|
|
4846
4913
|
* @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default
|
|
4847
4914
|
* @memberof Engine */
|
|
4848
4915
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
@@ -4992,8 +5059,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4992
5059
|
|
|
4993
5060
|
function startEngine()
|
|
4994
5061
|
{
|
|
4995
|
-
gameInit();
|
|
4996
|
-
engineUpdate();
|
|
5062
|
+
new Promise((resolve) => resolve(gameInit())).then(engineUpdate);
|
|
4997
5063
|
}
|
|
4998
5064
|
|
|
4999
5065
|
if (headlessMode)
|
|
@@ -5008,7 +5074,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5008
5074
|
'width:100vw;height:100vh;' + // fill the window
|
|
5009
5075
|
'display:flex;' + // use flexbox
|
|
5010
5076
|
'align-items:center;' + // horizontal center
|
|
5011
|
-
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
5012
5077
|
'justify-content:center;' + // vertical center
|
|
5013
5078
|
'background:#000;' + // set background color
|
|
5014
5079
|
'user-select:none;' + // prevent hold to select
|
|
@@ -5346,4 +5411,5 @@ function drawEngineSplashScreen(t)
|
|
|
5346
5411
|
}
|
|
5347
5412
|
|
|
5348
5413
|
x.restore();
|
|
5349
|
-
}
|
|
5414
|
+
}
|
|
5415
|
+
|
package/examples/box2d/game.js
CHANGED
|
@@ -15,13 +15,11 @@
|
|
|
15
15
|
setShowSplashScreen(true);
|
|
16
16
|
//box2dDebug = 1; // enable box2d debug draw
|
|
17
17
|
|
|
18
|
-
// fix texture bleeding by shrinking tile slightly
|
|
19
|
-
tileFixBleedScale = .2;
|
|
20
|
-
|
|
21
18
|
// game variables
|
|
22
19
|
const maxScenes = 11;
|
|
23
20
|
let scene = 0;
|
|
24
21
|
let sceneName;
|
|
22
|
+
let spriteAtlas;
|
|
25
23
|
let groundObject;
|
|
26
24
|
let mouseJoint;
|
|
27
25
|
let car;
|
|
@@ -30,6 +28,19 @@ let repeatSpawnTimer = new Timer;
|
|
|
30
28
|
///////////////////////////////////////////////////////////////////////////////
|
|
31
29
|
function gameInit()
|
|
32
30
|
{
|
|
31
|
+
// create a table of all sprites
|
|
32
|
+
const gameTile = (i)=> tile(i, 16, 0, 1);
|
|
33
|
+
spriteAtlas =
|
|
34
|
+
{
|
|
35
|
+
circle: gameTile(0),
|
|
36
|
+
dot: gameTile(1),
|
|
37
|
+
circleOutline: gameTile(2),
|
|
38
|
+
squareOutline: gameTile(3),
|
|
39
|
+
wheel: gameTile(4),
|
|
40
|
+
gear: gameTile(5),
|
|
41
|
+
squareOutline2: gameTile(6),
|
|
42
|
+
};
|
|
43
|
+
|
|
33
44
|
loadScene(scene);
|
|
34
45
|
}
|
|
35
46
|
|
|
@@ -128,7 +139,7 @@ function gameRenderPost()
|
|
|
128
139
|
{
|
|
129
140
|
// draw mouse joint
|
|
130
141
|
const ab = vec2(mouseJoint.GetAnchorB());
|
|
131
|
-
drawTile(ab, vec2(.3),
|
|
142
|
+
drawTile(ab, vec2(.3), spriteAtlas.circle, BLACK);
|
|
132
143
|
drawLine(mousePos, ab, .1, BLACK);
|
|
133
144
|
}
|
|
134
145
|
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
function spawnBox(pos, size=1, color=WHITE, type=box2dBodyTypeDynamic, applyTexture=true, angle=0)
|
|
13
13
|
{
|
|
14
14
|
size = typeof size == 'number' ? vec2(size) : size; // square
|
|
15
|
-
const o = new Box2dObject(pos, size, applyTexture &&
|
|
15
|
+
const o = new Box2dObject(pos, size, applyTexture && spriteAtlas.squareOutline, angle, color, type);
|
|
16
|
+
o.drawSize = size.scale(1.02); // slightly enarge to cover gaps
|
|
16
17
|
o.addBox(size);
|
|
17
18
|
return o;
|
|
18
19
|
}
|
|
@@ -20,7 +21,7 @@ function spawnBox(pos, size=1, color=WHITE, type=box2dBodyTypeDynamic, applyText
|
|
|
20
21
|
function spawnCircle(pos, diameter=1, color=WHITE, type=box2dBodyTypeDynamic, applyTexture=true, angle=0)
|
|
21
22
|
{
|
|
22
23
|
const size = vec2(diameter);
|
|
23
|
-
const o = new Box2dObject(pos, size, applyTexture &&
|
|
24
|
+
const o = new Box2dObject(pos, size, applyTexture && spriteAtlas.circleOutline, angle, color, type);
|
|
24
25
|
o.addCircle(diameter);
|
|
25
26
|
return o;
|
|
26
27
|
}
|
|
@@ -115,7 +116,7 @@ class CarObject extends Box2dObject
|
|
|
115
116
|
const damping = .7;
|
|
116
117
|
const frequencyHz = 4;
|
|
117
118
|
const maxTorque = 50;
|
|
118
|
-
const sprite =
|
|
119
|
+
const sprite = spriteAtlas.wheel;
|
|
119
120
|
|
|
120
121
|
// create wheels
|
|
121
122
|
this.wheels = [];
|
|
@@ -208,7 +209,7 @@ class PulleyJointObjects extends Box2dObject
|
|
|
208
209
|
{
|
|
209
210
|
constructor(pos, size, color, connectionPos)
|
|
210
211
|
{
|
|
211
|
-
super(pos, size,
|
|
212
|
+
super(pos, size, spriteAtlas.squareOutline, 0, color);
|
|
212
213
|
this.addBox(size);
|
|
213
214
|
this.connectionPos = connectionPos;
|
|
214
215
|
}
|
|
@@ -228,7 +229,7 @@ class MotorJointObject extends Box2dObject
|
|
|
228
229
|
{
|
|
229
230
|
constructor(pos, size, color, otherObject)
|
|
230
231
|
{
|
|
231
|
-
super(pos, size,
|
|
232
|
+
super(pos, size, spriteAtlas.wheel, 0, color);
|
|
232
233
|
this.addCircle(size.x);
|
|
233
234
|
this.connectionPos = pos;
|
|
234
235
|
const joint = box2dCreateMotorJoint(otherObject, this);
|
|
@@ -265,7 +266,7 @@ class SoftBodyObject extends Box2dObject
|
|
|
265
266
|
const mass = .1;
|
|
266
267
|
const center = vec2(x-nodeSize.x/2, y-nodeSize.y/2);
|
|
267
268
|
const p = pos.add(center.multiply(spacing));
|
|
268
|
-
const o = new Box2dObject(p, vec2(objectDiameter*1.1),
|
|
269
|
+
const o = new Box2dObject(p, vec2(objectDiameter*1.1), spriteAtlas.circle, 0, color);
|
|
269
270
|
o.addCircle(objectDiameter);
|
|
270
271
|
o.setMass(mass);
|
|
271
272
|
o.setAngularDamping(10);
|
|
@@ -336,7 +337,7 @@ class ClothObject extends Box2dObject
|
|
|
336
337
|
const mass = .5;
|
|
337
338
|
const center = vec2(x-nodeSize.x/2, y-nodeSize.y/2);
|
|
338
339
|
const p = pos.add(center.multiply(spacing));
|
|
339
|
-
const o = new Box2dObject(p, vec2(.4),
|
|
340
|
+
const o = new Box2dObject(p, vec2(.4), spriteAtlas.circle, 0, color);
|
|
340
341
|
o.addCircle(objectDiameter);
|
|
341
342
|
o.setFilterData(2, 2);
|
|
342
343
|
o.setLinearDamping(1);
|
|
@@ -463,7 +464,7 @@ function explosion(pos, radius=3, strength=300)
|
|
|
463
464
|
new ParticleEmitter(
|
|
464
465
|
pos, 0, // pos, angle
|
|
465
466
|
radius/2, .2, 50*radius, PI,// emitSize, emitTime, emitRate, emiteCone
|
|
466
|
-
|
|
467
|
+
spriteAtlas.dot, // tileInfo
|
|
467
468
|
hsl(0,0,0), hsl(0,0,0), // colorStartA, colorStartB
|
|
468
469
|
hsl(0,0,0,0), hsl(0,0,0,0), // colorEndA, colorEndB
|
|
469
470
|
1, 1, 2, .1, .1, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
@@ -475,7 +476,7 @@ function explosion(pos, radius=3, strength=300)
|
|
|
475
476
|
new ParticleEmitter(
|
|
476
477
|
pos, 0, // pos, angle
|
|
477
478
|
radius, .1, 100*radius, PI, // emitSize, emitTime, emitRate, emiteCone
|
|
478
|
-
|
|
479
|
+
spriteAtlas.dot, // tileInfo
|
|
479
480
|
hsl(0,1,.5), hsl(.15,1,.5), // colorStartA, colorStartB
|
|
480
481
|
hsl(0,1,.5,0), hsl(.1, 1,.5,0), // colorEndA, colorEndB
|
|
481
482
|
.7, 1, .5, .1, .1, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
9
|
<script src=../../dist/littlejs.js></script>
|
|
10
|
-
<script src=../../plugins/Box2D_v2.3.1_min.wasm.js?
|
|
11
|
-
<script src=../../plugins/box2d.js?
|
|
12
|
-
<script src=scenes.js?
|
|
13
|
-
<script src=gameObjects.js?
|
|
14
|
-
<script src=game.js?
|
|
10
|
+
<script src=../../plugins/Box2D_v2.3.1_min.wasm.js?1105></script>
|
|
11
|
+
<script src=../../plugins/box2d.js?1105></script>
|
|
12
|
+
<script src=scenes.js?1105></script>
|
|
13
|
+
<script src=gameObjects.js?1105></script>
|
|
14
|
+
<script src=game.js?1105></script>
|
package/examples/box2d/scenes.js
CHANGED
|
@@ -132,7 +132,7 @@ function loadScene(_scene)
|
|
|
132
132
|
const o1 = spawnCircle(vec2(23.5,3), 3, randColor());
|
|
133
133
|
const j1 = box2dCreateRevoluteJoint(groundObject, o1, o1.pos);
|
|
134
134
|
const o2 = spawnCircle(vec2(28,3), 6, randColor());
|
|
135
|
-
o1.tileInfo = o2.tileInfo =
|
|
135
|
+
o1.tileInfo = o2.tileInfo = spriteAtlas.gear;
|
|
136
136
|
const j2 = box2dCreateRevoluteJoint(groundObject, o2, o2.pos);
|
|
137
137
|
box2dCreateGearJoint(o1, o2, j1, j2, 2);
|
|
138
138
|
}
|
|
@@ -144,21 +144,21 @@ function loadScene(_scene)
|
|
|
144
144
|
}
|
|
145
145
|
{
|
|
146
146
|
// distance joint
|
|
147
|
-
const o1 = new Box2dObject(vec2(30,11), vec2(4),
|
|
147
|
+
const o1 = new Box2dObject(vec2(30,11), vec2(4), spriteAtlas.circleOutline, 0, randColor(), box2dBodyTypeStatic);
|
|
148
148
|
o1.renderOrder = -2;
|
|
149
149
|
const o2 = spawnCircle(vec2(30,8), 2, randColor());
|
|
150
150
|
box2dCreateDistanceJoint(o1, o2);
|
|
151
151
|
}
|
|
152
152
|
{
|
|
153
153
|
// motor joint
|
|
154
|
-
const o = new Box2dObject(vec2(10,8), vec2(4),
|
|
154
|
+
const o = new Box2dObject(vec2(10,8), vec2(4), spriteAtlas.circleOutline, 0, randColor(), box2dBodyTypeStatic);
|
|
155
155
|
o.renderOrder = -2;
|
|
156
156
|
new MotorJointObject(vec2(10,8), vec2(2), randColor(), o);
|
|
157
157
|
}
|
|
158
158
|
{
|
|
159
159
|
// friction joint
|
|
160
160
|
const o = spawnBox(vec2(10,15), 3, randColor());
|
|
161
|
-
o.tileInfo =
|
|
161
|
+
o.tileInfo = spriteAtlas.squareOutline2;
|
|
162
162
|
const joint = box2dCreateFrictionJoint(groundObject, o);
|
|
163
163
|
joint.SetMaxForce(200);
|
|
164
164
|
joint.SetMaxTorque(200);
|
package/examples/box2d/tiles.png
CHANGED
|
Binary file
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel=icon type=image/png href=../favicon.png>
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
|
-
<script src=../../dist/littlejs.js?
|
|
9
|
+
<script src=../../dist/littlejs.js?1105></script>
|
|
10
10
|
<script src=../../plugins/postProcess.js></script>
|
|
11
|
-
<script src=gameObjects.js?
|
|
12
|
-
<script src=game.js?
|
|
11
|
+
<script src=gameObjects.js?1105></script>
|
|
12
|
+
<script src=game.js?1105></script>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
9
|
<!-- LittleJS Engine -->
|
|
10
|
-
<script src=../../dist/littlejs.js?
|
|
10
|
+
<script src=../../dist/littlejs.js?1105></script>
|
|
11
11
|
|
|
12
12
|
<!-- Add your game scripts here -->
|
|
13
|
-
<script src=game.js?
|
|
13
|
+
<script src=game.js?1105></script>
|