littlejsengine 1.1.26 → 1.2.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 +3 -1
- package/engine/engine.all.js +248 -324
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.release.js +223 -105
- package/engine/engine.js +34 -11
- package/engine/engineAudio.js +2 -2
- package/engine/engineBuild.bat +9 -1
- package/engine/engineBuildSetup.bat +2 -1
- package/engine/engineDebug.js +26 -220
- package/engine/engineDraw.js +98 -9
- package/engine/engineInput.js +13 -6
- package/engine/engineObject.js +13 -10
- package/engine/engineTileLayer.js +11 -9
- package/engine/engineUtilities.js +12 -12
- package/engine/engineWebGL.js +40 -46
- package/engine/index.d.ts +1612 -0
- package/examples/breakout/game.js +9 -7
- package/examples/breakout/gameObjects.js +1 -1
- package/examples/breakout/index.html +3 -3
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameEffects.js +1 -1
- package/examples/platformer/gameLevel.js +3 -3
- package/examples/platformer/gameObjects.js +1 -1
- package/examples/platformer/gamePlayer.js +11 -6
- package/examples/platformer/index.html +6 -6
- package/examples/platformer/tiles.png +0 -0
- package/examples/puzzle/game.js +1 -1
- package/examples/puzzle/index.html +2 -2
- package/examples/stress/index.html +3 -3
- package/game.js +0 -3
- package/index.html +13 -13
- package/package.json +1 -1
|
@@ -82,27 +82,27 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
|
|
|
82
82
|
|
|
83
83
|
/** Clamps the value beween max and min
|
|
84
84
|
* @param {Number} value
|
|
85
|
-
* @param {Number} [max=1]
|
|
86
85
|
* @param {Number} [min=0]
|
|
86
|
+
* @param {Number} [max=1]
|
|
87
87
|
* @return {Number}
|
|
88
88
|
* @memberof Utilities */
|
|
89
|
-
const clamp = (v,
|
|
89
|
+
const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
|
|
90
90
|
|
|
91
91
|
/** Returns what percentage the value is between max and min
|
|
92
92
|
* @param {Number} value
|
|
93
|
-
* @param {Number} [max=1]
|
|
94
93
|
* @param {Number} [min=0]
|
|
94
|
+
* @param {Number} [max=1]
|
|
95
95
|
* @return {Number}
|
|
96
96
|
* @memberof Utilities */
|
|
97
|
-
const percent = (v,
|
|
97
|
+
const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
|
|
98
98
|
|
|
99
99
|
/** Linearly interpolates the percent value between max and min
|
|
100
100
|
* @param {Number} percent
|
|
101
|
-
* @param {Number} [max=1]
|
|
102
101
|
* @param {Number} [min=0]
|
|
102
|
+
* @param {Number} [max=1]
|
|
103
103
|
* @return {Number}
|
|
104
104
|
* @memberof Utilities */
|
|
105
|
-
const lerp = (p,
|
|
105
|
+
const lerp = (p, min=0, max=1)=> min + clamp(p) * (max-min);
|
|
106
106
|
|
|
107
107
|
/** Applies smoothstep function to the percentage value
|
|
108
108
|
* @param {Number} value
|
|
@@ -117,11 +117,11 @@ const smoothStep = (p)=> p * p * (3 - 2 * p);
|
|
|
117
117
|
const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
|
|
118
118
|
|
|
119
119
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
120
|
-
* @param {Vector2} pointA
|
|
121
|
-
* @param {Vector2} sizeA
|
|
122
|
-
* @param {Vector2} pointB
|
|
123
|
-
* @param {Vector2} sizeB
|
|
124
|
-
* @return {Boolean}
|
|
120
|
+
* @param {Vector2} pointA - Center of box A
|
|
121
|
+
* @param {Vector2} sizeA - Size of box A
|
|
122
|
+
* @param {Vector2} pointB - Center of box B
|
|
123
|
+
* @param {Vector2} [sizeB] - Size of box B
|
|
124
|
+
* @return {Boolean} - True if overlapping
|
|
125
125
|
* @memberof Utilities */
|
|
126
126
|
const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x & abs(pA.y - pB.y)*2 < sA.y + sB.y;
|
|
127
127
|
|
|
@@ -510,7 +510,7 @@ class Timer
|
|
|
510
510
|
|
|
511
511
|
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
512
512
|
* @return {Number} */
|
|
513
|
-
getPercent() { return this.isSet()? percent(this.time - time,
|
|
513
|
+
getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
|
|
514
514
|
}
|
|
515
515
|
/**
|
|
516
516
|
* LittleJS Engine Settings
|
|
@@ -748,7 +748,7 @@ let medalDisplayIconSize = 50;
|
|
|
748
748
|
const engineName = 'LittleJS';
|
|
749
749
|
|
|
750
750
|
/** Version of engine */
|
|
751
|
-
const engineVersion = '1.
|
|
751
|
+
const engineVersion = '1.2.0';
|
|
752
752
|
|
|
753
753
|
/** Frames per second to update objects
|
|
754
754
|
* @default */
|
|
@@ -785,7 +785,7 @@ let averageFPS, drawCount;
|
|
|
785
785
|
// css text used for elements created by engine
|
|
786
786
|
const styleBody = 'margin:0;overflow:hidden;background:#000' +
|
|
787
787
|
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
788
|
-
const
|
|
788
|
+
const styleCanvas = 'position:absolute';
|
|
789
789
|
|
|
790
790
|
///////////////////////////////////////////////////////////////////////////////
|
|
791
791
|
|
|
@@ -810,7 +810,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
810
810
|
document.body.style = styleBody;
|
|
811
811
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
812
812
|
mainContext = mainCanvas.getContext('2d');
|
|
813
|
-
mainCanvas.style =
|
|
813
|
+
mainCanvas.style = styleCanvas;
|
|
814
814
|
|
|
815
815
|
// init stuff and start engine
|
|
816
816
|
debugInit();
|
|
@@ -819,7 +819,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
819
819
|
// create overlay canvas for hud to appear above gl canvas
|
|
820
820
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
821
821
|
overlayContext = overlayCanvas.getContext('2d');
|
|
822
|
-
overlayCanvas.style =
|
|
822
|
+
overlayCanvas.style = styleCanvas;
|
|
823
823
|
|
|
824
824
|
gameInit();
|
|
825
825
|
touchGamepadCreate();
|
|
@@ -835,7 +835,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
835
835
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
836
836
|
frameTimeLastMS = frameTimeMS;
|
|
837
837
|
if (debug || showWatermark)
|
|
838
|
-
averageFPS = lerp(.05, 1e3/(frameTimeDeltaMS||1)
|
|
838
|
+
averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
|
|
839
839
|
if (debug)
|
|
840
840
|
frameTimeDeltaMS *= keyIsDown(107) ? 5 : keyIsDown(109) ? .2 : 1; // +/- to speed/slow time
|
|
841
841
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
@@ -886,13 +886,24 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
886
886
|
|
|
887
887
|
// fit to window by adding space on top or bottom if necessary
|
|
888
888
|
const aspect = innerWidth / innerHeight;
|
|
889
|
-
const fixedAspect =
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
889
|
+
const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
|
|
890
|
+
|
|
891
|
+
// clear css
|
|
892
|
+
mainCanvas.style.width = overlayCanvas.style.width =
|
|
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
|
|
893
904
|
{
|
|
894
|
-
|
|
895
|
-
|
|
905
|
+
mainCanvas.style.height = overlayCanvas.style.height = innerHeight;
|
|
906
|
+
mainCanvas.style.left = overlayCanvas.style.left = (innerWidth - innerHeight*fixedAspect)/2;
|
|
896
907
|
}
|
|
897
908
|
}
|
|
898
909
|
else
|
|
@@ -900,6 +911,18 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
900
911
|
// clear and set size to same as window
|
|
901
912
|
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
902
913
|
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;
|
|
903
926
|
}
|
|
904
927
|
|
|
905
928
|
// render sort then render while removing destroyed objects
|
|
@@ -1042,7 +1065,7 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
1042
1065
|
class EngineObject
|
|
1043
1066
|
{
|
|
1044
1067
|
/** Create an engine object and adds it to the list of objects
|
|
1045
|
-
* @param {Vector2} [position=new Vector2(
|
|
1068
|
+
* @param {Vector2} [position=new Vector2()] - World space position of the object
|
|
1046
1069
|
* @param {Vector2} [size=objectDefaultSize] - World space size of the object
|
|
1047
1070
|
* @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
|
|
1048
1071
|
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
|
|
@@ -1087,12 +1110,15 @@ class EngineObject
|
|
|
1087
1110
|
this.gravityScale = 1;
|
|
1088
1111
|
/** @property {Number} [renderOrder=0] - Objects are sorted by render order */
|
|
1089
1112
|
this.renderOrder = renderOrder;
|
|
1113
|
+
/** @property {Vector2} [velocity=new Vector2()] - Velocity of the object */
|
|
1114
|
+
this.velocity = new Vector2();
|
|
1115
|
+
/** @property {Number} [angleVelocity=0] - Angular velocity of the object */
|
|
1116
|
+
this.angleVelocity = 0;
|
|
1090
1117
|
|
|
1091
1118
|
// init other internal object stuff
|
|
1092
1119
|
this.spawnTime = time;
|
|
1093
|
-
this.velocity = vec2(this.collideSolidObjects = this.angleVelocity = 0);
|
|
1094
|
-
this.collideTiles = 1;
|
|
1095
1120
|
this.children = [];
|
|
1121
|
+
this.collideTiles = 1;
|
|
1096
1122
|
|
|
1097
1123
|
// add to list of objects
|
|
1098
1124
|
engineObjects.push(this);
|
|
@@ -1111,8 +1137,8 @@ class EngineObject
|
|
|
1111
1137
|
}
|
|
1112
1138
|
|
|
1113
1139
|
// limit max speed to prevent missing collisions
|
|
1114
|
-
this.velocity.x = clamp(this.velocity.x, objectMaxSpeed,
|
|
1115
|
-
this.velocity.y = clamp(this.velocity.y, objectMaxSpeed,
|
|
1140
|
+
this.velocity.x = clamp(this.velocity.x, -objectMaxSpeed, objectMaxSpeed);
|
|
1141
|
+
this.velocity.y = clamp(this.velocity.y, -objectMaxSpeed, objectMaxSpeed);
|
|
1116
1142
|
|
|
1117
1143
|
// apply physics
|
|
1118
1144
|
const oldPos = this.pos.copy();
|
|
@@ -1202,8 +1228,8 @@ class EngineObject
|
|
|
1202
1228
|
|
|
1203
1229
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1204
1230
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
1205
|
-
this.velocity.y = lerp(elasticity,
|
|
1206
|
-
o.velocity.y = lerp(elasticity,
|
|
1231
|
+
this.velocity.y = lerp(elasticity, inelastic, elastic0);
|
|
1232
|
+
o.velocity.y = lerp(elasticity, inelastic, elastic1);
|
|
1207
1233
|
}
|
|
1208
1234
|
}
|
|
1209
1235
|
if (!smallStepUp && (isBlockedX || !isBlockedY)) // resolve x collision
|
|
@@ -1223,8 +1249,8 @@ class EngineObject
|
|
|
1223
1249
|
|
|
1224
1250
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1225
1251
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
1226
|
-
this.velocity.x = lerp(elasticity,
|
|
1227
|
-
o.velocity.x = lerp(elasticity,
|
|
1252
|
+
this.velocity.x = lerp(elasticity, inelastic, elastic0);
|
|
1253
|
+
o.velocity.x = lerp(elasticity, inelastic, elastic1);
|
|
1228
1254
|
}
|
|
1229
1255
|
else // bounce if other object is fixed
|
|
1230
1256
|
this.velocity.x *= -this.elasticity;
|
|
@@ -1254,7 +1280,7 @@ class EngineObject
|
|
|
1254
1280
|
|
|
1255
1281
|
// adjust next velocity to settle on ground
|
|
1256
1282
|
const o = (oldPos.y - this.size.y/2|0) - (oldPos.y - this.size.y/2);
|
|
1257
|
-
if (o < 0 && o >
|
|
1283
|
+
if (o < 0 && o > this.damping * this.velocity.y + gravity * this.gravityScale)
|
|
1258
1284
|
this.velocity.y = this.damping ? (o - gravity * this.gravityScale) / this.damping : 0;
|
|
1259
1285
|
|
|
1260
1286
|
// move to previous position
|
|
@@ -1565,6 +1591,18 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1565
1591
|
context.restore();
|
|
1566
1592
|
}
|
|
1567
1593
|
|
|
1594
|
+
/** Enable normal or additive blend mode
|
|
1595
|
+
* @param {Boolean} [additive=0]
|
|
1596
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
1597
|
+
* @memberof Draw */
|
|
1598
|
+
function setBlendMode(additive, useWebGL=glEnable)
|
|
1599
|
+
{
|
|
1600
|
+
if (glEnable && useWebGL)
|
|
1601
|
+
glSetBlendMode(additive);
|
|
1602
|
+
else
|
|
1603
|
+
mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1568
1606
|
/** Draw text on overlay canvas in world space
|
|
1569
1607
|
* @param {String} text
|
|
1570
1608
|
* @param {Vector2} pos
|
|
@@ -1590,16 +1628,93 @@ function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new
|
|
|
1590
1628
|
overlayContext.fillText(text, pos.x, pos.y);
|
|
1591
1629
|
}
|
|
1592
1630
|
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
*
|
|
1597
|
-
|
|
1631
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1632
|
+
|
|
1633
|
+
/**
|
|
1634
|
+
* Font Image Object - Draw text on a 2D canvas by using characters in an image
|
|
1635
|
+
* <br> - 96 characters (from space to tilde) are stored in an image
|
|
1636
|
+
* <br> - Uses a default 8x8 font if none is supplied
|
|
1637
|
+
* <br> - You can also use fonts from the main tile sheet
|
|
1638
|
+
* @example
|
|
1639
|
+
* // use built in font
|
|
1640
|
+
* const font = new ImageFont;
|
|
1641
|
+
*
|
|
1642
|
+
* // draw text
|
|
1643
|
+
* font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
|
|
1644
|
+
*/
|
|
1645
|
+
class FontImage
|
|
1598
1646
|
{
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1647
|
+
/** Create an image font
|
|
1648
|
+
* @param {Image} [image] - The image the font is stored in, if undefined the default font is used
|
|
1649
|
+
* @param {Vector2} [tileSize=vec2(8)] - The size of the font source tiles
|
|
1650
|
+
* @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
|
|
1651
|
+
* @param {Number} [startTileIndex=0] - Tile index in image where font starts
|
|
1652
|
+
* @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
1653
|
+
*/
|
|
1654
|
+
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
|
|
1655
|
+
{
|
|
1656
|
+
if (!image)
|
|
1657
|
+
{
|
|
1658
|
+
// use default font image
|
|
1659
|
+
image = new Image();
|
|
1660
|
+
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6MCIBdFY+Q0PMNgf0yCgsSAaZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWw1hPWNRcEyWh2npdpBmSUBrRaN1WSlQAWXaWLF0kkwR7C/N7XUvIaQr4xpeqeQigjLwBRZ/6KkufAACcv5z7GYhvgWHII8xN/4C2hvLCkmZSVgIJMHNTxpYOsJOiitEjUQnIQAFFP+opoQfCbjWmfzWiNzCjEvxAPFARmw/Y5Te0sl2zEp4M+m7cToJjdJS7y1xcFlFT7EtWBZ8nl7uLSff7UgYewtlyY6yHM4RrjNrnsfn/W0DBK9xNZctZ7gsTeXTe5eqLqXFlKnAn+vkNXzH6TqPW10ksKoSlauKk1TcncZLSTxNnQ8Av/8RGPXOFIuMCZ/fpwIZx5vU4QIRQ44HwUyHx/QrZhm5VtXFex7ja2se0KBpnPT0DSwS4xd86uAy+SqnLBgkPY5JolhJ33ENyK6n0VyRzCnzebIkFdY/NUtM66lTdhix1KUss6q5snH5ftVJXG0QITb1gUDvzD3z/BbyOLMirwCtPzAAAAAElFTkSuQmCC';
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
this.image = image;
|
|
1664
|
+
this.tileSize = tileSize;
|
|
1665
|
+
this.paddingSize = paddingSize;
|
|
1666
|
+
this.startTileIndex = startTileIndex;
|
|
1667
|
+
this.context = context;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
/** Draw text in screen space using the image font
|
|
1671
|
+
* @param {String} text
|
|
1672
|
+
* @param {Vector2} pos
|
|
1673
|
+
* @param {Number} [scale=4]
|
|
1674
|
+
* @param {Boolean} [center]
|
|
1675
|
+
*/
|
|
1676
|
+
drawTextScreen(text, pos, scale=4, center)
|
|
1677
|
+
{
|
|
1678
|
+
const context = this.context;
|
|
1679
|
+
context.save();
|
|
1680
|
+
context.imageSmoothingEnabled = !cavasPixelated;
|
|
1681
|
+
|
|
1682
|
+
const size = this.tileSize;
|
|
1683
|
+
const drawSize = size.add(this.paddingSize).scale(scale);
|
|
1684
|
+
const cols = this.image.width / this.tileSize.x |0;
|
|
1685
|
+
text.split('\n').forEach((line, i)=>
|
|
1686
|
+
{
|
|
1687
|
+
const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
|
|
1688
|
+
for(let j=line.length; j--;)
|
|
1689
|
+
{
|
|
1690
|
+
// draw each character
|
|
1691
|
+
let charCode = line[j].charCodeAt();
|
|
1692
|
+
if (charCode < 32 || charCode > 127)
|
|
1693
|
+
charCode = 127; // unknown character
|
|
1694
|
+
|
|
1695
|
+
// get the character source location and draw it
|
|
1696
|
+
const tile = this.startTileIndex + charCode - 32;
|
|
1697
|
+
const x = tile % cols;
|
|
1698
|
+
const y = tile / cols |0;
|
|
1699
|
+
const drawPos = pos.add(vec2(j,i).multiply(drawSize));
|
|
1700
|
+
context.drawImage(this.image, x * size.x, y * size.y, size.x, size.y,
|
|
1701
|
+
drawPos.x - centerOffset, drawPos.y, size.x * scale, size.y * scale);
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
|
|
1705
|
+
context.restore();
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
/** Draw text in world space using the image font
|
|
1709
|
+
* @param {String} text
|
|
1710
|
+
* @param {Vector2} pos
|
|
1711
|
+
* @param {Number} [scale=.25]
|
|
1712
|
+
* @param {Boolean} [center]
|
|
1713
|
+
*/
|
|
1714
|
+
drawText(text, pos, scale=1, center)
|
|
1715
|
+
{
|
|
1716
|
+
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
|
|
1717
|
+
}
|
|
1603
1718
|
}
|
|
1604
1719
|
|
|
1605
1720
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1790,7 +1905,7 @@ const mouseToScreen = (mousePos)=>
|
|
|
1790
1905
|
|
|
1791
1906
|
const rect = mainCanvas.getBoundingClientRect();
|
|
1792
1907
|
return mainCanvasSize.multiply(
|
|
1793
|
-
vec2(percent(mousePos.x, rect.
|
|
1908
|
+
vec2(percent(mousePos.x, rect.left, rect.right), percent(mousePos.y, rect.top, rect.bottom)));
|
|
1794
1909
|
}
|
|
1795
1910
|
|
|
1796
1911
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1831,8 +1946,8 @@ function gamepadsUpdate()
|
|
|
1831
1946
|
// read clamp dead zone of analog sticks
|
|
1832
1947
|
const deadZone = .3, deadZoneMax = .8;
|
|
1833
1948
|
const applyDeadZone = (v)=>
|
|
1834
|
-
v > deadZone ? percent( v,
|
|
1835
|
-
v < -deadZone ? -percent(-v,
|
|
1949
|
+
v > deadZone ? percent( v, deadZone, deadZoneMax) :
|
|
1950
|
+
v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
|
|
1836
1951
|
|
|
1837
1952
|
// read analog sticks
|
|
1838
1953
|
for (let j = 0; j < gamepad.axes.length-1; j+=2)
|
|
@@ -1912,7 +2027,7 @@ if (isTouchDevice)
|
|
|
1912
2027
|
// touch gamepad, virtual on screen gamepad emulator for touch devices
|
|
1913
2028
|
|
|
1914
2029
|
// touch input internal variables
|
|
1915
|
-
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2();
|
|
2030
|
+
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2(), touchGamepadAnalog = 1;
|
|
1916
2031
|
|
|
1917
2032
|
// create the touch gamepad, called automatically by the engine
|
|
1918
2033
|
function touchGamepadCreate()
|
|
@@ -1958,7 +2073,14 @@ function touchGamepadCreate()
|
|
|
1958
2073
|
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
1959
2074
|
{
|
|
1960
2075
|
// virtual analog stick
|
|
1961
|
-
|
|
2076
|
+
if (touchGamepadAnalog)
|
|
2077
|
+
touchGamepadStick = touchPos.subtract(stickCenter).clampLength(touchGamepadSize/2).scale(2/touchGamepadSize);
|
|
2078
|
+
else
|
|
2079
|
+
{
|
|
2080
|
+
// 8 way dpad
|
|
2081
|
+
const angle = touchPos.subtract(stickCenter).angle();
|
|
2082
|
+
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
2083
|
+
}
|
|
1962
2084
|
}
|
|
1963
2085
|
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
1964
2086
|
{
|
|
@@ -1982,7 +2104,7 @@ function touchGamepadRender()
|
|
|
1982
2104
|
return;
|
|
1983
2105
|
|
|
1984
2106
|
// fade off when not touching or paused
|
|
1985
|
-
const alpha = percent(touchGamepadTimer.get(),
|
|
2107
|
+
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
1986
2108
|
if (!alpha || paused)
|
|
1987
2109
|
return;
|
|
1988
2110
|
|
|
@@ -2085,7 +2207,7 @@ class Sound
|
|
|
2085
2207
|
return; // out of range
|
|
2086
2208
|
|
|
2087
2209
|
// attenuate volume by distance
|
|
2088
|
-
volume *= percent(lengthSquared**.5, range*this.taper
|
|
2210
|
+
volume *= percent(lengthSquared**.5, range, range*this.taper);
|
|
2089
2211
|
}
|
|
2090
2212
|
|
|
2091
2213
|
// get pan from screen space coords
|
|
@@ -2267,7 +2389,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
2267
2389
|
// connect source to gain
|
|
2268
2390
|
(
|
|
2269
2391
|
window.StereoPannerNode ? // create pan node if possible
|
|
2270
|
-
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, 1,
|
|
2392
|
+
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)}))
|
|
2271
2393
|
: source
|
|
2272
2394
|
)
|
|
2273
2395
|
.connect(gainNode);
|
|
@@ -2640,11 +2762,11 @@ class TileLayerData
|
|
|
2640
2762
|
class TileLayer extends EngineObject
|
|
2641
2763
|
{
|
|
2642
2764
|
/** Create a tile layer object
|
|
2643
|
-
* @param {Vector2} [position=new Vector2(
|
|
2644
|
-
* @param {Vector2} [size=objectDefaultSize]
|
|
2645
|
-
* @param {Vector2} [tileSize=tileSizeDefault]
|
|
2646
|
-
* @param {Vector2} [scale=new Vector2(1,1)]
|
|
2647
|
-
* @param {Number} [renderOrder=0]
|
|
2765
|
+
* @param {Vector2} [position=new Vector2()] - World space position
|
|
2766
|
+
* @param {Vector2} [size=objectDefaultSize] - World space size
|
|
2767
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
|
|
2768
|
+
* @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
|
|
2769
|
+
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
2648
2770
|
*/
|
|
2649
2771
|
constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
|
|
2650
2772
|
{
|
|
@@ -2704,17 +2826,19 @@ class TileLayer extends EngineObject
|
|
|
2704
2826
|
);
|
|
2705
2827
|
}
|
|
2706
2828
|
|
|
2707
|
-
/** Draw all the tile data to an offscreen canvas
|
|
2829
|
+
/** Draw all the tile data to an offscreen canvas
|
|
2830
|
+
* - This may be slow in some browsers
|
|
2831
|
+
*/
|
|
2708
2832
|
redraw()
|
|
2709
2833
|
{
|
|
2710
|
-
this.redrawStart();
|
|
2834
|
+
this.redrawStart(1);
|
|
2711
2835
|
this.drawAllTileData();
|
|
2712
2836
|
this.redrawEnd();
|
|
2713
2837
|
}
|
|
2714
2838
|
|
|
2715
2839
|
/** Call to start the redraw process
|
|
2716
|
-
* @param {Boolean} [clear=
|
|
2717
|
-
redrawStart(clear =
|
|
2840
|
+
* @param {Boolean} [clear=0] - Should it clear the canvas before drawing */
|
|
2841
|
+
redrawStart(clear = 0)
|
|
2718
2842
|
{
|
|
2719
2843
|
if (clear)
|
|
2720
2844
|
{
|
|
@@ -3445,8 +3569,8 @@ function glInit()
|
|
|
3445
3569
|
|
|
3446
3570
|
// create the canvas and tile texture
|
|
3447
3571
|
glCanvas = document.createElement('canvas');
|
|
3448
|
-
glContext = glCanvas.getContext('webgl');
|
|
3449
|
-
glCanvas.style =
|
|
3572
|
+
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
3573
|
+
glCanvas.style = styleCanvas;
|
|
3450
3574
|
glTileTexture = glCreateTexture(tileImage);
|
|
3451
3575
|
|
|
3452
3576
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
@@ -3456,14 +3580,13 @@ function glInit()
|
|
|
3456
3580
|
glShader = glCreateProgram(
|
|
3457
3581
|
'precision highp float;'+ // use highp for better accuracy, lowp for better perf
|
|
3458
3582
|
'uniform mat4 m;'+ // transform matrix
|
|
3459
|
-
'attribute
|
|
3460
|
-
'attribute
|
|
3461
|
-
'attribute vec4 c,b;'+ // color, additiveColor
|
|
3583
|
+
'attribute vec2 p,t;'+ // position, uv
|
|
3584
|
+
'attribute vec4 c,a;'+ // color, additiveColor
|
|
3462
3585
|
'varying vec2 v;'+ // return uv
|
|
3463
3586
|
'varying vec4 d,e;'+ // return color, additiveColor
|
|
3464
3587
|
'void main(){'+ // shader entry point
|
|
3465
|
-
'gl_Position=m*vec4(
|
|
3466
|
-
'v=t;d=c;e=
|
|
3588
|
+
'gl_Position=m*vec4(p,1,1);'+// transform position
|
|
3589
|
+
'v=t;d=c;e=a;'+ // pass stuff to fragment shader
|
|
3467
3590
|
'}' // end of shader
|
|
3468
3591
|
,
|
|
3469
3592
|
'precision highp float;'+ // use highp for better accuracy, lowp for better perf
|
|
@@ -3490,12 +3613,10 @@ function glInit()
|
|
|
3490
3613
|
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
3491
3614
|
offset += size*typeSize;
|
|
3492
3615
|
}
|
|
3493
|
-
initVertexAttribArray('a', gl_FLOAT, 4, 1); // angle
|
|
3494
3616
|
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
3495
|
-
initVertexAttribArray('s', gl_FLOAT, 4, 2); // size
|
|
3496
3617
|
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
3497
3618
|
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
3498
|
-
initVertexAttribArray('
|
|
3619
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
3499
3620
|
}
|
|
3500
3621
|
|
|
3501
3622
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
@@ -3610,6 +3731,7 @@ function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
|
3610
3731
|
|
|
3611
3732
|
// clear and set to same size as main canvas
|
|
3612
3733
|
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height);
|
|
3734
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3613
3735
|
|
|
3614
3736
|
// set up the shader
|
|
3615
3737
|
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
@@ -3656,12 +3778,10 @@ function glCopyToContext(context, forceDraw)
|
|
|
3656
3778
|
if (!glEnable || !glBatchCount) return;
|
|
3657
3779
|
|
|
3658
3780
|
glFlush();
|
|
3781
|
+
|
|
3782
|
+
// do not draw in overlay mode because the canvas is visible
|
|
3659
3783
|
if (!glOverlay || forceDraw)
|
|
3660
|
-
{
|
|
3661
|
-
// do not draw/clear in overlay mode because the canvas is visible
|
|
3662
3784
|
context.drawImage(glCanvas, 0, 0);
|
|
3663
|
-
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3664
|
-
}
|
|
3665
3785
|
}
|
|
3666
3786
|
|
|
3667
3787
|
/** Add a sprite to the gl draw list, used by all gl draw functions
|
|
@@ -3684,51 +3804,49 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
3684
3804
|
// flush if there is no room for more verts or if different blend mode
|
|
3685
3805
|
if (glBatchCount == gl_MAX_BATCH || glBatchAdditive != glAdditive)
|
|
3686
3806
|
glFlush();
|
|
3807
|
+
|
|
3808
|
+
// prepare to create the verts from size and angle
|
|
3809
|
+
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
3810
|
+
const cX = c*sizeX, cY = c*sizeY, sX = s*sizeX, sY = s*sizeY;
|
|
3687
3811
|
|
|
3688
3812
|
// setup 2 triangles to form a quad
|
|
3689
3813
|
let offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT;
|
|
3690
3814
|
|
|
3691
3815
|
// vertex 0
|
|
3692
|
-
glPositionData[offset++] =
|
|
3693
|
-
glPositionData[offset++] =
|
|
3694
|
-
glPositionData[offset++] =
|
|
3695
|
-
|
|
3696
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3816
|
+
glPositionData[offset++] = x - cX - sY;
|
|
3817
|
+
glPositionData[offset++] = y - cY + sX;
|
|
3818
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3819
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3697
3820
|
|
|
3698
3821
|
// vertex 1
|
|
3699
|
-
glPositionData[offset++] =
|
|
3700
|
-
glPositionData[offset++] =
|
|
3701
|
-
glPositionData[offset++] =
|
|
3702
|
-
|
|
3703
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3822
|
+
glPositionData[offset++] = x + cX + sY;
|
|
3823
|
+
glPositionData[offset++] = y + cY - sX;
|
|
3824
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3825
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3704
3826
|
|
|
3705
3827
|
// vertex 2
|
|
3706
|
-
glPositionData[offset++] =
|
|
3707
|
-
glPositionData[offset++] =
|
|
3708
|
-
glPositionData[offset++] =
|
|
3709
|
-
|
|
3710
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3828
|
+
glPositionData[offset++] = x - cX + sY;
|
|
3829
|
+
glPositionData[offset++] = y + cY + sX;
|
|
3830
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv0Y;
|
|
3831
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3711
3832
|
|
|
3712
3833
|
// vertex 0
|
|
3713
|
-
glPositionData[offset++] =
|
|
3714
|
-
glPositionData[offset++] =
|
|
3715
|
-
glPositionData[offset++] =
|
|
3716
|
-
|
|
3717
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3834
|
+
glPositionData[offset++] = x - cX - sY;
|
|
3835
|
+
glPositionData[offset++] = y - cY + sX;
|
|
3836
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3837
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3718
3838
|
|
|
3719
3839
|
// vertex 3
|
|
3720
|
-
glPositionData[offset++] =
|
|
3721
|
-
glPositionData[offset++] =
|
|
3722
|
-
glPositionData[offset++] =
|
|
3723
|
-
|
|
3724
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3840
|
+
glPositionData[offset++] = x + cX - sY;
|
|
3841
|
+
glPositionData[offset++] = y - cY - sX;
|
|
3842
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv1Y;
|
|
3843
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3725
3844
|
|
|
3726
3845
|
// vertex 1
|
|
3727
|
-
glPositionData[offset++] =
|
|
3728
|
-
glPositionData[offset++] =
|
|
3729
|
-
glPositionData[offset++] =
|
|
3730
|
-
|
|
3731
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3846
|
+
glPositionData[offset++] = x + cX + sY;
|
|
3847
|
+
glPositionData[offset++] = y + cY - sX;
|
|
3848
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3849
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3732
3850
|
}
|
|
3733
3851
|
|
|
3734
3852
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -3760,6 +3878,6 @@ gl_LINK_STATUS = 35714,
|
|
|
3760
3878
|
|
|
3761
3879
|
// constants for batch rendering
|
|
3762
3880
|
gl_VERTICES_PER_QUAD = 6,
|
|
3763
|
-
gl_INDICIES_PER_VERT =
|
|
3881
|
+
gl_INDICIES_PER_VERT = 6,
|
|
3764
3882
|
gl_MAX_BATCH = 1<<16,
|
|
3765
|
-
gl_VERTEX_BYTE_STRIDE =
|
|
3883
|
+
gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|