littlejsengine 1.2.0 → 1.3.0
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 +5 -9
- package/build.bat +5 -5
- package/buildSetup.bat +7 -0
- package/engine/engine.all.js +193 -125
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.release.js +183 -110
- package/engine/engine.js +10 -33
- package/engine/engineBuild.bat +3 -3
- package/engine/engineDebug.js +10 -15
- package/engine/engineDraw.js +38 -17
- package/engine/engineFont.png +0 -0
- package/engine/engineInput.js +22 -6
- package/engine/engineMedals.js +7 -4
- package/engine/engineObject.js +17 -1
- package/engine/engineParticles.js +5 -3
- package/engine/engineSettings.js +5 -0
- package/engine/engineTileLayer.js +11 -10
- package/engine/engineUtilities.js +38 -6
- package/engine/engineWebGL.js +30 -30
- package/engine/index.d.ts +55 -21
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakout/index.html +3 -3
- package/examples/particles/index.html +366 -0
- package/examples/particles/tiles.png +0 -0
- package/examples/platformer/game.js +4 -4
- package/examples/platformer/gameEffects.js +9 -9
- package/examples/platformer/gameObjects.js +2 -2
- package/examples/platformer/index.html +6 -6
- package/examples/puzzle/game.js +2 -2
- package/examples/puzzle/index.html +2 -2
- package/examples/screenshot.jpg +0 -0
- package/examples/stress/index.html +2 -2
- package/game.js +4 -4
- package/index.html +13 -13
- package/package.json +1 -1
- package/engine/engineBuildSetup.bat +0 -7
|
@@ -344,7 +344,13 @@ class Vector2
|
|
|
344
344
|
/** Returns true if this vector is within the bounds of an array size passed in
|
|
345
345
|
* @param {Vector2} arraySize
|
|
346
346
|
* @return {Boolean} */
|
|
347
|
-
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
347
|
+
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
348
|
+
|
|
349
|
+
/** Returns this vector expressed as a string
|
|
350
|
+
* @param {float} digits - precision to display
|
|
351
|
+
* @return {String} */
|
|
352
|
+
toString(digits=3)
|
|
353
|
+
{ return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }
|
|
348
354
|
}
|
|
349
355
|
|
|
350
356
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -415,7 +421,7 @@ class Color
|
|
|
415
421
|
* @return {Color} */
|
|
416
422
|
lerp(c, p) { return this.add(c.subtract(this).scale(clamp(p))); }
|
|
417
423
|
|
|
418
|
-
/** Sets this color given a hue, saturation, lightness
|
|
424
|
+
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
419
425
|
* @param {Number} [hue=0]
|
|
420
426
|
* @param {Number} [saturation=0]
|
|
421
427
|
* @param {Number} [lightness=1]
|
|
@@ -451,21 +457,43 @@ class Color
|
|
|
451
457
|
).clamp();
|
|
452
458
|
}
|
|
453
459
|
|
|
454
|
-
/** Returns this color expressed as an
|
|
460
|
+
/** Returns this color expressed as an CSS color value
|
|
455
461
|
* @return {String} */
|
|
456
|
-
|
|
462
|
+
toString()
|
|
457
463
|
{
|
|
458
464
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
459
465
|
return `rgb(${this.r*255|0},${this.g*255|0},${this.b*255|0},${this.a})`;
|
|
460
466
|
}
|
|
461
467
|
|
|
462
|
-
/** Returns this color expressed as 32 bit integer value
|
|
468
|
+
/** Returns this color expressed as 32 bit integer RGBA value
|
|
463
469
|
* @return {Number} */
|
|
464
470
|
rgbaInt()
|
|
465
471
|
{
|
|
466
472
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
467
473
|
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
468
474
|
}
|
|
475
|
+
|
|
476
|
+
/** Returns this color expressed as a hex code
|
|
477
|
+
* @return {String} */
|
|
478
|
+
hex()
|
|
479
|
+
{
|
|
480
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
481
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Set this color from a hex code
|
|
485
|
+
* @param {String} hex - html hex code
|
|
486
|
+
* @return {Color} */
|
|
487
|
+
setHex(hex)
|
|
488
|
+
{
|
|
489
|
+
const fromHex = (a)=> parseInt(hex.slice(a,a+2), 16)/255;
|
|
490
|
+
this.r = fromHex(1);
|
|
491
|
+
this.g = fromHex(3),
|
|
492
|
+
this.b = fromHex(5);
|
|
493
|
+
this.a = 1;
|
|
494
|
+
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
495
|
+
return this;
|
|
496
|
+
}
|
|
469
497
|
}
|
|
470
498
|
|
|
471
499
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -502,7 +530,7 @@ class Timer
|
|
|
502
530
|
|
|
503
531
|
/** Returns true if set and elapsed
|
|
504
532
|
* @return {Boolean} */
|
|
505
|
-
elapsed() { return time >
|
|
533
|
+
elapsed() { return time > this.time; }
|
|
506
534
|
|
|
507
535
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
508
536
|
* @return {Number} */
|
|
@@ -511,6 +539,10 @@ class Timer
|
|
|
511
539
|
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
512
540
|
* @return {Number} */
|
|
513
541
|
getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
|
|
542
|
+
|
|
543
|
+
/** Returns this timer expressed as a string
|
|
544
|
+
* @return {String} */
|
|
545
|
+
toString() { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }
|
|
514
546
|
}
|
|
515
547
|
/**
|
|
516
548
|
* LittleJS Engine Settings
|
|
@@ -659,6 +691,11 @@ let inputWASDEmulateDirection = 1;
|
|
|
659
691
|
* @memberof Settings */
|
|
660
692
|
let touchGamepadEnable = 0;
|
|
661
693
|
|
|
694
|
+
/** True if touch gamepad should be analog stick or false to use if 8 way dpad
|
|
695
|
+
* @default
|
|
696
|
+
* @memberof Settings */
|
|
697
|
+
let touchGamepadAnalog = 1;
|
|
698
|
+
|
|
662
699
|
/** Size of virutal gamepad for touch devices in pixels
|
|
663
700
|
* @default
|
|
664
701
|
* @memberof Settings */
|
|
@@ -748,7 +785,7 @@ let medalDisplayIconSize = 50;
|
|
|
748
785
|
const engineName = 'LittleJS';
|
|
749
786
|
|
|
750
787
|
/** Version of engine */
|
|
751
|
-
const engineVersion = '1.
|
|
788
|
+
const engineVersion = '1.3.0';
|
|
752
789
|
|
|
753
790
|
/** Frames per second to update objects
|
|
754
791
|
* @default */
|
|
@@ -785,7 +822,7 @@ let averageFPS, drawCount;
|
|
|
785
822
|
// css text used for elements created by engine
|
|
786
823
|
const styleBody = 'margin:0;overflow:hidden;background:#000' +
|
|
787
824
|
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
788
|
-
const styleCanvas = 'position:absolute';
|
|
825
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
789
826
|
|
|
790
827
|
///////////////////////////////////////////////////////////////////////////////
|
|
791
828
|
|
|
@@ -829,8 +866,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
829
866
|
// main update loop
|
|
830
867
|
const engineUpdate = (frameTimeMS=0)=>
|
|
831
868
|
{
|
|
832
|
-
requestAnimationFrame(engineUpdate);
|
|
833
|
-
|
|
834
869
|
// update time keeping
|
|
835
870
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
836
871
|
frameTimeLastMS = frameTimeMS;
|
|
@@ -886,24 +921,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
886
921
|
|
|
887
922
|
// fit to window by adding space on top or bottom if necessary
|
|
888
923
|
const aspect = innerWidth / innerHeight;
|
|
889
|
-
const fixedAspect =
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
mainCanvas.style.height = overlayCanvas.style.height =
|
|
894
|
-
mainCanvas.style.top = overlayCanvas.style.top =
|
|
895
|
-
mainCanvas.style.left = overlayCanvas.style.left = '';
|
|
896
|
-
|
|
897
|
-
// letterbox center canvas
|
|
898
|
-
if (aspect < fixedAspect)
|
|
899
|
-
{
|
|
900
|
-
mainCanvas.style.width = overlayCanvas.style.width = innerWidth;
|
|
901
|
-
mainCanvas.style.top = overlayCanvas.style.top = (innerHeight - innerWidth/fixedAspect)/2;
|
|
902
|
-
}
|
|
903
|
-
else
|
|
924
|
+
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
925
|
+
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
926
|
+
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
927
|
+
if (glCanvas)
|
|
904
928
|
{
|
|
905
|
-
|
|
906
|
-
|
|
929
|
+
glCanvas.style.width = mainCanvas.style.width;
|
|
930
|
+
glCanvas.style.height = mainCanvas.style.height;
|
|
907
931
|
}
|
|
908
932
|
}
|
|
909
933
|
else
|
|
@@ -911,18 +935,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
911
935
|
// clear and set size to same as window
|
|
912
936
|
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
913
937
|
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
914
|
-
|
|
915
|
-
// center canvas
|
|
916
|
-
overlayCanvas.style.top = mainCanvas.style.top = (innerHeight - mainCanvas.height)/2;
|
|
917
|
-
overlayCanvas.style.left = mainCanvas.style.left = (innerWidth - mainCanvas.width)/2;
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
if (glEnable)
|
|
921
|
-
{
|
|
922
|
-
glCanvas.style.width = mainCanvas.style.width;
|
|
923
|
-
glCanvas.style.height = mainCanvas.style.height;
|
|
924
|
-
glCanvas.style.top = mainCanvas.style.top;
|
|
925
|
-
glCanvas.style.left = mainCanvas.style.left;
|
|
926
938
|
}
|
|
927
939
|
|
|
928
940
|
// render sort then render while removing destroyed objects
|
|
@@ -952,6 +964,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
952
964
|
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
953
965
|
drawCount = 0;
|
|
954
966
|
}
|
|
967
|
+
|
|
968
|
+
requestAnimationFrame(engineUpdate);
|
|
955
969
|
}
|
|
956
970
|
|
|
957
971
|
// set tile image source to load the image and start the engine
|
|
@@ -1069,7 +1083,7 @@ class EngineObject
|
|
|
1069
1083
|
* @param {Vector2} [size=objectDefaultSize] - World space size of the object
|
|
1070
1084
|
* @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
|
|
1071
1085
|
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
|
|
1072
|
-
* @param {Number} [angle=0] - Angle
|
|
1086
|
+
* @param {Number} [angle=0] - Angle the object is rotated by
|
|
1073
1087
|
* @param {Color} [color] - Color to apply to tile when rendered
|
|
1074
1088
|
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
1075
1089
|
*/
|
|
@@ -1385,6 +1399,22 @@ class EngineObject
|
|
|
1385
1399
|
this.isSolid = isSolid;
|
|
1386
1400
|
this.collideTiles = collideTiles;
|
|
1387
1401
|
}
|
|
1402
|
+
|
|
1403
|
+
toString()
|
|
1404
|
+
{
|
|
1405
|
+
let text = 'type = ' + this.constructor.name;
|
|
1406
|
+
if (this.pos.x || this.pos.y)
|
|
1407
|
+
text += '\npos = ' + this.pos;
|
|
1408
|
+
if (this.velocity.x || this.velocity.y)
|
|
1409
|
+
text += '\nvelocity = ' + this.velocity;
|
|
1410
|
+
if (this.size.x || this.size.y)
|
|
1411
|
+
text += '\nsize = ' + this.size;
|
|
1412
|
+
if (this.angle)
|
|
1413
|
+
text += '\nangle = ' + this.angle.toFixed(3);
|
|
1414
|
+
if (this.color)
|
|
1415
|
+
text += '\ncolor = ' + this.color;
|
|
1416
|
+
return text;
|
|
1417
|
+
}
|
|
1388
1418
|
}
|
|
1389
1419
|
/**
|
|
1390
1420
|
* LittleJS Drawing System
|
|
@@ -1498,7 +1528,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
1498
1528
|
if (tileIndex < 0)
|
|
1499
1529
|
{
|
|
1500
1530
|
// if negative tile index, force untextured
|
|
1501
|
-
context.fillStyle = color
|
|
1531
|
+
context.fillStyle = color;
|
|
1502
1532
|
context.fillRect(-.5, -.5, 1, 1);
|
|
1503
1533
|
}
|
|
1504
1534
|
else
|
|
@@ -1603,7 +1633,8 @@ function setBlendMode(additive, useWebGL=glEnable)
|
|
|
1603
1633
|
mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
1604
1634
|
}
|
|
1605
1635
|
|
|
1606
|
-
/** Draw text on overlay canvas in
|
|
1636
|
+
/** Draw text on overlay canvas in screen space
|
|
1637
|
+
* Automatically splits new lines into rows
|
|
1607
1638
|
* @param {String} text
|
|
1608
1639
|
* @param {Vector2} pos
|
|
1609
1640
|
* @param {Number} [size=1]
|
|
@@ -1612,20 +1643,37 @@ function setBlendMode(additive, useWebGL=glEnable)
|
|
|
1612
1643
|
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1613
1644
|
* @param {String} [textAlign='center']
|
|
1614
1645
|
* @memberof Draw */
|
|
1615
|
-
function
|
|
1646
|
+
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
|
|
1616
1647
|
{
|
|
1617
|
-
|
|
1618
|
-
overlayContext.
|
|
1648
|
+
overlayContext.fillStyle = color;
|
|
1649
|
+
overlayContext.lineWidth = lineWidth *= cameraScale;
|
|
1650
|
+
overlayContext.strokeStyle = lineColor;
|
|
1619
1651
|
overlayContext.textAlign = textAlign;
|
|
1652
|
+
overlayContext.font = size + 'px '+ font;
|
|
1620
1653
|
overlayContext.textBaseline = 'middle';
|
|
1621
|
-
|
|
1654
|
+
|
|
1655
|
+
pos = pos.copy();
|
|
1656
|
+
text.split('\n').forEach(line=>
|
|
1622
1657
|
{
|
|
1623
|
-
overlayContext.
|
|
1624
|
-
overlayContext.
|
|
1625
|
-
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1658
|
+
lineWidth && overlayContext.strokeText(line, pos.x, pos.y);
|
|
1659
|
+
overlayContext.fillText(line, pos.x, pos.y);
|
|
1660
|
+
pos.y += size;
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
/** Draw text on overlay canvas in world space
|
|
1665
|
+
* Automatically splits new lines into rows
|
|
1666
|
+
* @param {String} text
|
|
1667
|
+
* @param {Vector2} pos
|
|
1668
|
+
* @param {Number} [size=1]
|
|
1669
|
+
* @param {Color} [color=new Color(1,1,1)]
|
|
1670
|
+
* @param {Number} [lineWidth=0]
|
|
1671
|
+
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1672
|
+
* @param {String} [textAlign='center']
|
|
1673
|
+
* @memberof Draw */
|
|
1674
|
+
function drawText(text, pos, size=1, color, lineWidth, lineColor, textAlign, font)
|
|
1675
|
+
{
|
|
1676
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth, lineColor, textAlign, font);
|
|
1629
1677
|
}
|
|
1630
1678
|
|
|
1631
1679
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1642,6 +1690,9 @@ function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new
|
|
|
1642
1690
|
* // draw text
|
|
1643
1691
|
* font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
|
|
1644
1692
|
*/
|
|
1693
|
+
|
|
1694
|
+
let engineFontImage;
|
|
1695
|
+
|
|
1645
1696
|
class FontImage
|
|
1646
1697
|
{
|
|
1647
1698
|
/** Create an image font
|
|
@@ -1653,14 +1704,14 @@ class FontImage
|
|
|
1653
1704
|
*/
|
|
1654
1705
|
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
|
|
1655
1706
|
{
|
|
1656
|
-
if (!image)
|
|
1707
|
+
if (!image && !engineFontImage)
|
|
1657
1708
|
{
|
|
1658
|
-
//
|
|
1659
|
-
|
|
1660
|
-
|
|
1709
|
+
// load default font image
|
|
1710
|
+
engineFontImage = new Image();
|
|
1711
|
+
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
1661
1712
|
}
|
|
1662
1713
|
|
|
1663
|
-
this.image = image;
|
|
1714
|
+
this.image = image || engineFontImage;
|
|
1664
1715
|
this.tileSize = tileSize;
|
|
1665
1716
|
this.paddingSize = paddingSize;
|
|
1666
1717
|
this.startTileIndex = startTileIndex;
|
|
@@ -2027,7 +2078,7 @@ if (isTouchDevice)
|
|
|
2027
2078
|
// touch gamepad, virtual on screen gamepad emulator for touch devices
|
|
2028
2079
|
|
|
2029
2080
|
// touch input internal variables
|
|
2030
|
-
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2()
|
|
2081
|
+
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2();
|
|
2031
2082
|
|
|
2032
2083
|
// create the touch gamepad, called automatically by the engine
|
|
2033
2084
|
function touchGamepadCreate()
|
|
@@ -2074,7 +2125,7 @@ function touchGamepadCreate()
|
|
|
2074
2125
|
{
|
|
2075
2126
|
// virtual analog stick
|
|
2076
2127
|
if (touchGamepadAnalog)
|
|
2077
|
-
touchGamepadStick = touchPos.subtract(stickCenter).
|
|
2128
|
+
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
2078
2129
|
else
|
|
2079
2130
|
{
|
|
2080
2131
|
// 8 way dpad
|
|
@@ -2115,12 +2166,28 @@ function touchGamepadRender()
|
|
|
2115
2166
|
overlayContext.lineWidth = 3;
|
|
2116
2167
|
|
|
2117
2168
|
// draw left analog stick
|
|
2118
|
-
overlayContext.fillStyle =
|
|
2169
|
+
overlayContext.fillStyle = touchGamepadStick.lengthSquared() > 0 ? '#fff' : '#000';
|
|
2119
2170
|
overlayContext.beginPath();
|
|
2120
|
-
overlayContext.arc(touchGamepadSize, mainCanvasSize.y-touchGamepadSize, touchGamepadSize/2, 0,9);
|
|
2121
|
-
overlayContext.fill();
|
|
2122
|
-
overlayContext.stroke();
|
|
2123
2171
|
|
|
2172
|
+
const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2173
|
+
if (touchGamepadAnalog)
|
|
2174
|
+
{
|
|
2175
|
+
overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
|
|
2176
|
+
overlayContext.fill();
|
|
2177
|
+
overlayContext.stroke();
|
|
2178
|
+
}
|
|
2179
|
+
else // draw cross shaped gamepad
|
|
2180
|
+
{
|
|
2181
|
+
for(let i=10; i--;)
|
|
2182
|
+
{
|
|
2183
|
+
const angle = i*PI/4;
|
|
2184
|
+
overlayContext.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
|
|
2185
|
+
i%2 && overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize*.33, angle, angle);
|
|
2186
|
+
i==1 && overlayContext.fill();
|
|
2187
|
+
}
|
|
2188
|
+
overlayContext.stroke();
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2124
2191
|
// draw right face buttons
|
|
2125
2192
|
const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2126
2193
|
for (let i=4; i--;)
|
|
@@ -2761,14 +2828,14 @@ class TileLayerData
|
|
|
2761
2828
|
*/
|
|
2762
2829
|
class TileLayer extends EngineObject
|
|
2763
2830
|
{
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2831
|
+
/** Create a tile layer object
|
|
2832
|
+
* @param {Vector2} [position=new Vector2()] - World space position
|
|
2833
|
+
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
2834
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
|
|
2835
|
+
* @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
|
|
2836
|
+
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
2837
|
+
*/
|
|
2838
|
+
constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
|
|
2772
2839
|
{
|
|
2773
2840
|
super(pos, size, -1, tileSize, 0, undefined, renderOrder);
|
|
2774
2841
|
|
|
@@ -2928,7 +2995,7 @@ class TileLayer extends EngineObject
|
|
|
2928
2995
|
if (tileIndex < 0)
|
|
2929
2996
|
{
|
|
2930
2997
|
// untextured
|
|
2931
|
-
context.fillStyle = color
|
|
2998
|
+
context.fillStyle = color;
|
|
2932
2999
|
context.fillRect(-.5, -.5, 1, 1);
|
|
2933
3000
|
}
|
|
2934
3001
|
else
|
|
@@ -2947,7 +3014,8 @@ class TileLayer extends EngineObject
|
|
|
2947
3014
|
* @param {Vector2} [size=new Vector2(1,1)]
|
|
2948
3015
|
* @param {Color} [color=new Color(1,1,1)]
|
|
2949
3016
|
* @param {Number} [angle=0] */
|
|
2950
|
-
drawRect(pos, size, color, angle)
|
|
3017
|
+
drawRect(pos, size, color, angle)
|
|
3018
|
+
{ this.drawTile(pos, size, -1, 0, color, angle); }
|
|
2951
3019
|
}
|
|
2952
3020
|
/*
|
|
2953
3021
|
LittleJS Particle System
|
|
@@ -2966,9 +3034,9 @@ class TileLayer extends EngineObject
|
|
|
2966
3034
|
* let pos = vec2(2,3);
|
|
2967
3035
|
* let particleEmiter = new ParticleEmitter
|
|
2968
3036
|
* (
|
|
2969
|
-
* pos, 1, 0, 500, PI, // pos, emitSize, emitTime, emitRate, emiteCone
|
|
3037
|
+
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
|
|
2970
3038
|
* 0, vec2(16), // tileIndex, tileSize
|
|
2971
|
-
* new Color,
|
|
3039
|
+
* new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
|
|
2972
3040
|
* new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
|
|
2973
3041
|
* 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
2974
3042
|
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
|
|
@@ -2979,6 +3047,7 @@ class ParticleEmitter extends EngineObject
|
|
|
2979
3047
|
{
|
|
2980
3048
|
/** Create a particle system with the given settings
|
|
2981
3049
|
* @param {Vector2} position - World space position of the emitter
|
|
3050
|
+
* @param {Number} [angle=0] - Angle to emit the particles
|
|
2982
3051
|
* @param {Number} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
2983
3052
|
* @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
|
|
2984
3053
|
* @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
|
|
@@ -3008,6 +3077,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3008
3077
|
constructor
|
|
3009
3078
|
(
|
|
3010
3079
|
pos,
|
|
3080
|
+
angle,
|
|
3011
3081
|
emitSize = 0,
|
|
3012
3082
|
emitTime = 0,
|
|
3013
3083
|
emitRate = 100,
|
|
@@ -3035,7 +3105,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3035
3105
|
renderOrder = additive ? 1e9 : 0
|
|
3036
3106
|
)
|
|
3037
3107
|
{
|
|
3038
|
-
super(pos, new Vector2, tileIndex, tileSize,
|
|
3108
|
+
super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
|
|
3039
3109
|
|
|
3040
3110
|
// emitter settings
|
|
3041
3111
|
/** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
@@ -3268,7 +3338,7 @@ function medalsInit(saveName)
|
|
|
3268
3338
|
{
|
|
3269
3339
|
// check if medals are unlocked
|
|
3270
3340
|
medalsSaveName = saveName;
|
|
3271
|
-
debugMedals || medals.forEach(medal=>
|
|
3341
|
+
debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
|
|
3272
3342
|
}
|
|
3273
3343
|
|
|
3274
3344
|
/**
|
|
@@ -3457,6 +3527,9 @@ class Newgrounds
|
|
|
3457
3527
|
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
3458
3528
|
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
3459
3529
|
debugMedals && console.log(this.scoreboards);
|
|
3530
|
+
|
|
3531
|
+
const keepAliveMS = 5 * 60 * 1e3;
|
|
3532
|
+
setInterval(()=>this.call('Gateway.ping', 0, 1), keepAliveMS);
|
|
3460
3533
|
}
|
|
3461
3534
|
|
|
3462
3535
|
/** Send message to unlock a medal by id
|
|
@@ -3468,9 +3541,6 @@ class Newgrounds
|
|
|
3468
3541
|
* @param {Number} value - The score value */
|
|
3469
3542
|
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, 1); }
|
|
3470
3543
|
|
|
3471
|
-
/** Send message to log a view */
|
|
3472
|
-
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
3473
|
-
|
|
3474
3544
|
/** Get scores from a scoreboard
|
|
3475
3545
|
* @param {Number} id - The scoreboard id
|
|
3476
3546
|
* @param {String} [user=0] - A user's id or name
|
|
@@ -3482,6 +3552,9 @@ class Newgrounds
|
|
|
3482
3552
|
getScores(id, user=0, social=0, skip=0, limit=10)
|
|
3483
3553
|
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
3484
3554
|
|
|
3555
|
+
/** Send message to log a view */
|
|
3556
|
+
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
3557
|
+
|
|
3485
3558
|
/** Send a message to call a component of the Newgrounds API
|
|
3486
3559
|
* @param {String} component - Name of the component
|
|
3487
3560
|
* @param {Object} [parameters=0] - Parameters to use for call
|
|
@@ -3578,24 +3651,24 @@ function glInit()
|
|
|
3578
3651
|
|
|
3579
3652
|
// setup vertex and fragment shaders
|
|
3580
3653
|
glShader = glCreateProgram(
|
|
3581
|
-
'precision highp float;'+
|
|
3582
|
-
'uniform mat4 m;'+
|
|
3583
|
-
'attribute vec2 p,t;'+
|
|
3584
|
-
'attribute vec4 c,a;'+
|
|
3585
|
-
'varying vec2 v;'+
|
|
3586
|
-
'varying vec4 d,e;'+
|
|
3587
|
-
'void main(){'+
|
|
3588
|
-
'gl_Position=m*vec4(p,1,1);'
|
|
3589
|
-
'v=t;d=c;e=a;'+
|
|
3590
|
-
'}'
|
|
3654
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
3655
|
+
'uniform mat4 m;'+ // transform matrix
|
|
3656
|
+
'attribute vec2 p,t;'+ // position, uv
|
|
3657
|
+
'attribute vec4 c,a;'+ // color, additiveColor
|
|
3658
|
+
'varying vec2 v;'+ // return uv
|
|
3659
|
+
'varying vec4 d,e;'+ // return color, additiveColor
|
|
3660
|
+
'void main(){'+ // shader entry point
|
|
3661
|
+
'gl_Position=m*vec4(p,1,1);'+ // transform position
|
|
3662
|
+
'v=t;d=c;e=a;'+ // pass stuff to fragment shader
|
|
3663
|
+
'}' // end of shader
|
|
3591
3664
|
,
|
|
3592
|
-
'precision highp float;'+
|
|
3593
|
-
'varying vec2 v;'+
|
|
3594
|
-
'varying vec4 d,e;'+
|
|
3595
|
-
'uniform sampler2D
|
|
3596
|
-
'void main(){'+
|
|
3597
|
-
'gl_FragColor=texture2D(
|
|
3598
|
-
'}'
|
|
3665
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
3666
|
+
'varying vec2 v;'+ // uv
|
|
3667
|
+
'varying vec4 d,e;'+ // color, additiveColor
|
|
3668
|
+
'uniform sampler2D s;'+ // texture
|
|
3669
|
+
'void main(){'+ // shader entry point
|
|
3670
|
+
'gl_FragColor=texture2D(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
3671
|
+
'}' // end of shader
|
|
3599
3672
|
);
|
|
3600
3673
|
|
|
3601
3674
|
// init buffers
|
|
@@ -3807,44 +3880,44 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
3807
3880
|
|
|
3808
3881
|
// prepare to create the verts from size and angle
|
|
3809
3882
|
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
3810
|
-
const
|
|
3883
|
+
const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
|
|
3811
3884
|
|
|
3812
3885
|
// setup 2 triangles to form a quad
|
|
3813
3886
|
let offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT;
|
|
3814
3887
|
|
|
3815
3888
|
// vertex 0
|
|
3816
|
-
glPositionData[offset++] = x -
|
|
3817
|
-
glPositionData[offset++] = y -
|
|
3889
|
+
glPositionData[offset++] = x - cx - sy;
|
|
3890
|
+
glPositionData[offset++] = y - cy + sx;
|
|
3818
3891
|
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3819
3892
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3820
3893
|
|
|
3821
3894
|
// vertex 1
|
|
3822
|
-
glPositionData[offset++] = x +
|
|
3823
|
-
glPositionData[offset++] = y +
|
|
3895
|
+
glPositionData[offset++] = x + cx + sy;
|
|
3896
|
+
glPositionData[offset++] = y + cy - sx;
|
|
3824
3897
|
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3825
3898
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3826
3899
|
|
|
3827
3900
|
// vertex 2
|
|
3828
|
-
glPositionData[offset++] = x -
|
|
3829
|
-
glPositionData[offset++] = y +
|
|
3901
|
+
glPositionData[offset++] = x - cx + sy;
|
|
3902
|
+
glPositionData[offset++] = y + cy + sx;
|
|
3830
3903
|
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv0Y;
|
|
3831
3904
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3832
3905
|
|
|
3833
3906
|
// vertex 0
|
|
3834
|
-
glPositionData[offset++] = x -
|
|
3835
|
-
glPositionData[offset++] = y -
|
|
3907
|
+
glPositionData[offset++] = x - cx - sy;
|
|
3908
|
+
glPositionData[offset++] = y - cy + sx;
|
|
3836
3909
|
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3837
3910
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3838
3911
|
|
|
3839
3912
|
// vertex 3
|
|
3840
|
-
glPositionData[offset++] = x +
|
|
3841
|
-
glPositionData[offset++] = y -
|
|
3913
|
+
glPositionData[offset++] = x + cx - sy;
|
|
3914
|
+
glPositionData[offset++] = y - cy - sx;
|
|
3842
3915
|
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv1Y;
|
|
3843
3916
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3844
3917
|
|
|
3845
3918
|
// vertex 1
|
|
3846
|
-
glPositionData[offset++] = x +
|
|
3847
|
-
glPositionData[offset++] = y +
|
|
3919
|
+
glPositionData[offset++] = x + cx + sy;
|
|
3920
|
+
glPositionData[offset++] = y + cy - sx;
|
|
3848
3921
|
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3849
3922
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3850
3923
|
}
|
package/engine/engine.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
const engineName = 'LittleJS';
|
|
23
23
|
|
|
24
24
|
/** Version of engine */
|
|
25
|
-
const engineVersion = '1.
|
|
25
|
+
const engineVersion = '1.3.0';
|
|
26
26
|
|
|
27
27
|
/** Frames per second to update objects
|
|
28
28
|
* @default */
|
|
@@ -59,7 +59,7 @@ let averageFPS, drawCount;
|
|
|
59
59
|
// css text used for elements created by engine
|
|
60
60
|
const styleBody = 'margin:0;overflow:hidden;background:#000' +
|
|
61
61
|
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
62
|
-
const styleCanvas = 'position:absolute';
|
|
62
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
63
63
|
|
|
64
64
|
///////////////////////////////////////////////////////////////////////////////
|
|
65
65
|
|
|
@@ -103,8 +103,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
103
103
|
// main update loop
|
|
104
104
|
const engineUpdate = (frameTimeMS=0)=>
|
|
105
105
|
{
|
|
106
|
-
requestAnimationFrame(engineUpdate);
|
|
107
|
-
|
|
108
106
|
// update time keeping
|
|
109
107
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
110
108
|
frameTimeLastMS = frameTimeMS;
|
|
@@ -160,24 +158,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
160
158
|
|
|
161
159
|
// fit to window by adding space on top or bottom if necessary
|
|
162
160
|
const aspect = innerWidth / innerHeight;
|
|
163
|
-
const fixedAspect =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
mainCanvas.style.height = overlayCanvas.style.height =
|
|
168
|
-
mainCanvas.style.top = overlayCanvas.style.top =
|
|
169
|
-
mainCanvas.style.left = overlayCanvas.style.left = '';
|
|
170
|
-
|
|
171
|
-
// letterbox center canvas
|
|
172
|
-
if (aspect < fixedAspect)
|
|
173
|
-
{
|
|
174
|
-
mainCanvas.style.width = overlayCanvas.style.width = innerWidth;
|
|
175
|
-
mainCanvas.style.top = overlayCanvas.style.top = (innerHeight - innerWidth/fixedAspect)/2;
|
|
176
|
-
}
|
|
177
|
-
else
|
|
161
|
+
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
162
|
+
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
163
|
+
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
164
|
+
if (glCanvas)
|
|
178
165
|
{
|
|
179
|
-
|
|
180
|
-
|
|
166
|
+
glCanvas.style.width = mainCanvas.style.width;
|
|
167
|
+
glCanvas.style.height = mainCanvas.style.height;
|
|
181
168
|
}
|
|
182
169
|
}
|
|
183
170
|
else
|
|
@@ -185,18 +172,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
185
172
|
// clear and set size to same as window
|
|
186
173
|
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
187
174
|
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
188
|
-
|
|
189
|
-
// center canvas
|
|
190
|
-
overlayCanvas.style.top = mainCanvas.style.top = (innerHeight - mainCanvas.height)/2;
|
|
191
|
-
overlayCanvas.style.left = mainCanvas.style.left = (innerWidth - mainCanvas.width)/2;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (glEnable)
|
|
195
|
-
{
|
|
196
|
-
glCanvas.style.width = mainCanvas.style.width;
|
|
197
|
-
glCanvas.style.height = mainCanvas.style.height;
|
|
198
|
-
glCanvas.style.top = mainCanvas.style.top;
|
|
199
|
-
glCanvas.style.left = mainCanvas.style.left;
|
|
200
175
|
}
|
|
201
176
|
|
|
202
177
|
// render sort then render while removing destroyed objects
|
|
@@ -226,6 +201,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
226
201
|
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
227
202
|
drawCount = 0;
|
|
228
203
|
}
|
|
204
|
+
|
|
205
|
+
requestAnimationFrame(engineUpdate);
|
|
229
206
|
}
|
|
230
207
|
|
|
231
208
|
// set tile image source to load the image and start the engine
|