littlejsengine 1.10.4 → 1.11.1
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/LittleJS.zip +0 -0
- package/README.md +8 -78
- package/dist/littlejs.d.ts +60 -25
- package/dist/littlejs.esm.js +164 -63
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +160 -62
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +160 -62
- 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/index.html +10 -3
- package/examples/js13k/game.js +3 -0
- package/examples/js13k/index.html +13 -13
- package/examples/module/game.js +4 -1
- 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/game.js +3 -2
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/game.js +47 -10
- package/examples/starter/index.html +13 -13
- package/examples/starter/tiles.png +0 -0
- 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 +7 -2
- package/plugins/postProcess.js +2 -9
- package/plugins/uiSystem.js +85 -25
- package/reference.md +1 -1
- package/shortExamples/engine.html +71 -0
- package/shortExamples/index.html +47 -0
- package/shortExamples/index.js +0 -0
- package/shortExamples/tiles.png +0 -0
- package/src/engine.js +14 -12
- package/src/engineAudio.js +6 -9
- package/src/engineDraw.js +23 -7
- package/src/engineExport.js +4 -1
- package/src/engineInput.js +7 -3
- package/src/engineMedals.js +19 -5
- package/src/engineObject.js +8 -5
- package/src/engineSettings.js +13 -2
- package/src/engineTileLayer.js +9 -8
- package/src/engineUtilities.js +59 -10
- package/src/engineWebGL.js +1 -1
- 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
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -952,12 +1001,18 @@ let canvasMaxSize = vec2(1920, 1080);
|
|
|
952
1001
|
* @memberof Settings */
|
|
953
1002
|
let canvasFixedSize = vec2();
|
|
954
1003
|
|
|
955
|
-
/**
|
|
1004
|
+
/** Use nearest neighbor scaling algorithm for canvas for more pixelated look
|
|
956
1005
|
* @type {Boolean}
|
|
957
1006
|
* @default
|
|
958
1007
|
* @memberof Settings */
|
|
959
1008
|
let canvasPixelated = true;
|
|
960
1009
|
|
|
1010
|
+
/** Disables texture filtering for crisper pixel art
|
|
1011
|
+
* @type {Boolean}
|
|
1012
|
+
* @default
|
|
1013
|
+
* @memberof Settings */
|
|
1014
|
+
let tilesPixelated = true;
|
|
1015
|
+
|
|
961
1016
|
/** Default font used for text rendering
|
|
962
1017
|
* @type {String}
|
|
963
1018
|
* @default
|
|
@@ -1206,11 +1261,16 @@ function setCanvasMaxSize(size) { canvasMaxSize = size; }
|
|
|
1206
1261
|
* @memberof Settings */
|
|
1207
1262
|
function setCanvasFixedSize(size) { canvasFixedSize = size; }
|
|
1208
1263
|
|
|
1209
|
-
/**
|
|
1264
|
+
/** Use nearest neighbor scaling algorithm for canvas for more pixelated look
|
|
1210
1265
|
* @param {Boolean} pixelated
|
|
1211
1266
|
* @memberof Settings */
|
|
1212
1267
|
function setCanvasPixelated(pixelated) { canvasPixelated = pixelated; }
|
|
1213
1268
|
|
|
1269
|
+
/** Disables texture filtering for crisper pixel art
|
|
1270
|
+
* @param {Boolean} pixelated
|
|
1271
|
+
* @memberof Settings */
|
|
1272
|
+
function setTilesPixelated(pixelated) { tilesPixelated = pixelated; }
|
|
1273
|
+
|
|
1214
1274
|
/** Set default font used for text rendering
|
|
1215
1275
|
* @param {String} font
|
|
1216
1276
|
* @memberof Settings */
|
|
@@ -1436,7 +1496,7 @@ class EngineObject
|
|
|
1436
1496
|
* @param {Color} [color=(1,1,1,1)] - Color to apply to tile when rendered
|
|
1437
1497
|
* @param {Number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
1438
1498
|
*/
|
|
1439
|
-
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color, renderOrder=0)
|
|
1499
|
+
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=new Color, renderOrder=0)
|
|
1440
1500
|
{
|
|
1441
1501
|
// set passed in params
|
|
1442
1502
|
ASSERT(isVector2(pos) && isVector2(size), 'ensure pos and size are vec2s');
|
|
@@ -1550,15 +1610,18 @@ class EngineObject
|
|
|
1550
1610
|
|
|
1551
1611
|
// apply physics
|
|
1552
1612
|
const oldPos = this.pos.copy();
|
|
1553
|
-
this.
|
|
1554
|
-
this.
|
|
1555
|
-
|
|
1613
|
+
this.velocity.x *= this.damping;
|
|
1614
|
+
this.velocity.y *= this.damping;
|
|
1615
|
+
if (this.mass) // dont apply gravity to static objects
|
|
1616
|
+
this.velocity.y += gravity * this.gravityScale;
|
|
1617
|
+
this.pos.x += this.velocity.x;
|
|
1618
|
+
this.pos.y += this.velocity.y;
|
|
1556
1619
|
this.angle += this.angleVelocity *= this.angleDamping;
|
|
1557
1620
|
|
|
1558
1621
|
// physics sanity checks
|
|
1559
1622
|
ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
|
|
1560
1623
|
ASSERT(this.damping >= 0 && this.damping <= 1);
|
|
1561
|
-
if (!enablePhysicsSolver || !this.mass) // dont do collision for
|
|
1624
|
+
if (!enablePhysicsSolver || !this.mass) // dont do collision for static objects
|
|
1562
1625
|
return;
|
|
1563
1626
|
|
|
1564
1627
|
const wasMovingDown = this.velocity.y < 0;
|
|
@@ -1919,7 +1982,7 @@ let drawCount;
|
|
|
1919
1982
|
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
1920
1983
|
* tile(5, 8) // a tile at index 5 using a tile size of 8
|
|
1921
1984
|
* 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
|
|
1985
|
+
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
1923
1986
|
* @memberof Draw
|
|
1924
1987
|
*/
|
|
1925
1988
|
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
|
|
@@ -2264,6 +2327,24 @@ function setBlendMode(additive, useWebGL=glEnable, context)
|
|
|
2264
2327
|
}
|
|
2265
2328
|
}
|
|
2266
2329
|
|
|
2330
|
+
/** Draw text on main canvas in world space
|
|
2331
|
+
* Automatically splits new lines into rows
|
|
2332
|
+
* @param {String} text
|
|
2333
|
+
* @param {Vector2} pos
|
|
2334
|
+
* @param {Number} [size]
|
|
2335
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
2336
|
+
* @param {Number} [lineWidth]
|
|
2337
|
+
* @param {Color} [lineColor=(0,0,0,1)]
|
|
2338
|
+
* @param {CanvasTextAlign} [textAlign='center']
|
|
2339
|
+
* @param {String} [font=fontDefault]
|
|
2340
|
+
* @param {Number} [maxWidth]
|
|
2341
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=mainContext]
|
|
2342
|
+
* @memberof Draw */
|
|
2343
|
+
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth, context=mainContext)
|
|
2344
|
+
{
|
|
2345
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, maxWidth, context);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2267
2348
|
/** Draw text on overlay canvas in world space
|
|
2268
2349
|
* Automatically splits new lines into rows
|
|
2269
2350
|
* @param {String} text
|
|
@@ -2274,12 +2355,11 @@ function setBlendMode(additive, useWebGL=glEnable, context)
|
|
|
2274
2355
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
2275
2356
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
2276
2357
|
* @param {String} [font=fontDefault]
|
|
2277
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
2278
2358
|
* @param {Number} [maxWidth]
|
|
2279
2359
|
* @memberof Draw */
|
|
2280
|
-
function
|
|
2360
|
+
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth)
|
|
2281
2361
|
{
|
|
2282
|
-
|
|
2362
|
+
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, maxWidth, overlayContext);
|
|
2283
2363
|
}
|
|
2284
2364
|
|
|
2285
2365
|
/** Draw text on overlay canvas in screen space
|
|
@@ -2292,10 +2372,10 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
2292
2372
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
2293
2373
|
* @param {CanvasTextAlign} [textAlign]
|
|
2294
2374
|
* @param {String} [font=fontDefault]
|
|
2295
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
2296
2375
|
* @param {Number} [maxWidth]
|
|
2376
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
2297
2377
|
* @memberof Draw */
|
|
2298
|
-
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault,
|
|
2378
|
+
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, maxWidth=undefined, context=overlayContext)
|
|
2299
2379
|
{
|
|
2300
2380
|
context.fillStyle = color.toString();
|
|
2301
2381
|
context.lineWidth = lineWidth;
|
|
@@ -2371,7 +2451,6 @@ class FontImage
|
|
|
2371
2451
|
{
|
|
2372
2452
|
const context = this.context;
|
|
2373
2453
|
context.save();
|
|
2374
|
-
context.imageSmoothingEnabled = !canvasPixelated;
|
|
2375
2454
|
|
|
2376
2455
|
const size = this.tileSize;
|
|
2377
2456
|
const drawSize = size.add(this.paddingSize).scale(scale);
|
|
@@ -2468,7 +2547,7 @@ function keyWasReleased(key, device=0)
|
|
|
2468
2547
|
|
|
2469
2548
|
/** Clears all input
|
|
2470
2549
|
* @memberof Input */
|
|
2471
|
-
function clearInput() { inputData = [[]]; }
|
|
2550
|
+
function clearInput() { inputData = [[]]; touchGamepadButtons = []; }
|
|
2472
2551
|
|
|
2473
2552
|
/** Returns true if mouse button is down
|
|
2474
2553
|
* @function
|
|
@@ -2633,6 +2712,7 @@ function inputInit()
|
|
|
2633
2712
|
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
2634
2713
|
onwheel = (e)=> mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
2635
2714
|
oncontextmenu = (e)=> false; // prevent right click menu
|
|
2715
|
+
onblur = (e) => clearInput(); // reset input when focus is lost
|
|
2636
2716
|
|
|
2637
2717
|
// init touch input
|
|
2638
2718
|
if (isTouchDevice && touchInputEnable)
|
|
@@ -2831,10 +2911,13 @@ function touchInputInit()
|
|
|
2831
2911
|
if (touching)
|
|
2832
2912
|
{
|
|
2833
2913
|
touchGamepadTimer.set();
|
|
2834
|
-
if (paused)
|
|
2914
|
+
if (paused && !wasTouching)
|
|
2835
2915
|
{
|
|
2836
2916
|
// touch anywhere to press start when paused
|
|
2837
2917
|
touchGamepadButtons[9] = 1;
|
|
2918
|
+
|
|
2919
|
+
// call default touch handler so normal touch events still work
|
|
2920
|
+
handleTouchDefault(e);
|
|
2838
2921
|
return;
|
|
2839
2922
|
}
|
|
2840
2923
|
}
|
|
@@ -2859,7 +2942,7 @@ function touchInputInit()
|
|
|
2859
2942
|
const button = touchPos.subtract(buttonCenter).direction();
|
|
2860
2943
|
touchGamepadButtons[button] = 1;
|
|
2861
2944
|
}
|
|
2862
|
-
else if (touchPos.distance(startCenter) < touchGamepadSize)
|
|
2945
|
+
else if (touchPos.distance(startCenter) < touchGamepadSize && !wasTouching)
|
|
2863
2946
|
{
|
|
2864
2947
|
// virtual start button in center
|
|
2865
2948
|
touchGamepadButtons[9] = 1;
|
|
@@ -2947,7 +3030,7 @@ function touchGamepadRender()
|
|
|
2947
3030
|
/** Audio context used by the engine
|
|
2948
3031
|
* @type {AudioContext}
|
|
2949
3032
|
* @memberof Audio */
|
|
2950
|
-
let audioContext;
|
|
3033
|
+
let audioContext = new AudioContext;
|
|
2951
3034
|
|
|
2952
3035
|
/** Master gain node for all audio to pass through
|
|
2953
3036
|
* @type {GainNode}
|
|
@@ -2958,14 +3041,10 @@ function audioInit()
|
|
|
2958
3041
|
{
|
|
2959
3042
|
if (!soundEnable || headlessMode) return;
|
|
2960
3043
|
|
|
2961
|
-
// create audio context
|
|
2962
|
-
audioContext = new AudioContext;
|
|
2963
|
-
|
|
2964
|
-
// create and connect gain node
|
|
2965
3044
|
// (createGain is more widely spported then GainNode construtor)
|
|
2966
3045
|
audioGainNode = audioContext.createGain();
|
|
2967
3046
|
audioGainNode.connect(audioContext.destination);
|
|
2968
|
-
|
|
3047
|
+
audioGainNode.gain.value = soundVolume; // set starting value
|
|
2969
3048
|
}
|
|
2970
3049
|
|
|
2971
3050
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -3005,7 +3084,7 @@ class Sound
|
|
|
3005
3084
|
{
|
|
3006
3085
|
// generate zzfx sound now for fast playback
|
|
3007
3086
|
const defaultRandomness = .05;
|
|
3008
|
-
this.randomness = zzfxSound[1]
|
|
3087
|
+
this.randomness = zzfxSound[1] != undefined ? zzfxSound[1] : defaultRandomness;
|
|
3009
3088
|
zzfxSound[1] = 0; // generate without randomness
|
|
3010
3089
|
this.sampleChannels = [zzfxG(...zzfxSound)];
|
|
3011
3090
|
this.sampleRate = zzfxR;
|
|
@@ -3047,10 +3126,11 @@ class Sound
|
|
|
3047
3126
|
// play the sound
|
|
3048
3127
|
const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
3049
3128
|
this.gainNode = audioContext.createGain();
|
|
3050
|
-
|
|
3129
|
+
this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
|
|
3130
|
+
return this.source;
|
|
3051
3131
|
}
|
|
3052
3132
|
|
|
3053
|
-
/** Set the sound volume
|
|
3133
|
+
/** Set the sound volume of the most recently played instance of this sound
|
|
3054
3134
|
* @param {Number} [volume] - How much to scale volume by
|
|
3055
3135
|
*/
|
|
3056
3136
|
setVolume(volume=1)
|
|
@@ -3541,18 +3621,18 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
3541
3621
|
|
|
3542
3622
|
|
|
3543
3623
|
|
|
3544
|
-
/** The tile collision layer
|
|
3624
|
+
/** The tile collision layer grid, use setTileCollisionData and getTileCollisionData to access
|
|
3545
3625
|
* @type {Array}
|
|
3546
3626
|
* @memberof TileCollision */
|
|
3547
3627
|
let tileCollision = [];
|
|
3548
3628
|
|
|
3549
|
-
/** Size of the tile collision layer
|
|
3629
|
+
/** Size of the tile collision layer 2d grid
|
|
3550
3630
|
* @type {Vector2}
|
|
3551
3631
|
* @memberof TileCollision */
|
|
3552
3632
|
let tileCollisionSize = vec2();
|
|
3553
3633
|
|
|
3554
3634
|
/** Clear and initialize tile collision
|
|
3555
|
-
* @param {Vector2} size
|
|
3635
|
+
* @param {Vector2} size - width and height of tile collision 2d grid
|
|
3556
3636
|
* @memberof TileCollision */
|
|
3557
3637
|
function initTileCollision(size)
|
|
3558
3638
|
{
|
|
@@ -3562,7 +3642,7 @@ function initTileCollision(size)
|
|
|
3562
3642
|
tileCollision[i] = 0;
|
|
3563
3643
|
}
|
|
3564
3644
|
|
|
3565
|
-
/** Set tile collision data
|
|
3645
|
+
/** Set tile collision data for a given cell in the grid
|
|
3566
3646
|
* @param {Vector2} pos
|
|
3567
3647
|
* @param {Number} [data]
|
|
3568
3648
|
* @memberof TileCollision */
|
|
@@ -3571,7 +3651,7 @@ function setTileCollisionData(pos, data=0)
|
|
|
3571
3651
|
pos.arrayCheck(tileCollisionSize) && (tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
|
|
3572
3652
|
}
|
|
3573
3653
|
|
|
3574
|
-
/** Get tile collision data
|
|
3654
|
+
/** Get tile collision data for a given cell in the grid
|
|
3575
3655
|
* @param {Vector2} pos
|
|
3576
3656
|
* @return {Number}
|
|
3577
3657
|
* @memberof TileCollision */
|
|
@@ -3602,7 +3682,8 @@ function tileCollisionTest(pos, size=vec2(), object)
|
|
|
3602
3682
|
return false;
|
|
3603
3683
|
}
|
|
3604
3684
|
|
|
3605
|
-
/** Return the center of first tile hit
|
|
3685
|
+
/** Return the center of first tile hit, undefined if nothing was hit.
|
|
3686
|
+
* This does not return the exact intersection, but the center of the tile hit.
|
|
3606
3687
|
* @param {Vector2} posStart
|
|
3607
3688
|
* @param {Vector2} posEnd
|
|
3608
3689
|
* @param {EngineObject} [object]
|
|
@@ -3810,7 +3891,7 @@ class TileLayer extends EngineObject
|
|
|
3810
3891
|
}
|
|
3811
3892
|
|
|
3812
3893
|
// disable smoothing for pixel art
|
|
3813
|
-
this.context.imageSmoothingEnabled = !
|
|
3894
|
+
this.context.imageSmoothingEnabled = !tilesPixelated;
|
|
3814
3895
|
|
|
3815
3896
|
// setup gl rendering if enabled
|
|
3816
3897
|
glPreRender();
|
|
@@ -3847,8 +3928,8 @@ class TileLayer extends EngineObject
|
|
|
3847
3928
|
const d = this.getData(layerPos);
|
|
3848
3929
|
if (d.tile != undefined)
|
|
3849
3930
|
{
|
|
3850
|
-
const pos = this.pos.add(layerPos).add(vec2(.5));
|
|
3851
3931
|
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
3932
|
+
const pos = layerPos.add(vec2(.5));
|
|
3852
3933
|
const tileInfo = tile(d.tile, s, this.tileInfo.textureIndex);
|
|
3853
3934
|
drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
3854
3935
|
}
|
|
@@ -4277,7 +4358,7 @@ function medalsInit(saveName)
|
|
|
4277
4358
|
// check if medals are unlocked
|
|
4278
4359
|
medalsSaveName = saveName;
|
|
4279
4360
|
if (!debugMedals)
|
|
4280
|
-
medalsForEach(medal=> medal.unlocked =
|
|
4361
|
+
medalsForEach(medal=> medal.unlocked = !!localStorage[medal.storageKey()]);
|
|
4281
4362
|
|
|
4282
4363
|
// engine automatically renders medals
|
|
4283
4364
|
engineAddPlugin(undefined, medalsRender);
|
|
@@ -4340,14 +4421,28 @@ class Medal
|
|
|
4340
4421
|
constructor(id, name, description='', icon='🏆', src)
|
|
4341
4422
|
{
|
|
4342
4423
|
ASSERT(id >= 0 && !medals[id]);
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4424
|
+
|
|
4425
|
+
/** @property {Number} - The unique identifier of the medal */
|
|
4426
|
+
this.id = id;
|
|
4427
|
+
|
|
4428
|
+
/** @property {String} - Name of the medal */
|
|
4346
4429
|
this.name = name;
|
|
4430
|
+
|
|
4431
|
+
/** @property {String} - Description of the medal */
|
|
4347
4432
|
this.description = description;
|
|
4433
|
+
|
|
4434
|
+
/** @property {String} - Icon for the medal */
|
|
4348
4435
|
this.icon = icon;
|
|
4436
|
+
|
|
4437
|
+
/** @property {Boolean} - Is the medal unlocked? */
|
|
4438
|
+
this.unlocked = false;
|
|
4439
|
+
|
|
4440
|
+
// load the source image if provided
|
|
4349
4441
|
if (src)
|
|
4350
4442
|
(this.image = new Image).src = src;
|
|
4443
|
+
|
|
4444
|
+
// add this to list of medals
|
|
4445
|
+
medals[id] = this;
|
|
4351
4446
|
}
|
|
4352
4447
|
|
|
4353
4448
|
/** Unlocks a medal if not already unlocked */
|
|
@@ -4358,7 +4453,7 @@ class Medal
|
|
|
4358
4453
|
|
|
4359
4454
|
// save the medal
|
|
4360
4455
|
ASSERT(medalsSaveName, 'save name must be set');
|
|
4361
|
-
localStorage[this.storageKey()] = this.unlocked =
|
|
4456
|
+
localStorage[this.storageKey()] = this.unlocked = true;
|
|
4362
4457
|
medalsDisplayQueue.push(this);
|
|
4363
4458
|
}
|
|
4364
4459
|
|
|
@@ -4617,7 +4712,7 @@ function glCreateTexture(image)
|
|
|
4617
4712
|
}
|
|
4618
4713
|
|
|
4619
4714
|
// use point filtering for pixelated rendering
|
|
4620
|
-
const filter =
|
|
4715
|
+
const filter = tilesPixelated ? gl_NEAREST : gl_LINEAR;
|
|
4621
4716
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
|
|
4622
4717
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
|
|
4623
4718
|
return texture;
|
|
@@ -4766,7 +4861,7 @@ const engineName = 'LittleJS';
|
|
|
4766
4861
|
* @type {String}
|
|
4767
4862
|
* @default
|
|
4768
4863
|
* @memberof Engine */
|
|
4769
|
-
const engineVersion = '1.
|
|
4864
|
+
const engineVersion = '1.11.1';
|
|
4770
4865
|
|
|
4771
4866
|
/** Frames per second to update
|
|
4772
4867
|
* @type {Number}
|
|
@@ -4829,6 +4924,8 @@ const pluginUpdateList = [], pluginRenderList = [];
|
|
|
4829
4924
|
* @memberof Engine */
|
|
4830
4925
|
function engineAddPlugin(updateFunction, renderFunction)
|
|
4831
4926
|
{
|
|
4927
|
+
ASSERT(!pluginUpdateList.includes(updateFunction));
|
|
4928
|
+
ASSERT(!pluginRenderList.includes(renderFunction));
|
|
4832
4929
|
updateFunction && pluginUpdateList.push(updateFunction);
|
|
4833
4930
|
renderFunction && pluginRenderList.push(renderFunction);
|
|
4834
4931
|
}
|
|
@@ -4837,12 +4934,12 @@ function engineAddPlugin(updateFunction, renderFunction)
|
|
|
4837
4934
|
// Main engine functions
|
|
4838
4935
|
|
|
4839
4936
|
/** 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=[]] -
|
|
4937
|
+
* @param {Function|function():Promise} gameInit - Called once after the engine starts up
|
|
4938
|
+
* @param {Function} gameUpdate - Called every frame before objects are updated
|
|
4939
|
+
* @param {Function} gameUpdatePost - Called after physics and objects are updated, even when paused
|
|
4940
|
+
* @param {Function} gameRender - Called before objects are rendered, for drawing the background
|
|
4941
|
+
* @param {Function} gameRenderPost - Called after objects are rendered, useful for drawing UI
|
|
4942
|
+
* @param {Array} [imageSources=[]] - List of images to load
|
|
4846
4943
|
* @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default
|
|
4847
4944
|
* @memberof Engine */
|
|
4848
4945
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
@@ -4856,7 +4953,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4856
4953
|
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
4857
4954
|
|
|
4858
4955
|
// disable smoothing for pixel art
|
|
4859
|
-
|
|
4956
|
+
overlayContext.imageSmoothingEnabled =
|
|
4957
|
+
mainContext.imageSmoothingEnabled = !tilesPixelated;
|
|
4860
4958
|
|
|
4861
4959
|
// setup gl rendering if enabled
|
|
4862
4960
|
glPreRender();
|
|
@@ -4992,8 +5090,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4992
5090
|
|
|
4993
5091
|
function startEngine()
|
|
4994
5092
|
{
|
|
4995
|
-
gameInit();
|
|
4996
|
-
engineUpdate();
|
|
5093
|
+
new Promise((resolve) => resolve(gameInit())).then(engineUpdate);
|
|
4997
5094
|
}
|
|
4998
5095
|
|
|
4999
5096
|
if (headlessMode)
|
|
@@ -5008,9 +5105,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5008
5105
|
'width:100vw;height:100vh;' + // fill the window
|
|
5009
5106
|
'display:flex;' + // use flexbox
|
|
5010
5107
|
'align-items:center;' + // horizontal center
|
|
5011
|
-
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
5012
5108
|
'justify-content:center;' + // vertical center
|
|
5013
5109
|
'background:#000;' + // set background color
|
|
5110
|
+
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
5014
5111
|
'user-select:none;' + // prevent hold to select
|
|
5015
5112
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
5016
5113
|
(!touchInputEnable ? '' : // no touch css setttings
|
|
@@ -5346,4 +5443,5 @@ function drawEngineSplashScreen(t)
|
|
|
5346
5443
|
}
|
|
5347
5444
|
|
|
5348
5445
|
x.restore();
|
|
5349
|
-
}
|
|
5446
|
+
}
|
|
5447
|
+
|
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
|
|