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
package/engine/engine.all.js
CHANGED
|
@@ -160,7 +160,7 @@ const debugUpdate = ()=>
|
|
|
160
160
|
{
|
|
161
161
|
if (!debug)
|
|
162
162
|
return;
|
|
163
|
-
|
|
163
|
+
|
|
164
164
|
if (keyWasPressed(192)) // ~
|
|
165
165
|
debugOverlay = !debugOverlay;
|
|
166
166
|
if (debugOverlay)
|
|
@@ -273,17 +273,11 @@ const debugRender = ()=>
|
|
|
273
273
|
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), new Color(0,0,1,.5));
|
|
274
274
|
drawLine(mousePos, bestObject.pos, .1, !raycastHitPos ? new Color(0,1,0,.5) : new Color(1,0,0,.5));
|
|
275
275
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
+ (bestObject.angle>0?' ':' ') + (bestObject.angle*180/PI).toFixed(1) + '°',
|
|
282
|
-
pos = pos.add(height), ...args);
|
|
283
|
-
drawText('vel = ' + printVec2(bestObject.velocity), pos = pos.add(height), ...args);
|
|
284
|
-
drawText('size = ' + printVec2(bestObject.size), pos = pos.add(height), ...args);
|
|
285
|
-
drawText('type = ' + ( bestObject.constructor.name), pos = mousePos.subtract(height), ...args);
|
|
286
|
-
drawText('collision = ' + getTileCollisionData(mousePos), pos = mousePos.subtract(height.scale(2)), ...args);
|
|
276
|
+
const debugText = 'mouse pos = ' + mousePos +
|
|
277
|
+
'\nmouse collision = ' + getTileCollisionData(mousePos) +
|
|
278
|
+
'\n\n--- object info ---\n' +
|
|
279
|
+
bestObject.toString();
|
|
280
|
+
drawTextScreen(debugText, mousePosScreen, 24, new Color, .05, undefined, undefined, 'monospace');
|
|
287
281
|
mainContext = saveContext;
|
|
288
282
|
}
|
|
289
283
|
|
|
@@ -292,11 +286,12 @@ const debugRender = ()=>
|
|
|
292
286
|
|
|
293
287
|
{
|
|
294
288
|
// draw debug primitives
|
|
295
|
-
overlayContext.save();
|
|
296
289
|
overlayContext.lineWidth = 2;
|
|
297
290
|
const pointSize = debugPointSize * cameraScale;
|
|
298
291
|
debugPrimitives.forEach(p=>
|
|
299
292
|
{
|
|
293
|
+
overlayContext.save();
|
|
294
|
+
|
|
300
295
|
// create canvas transform from world space to screen space
|
|
301
296
|
const pos = worldToScreen(p.pos);
|
|
302
297
|
|
|
@@ -332,12 +327,12 @@ const debugRender = ()=>
|
|
|
332
327
|
p.fill && overlayContext.fill();
|
|
333
328
|
overlayContext.stroke();
|
|
334
329
|
}
|
|
330
|
+
|
|
331
|
+
overlayContext.restore();
|
|
335
332
|
});
|
|
336
333
|
|
|
337
334
|
// remove expired pritives
|
|
338
335
|
debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
|
|
339
|
-
|
|
340
|
-
overlayContext.restore();
|
|
341
336
|
}
|
|
342
337
|
|
|
343
338
|
{
|
|
@@ -711,7 +706,13 @@ class Vector2
|
|
|
711
706
|
/** Returns true if this vector is within the bounds of an array size passed in
|
|
712
707
|
* @param {Vector2} arraySize
|
|
713
708
|
* @return {Boolean} */
|
|
714
|
-
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
709
|
+
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
710
|
+
|
|
711
|
+
/** Returns this vector expressed as a string
|
|
712
|
+
* @param {float} digits - precision to display
|
|
713
|
+
* @return {String} */
|
|
714
|
+
toString(digits=3)
|
|
715
|
+
{ return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }
|
|
715
716
|
}
|
|
716
717
|
|
|
717
718
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -782,7 +783,7 @@ class Color
|
|
|
782
783
|
* @return {Color} */
|
|
783
784
|
lerp(c, p) { return this.add(c.subtract(this).scale(clamp(p))); }
|
|
784
785
|
|
|
785
|
-
/** Sets this color given a hue, saturation, lightness
|
|
786
|
+
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
786
787
|
* @param {Number} [hue=0]
|
|
787
788
|
* @param {Number} [saturation=0]
|
|
788
789
|
* @param {Number} [lightness=1]
|
|
@@ -818,21 +819,43 @@ class Color
|
|
|
818
819
|
).clamp();
|
|
819
820
|
}
|
|
820
821
|
|
|
821
|
-
/** Returns this color expressed as an
|
|
822
|
+
/** Returns this color expressed as an CSS color value
|
|
822
823
|
* @return {String} */
|
|
823
|
-
|
|
824
|
+
toString()
|
|
824
825
|
{
|
|
825
826
|
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);
|
|
826
827
|
return `rgb(${this.r*255|0},${this.g*255|0},${this.b*255|0},${this.a})`;
|
|
827
828
|
}
|
|
828
829
|
|
|
829
|
-
/** Returns this color expressed as 32 bit integer value
|
|
830
|
+
/** Returns this color expressed as 32 bit integer RGBA value
|
|
830
831
|
* @return {Number} */
|
|
831
832
|
rgbaInt()
|
|
832
833
|
{
|
|
833
834
|
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);
|
|
834
835
|
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
835
836
|
}
|
|
837
|
+
|
|
838
|
+
/** Returns this color expressed as a hex code
|
|
839
|
+
* @return {String} */
|
|
840
|
+
hex()
|
|
841
|
+
{
|
|
842
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
843
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/** Set this color from a hex code
|
|
847
|
+
* @param {String} hex - html hex code
|
|
848
|
+
* @return {Color} */
|
|
849
|
+
setHex(hex)
|
|
850
|
+
{
|
|
851
|
+
const fromHex = (a)=> parseInt(hex.slice(a,a+2), 16)/255;
|
|
852
|
+
this.r = fromHex(1);
|
|
853
|
+
this.g = fromHex(3),
|
|
854
|
+
this.b = fromHex(5);
|
|
855
|
+
this.a = 1;
|
|
856
|
+
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
857
|
+
return this;
|
|
858
|
+
}
|
|
836
859
|
}
|
|
837
860
|
|
|
838
861
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -869,7 +892,7 @@ class Timer
|
|
|
869
892
|
|
|
870
893
|
/** Returns true if set and elapsed
|
|
871
894
|
* @return {Boolean} */
|
|
872
|
-
elapsed() { return time >
|
|
895
|
+
elapsed() { return time > this.time; }
|
|
873
896
|
|
|
874
897
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
875
898
|
* @return {Number} */
|
|
@@ -878,6 +901,10 @@ class Timer
|
|
|
878
901
|
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
879
902
|
* @return {Number} */
|
|
880
903
|
getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
|
|
904
|
+
|
|
905
|
+
/** Returns this timer expressed as a string
|
|
906
|
+
* @return {String} */
|
|
907
|
+
toString() { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }
|
|
881
908
|
}
|
|
882
909
|
/**
|
|
883
910
|
* LittleJS Engine Settings
|
|
@@ -1026,6 +1053,11 @@ let inputWASDEmulateDirection = 1;
|
|
|
1026
1053
|
* @memberof Settings */
|
|
1027
1054
|
let touchGamepadEnable = 0;
|
|
1028
1055
|
|
|
1056
|
+
/** True if touch gamepad should be analog stick or false to use if 8 way dpad
|
|
1057
|
+
* @default
|
|
1058
|
+
* @memberof Settings */
|
|
1059
|
+
let touchGamepadAnalog = 1;
|
|
1060
|
+
|
|
1029
1061
|
/** Size of virutal gamepad for touch devices in pixels
|
|
1030
1062
|
* @default
|
|
1031
1063
|
* @memberof Settings */
|
|
@@ -1129,7 +1161,7 @@ class EngineObject
|
|
|
1129
1161
|
* @param {Vector2} [size=objectDefaultSize] - World space size of the object
|
|
1130
1162
|
* @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
|
|
1131
1163
|
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
|
|
1132
|
-
* @param {Number} [angle=0] - Angle
|
|
1164
|
+
* @param {Number} [angle=0] - Angle the object is rotated by
|
|
1133
1165
|
* @param {Color} [color] - Color to apply to tile when rendered
|
|
1134
1166
|
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
1135
1167
|
*/
|
|
@@ -1445,6 +1477,22 @@ class EngineObject
|
|
|
1445
1477
|
this.isSolid = isSolid;
|
|
1446
1478
|
this.collideTiles = collideTiles;
|
|
1447
1479
|
}
|
|
1480
|
+
|
|
1481
|
+
toString()
|
|
1482
|
+
{
|
|
1483
|
+
let text = 'type = ' + this.constructor.name;
|
|
1484
|
+
if (this.pos.x || this.pos.y)
|
|
1485
|
+
text += '\npos = ' + this.pos;
|
|
1486
|
+
if (this.velocity.x || this.velocity.y)
|
|
1487
|
+
text += '\nvelocity = ' + this.velocity;
|
|
1488
|
+
if (this.size.x || this.size.y)
|
|
1489
|
+
text += '\nsize = ' + this.size;
|
|
1490
|
+
if (this.angle)
|
|
1491
|
+
text += '\nangle = ' + this.angle.toFixed(3);
|
|
1492
|
+
if (this.color)
|
|
1493
|
+
text += '\ncolor = ' + this.color;
|
|
1494
|
+
return text;
|
|
1495
|
+
}
|
|
1448
1496
|
}
|
|
1449
1497
|
/**
|
|
1450
1498
|
* LittleJS Drawing System
|
|
@@ -1558,7 +1606,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
1558
1606
|
if (tileIndex < 0)
|
|
1559
1607
|
{
|
|
1560
1608
|
// if negative tile index, force untextured
|
|
1561
|
-
context.fillStyle = color
|
|
1609
|
+
context.fillStyle = color;
|
|
1562
1610
|
context.fillRect(-.5, -.5, 1, 1);
|
|
1563
1611
|
}
|
|
1564
1612
|
else
|
|
@@ -1663,7 +1711,8 @@ function setBlendMode(additive, useWebGL=glEnable)
|
|
|
1663
1711
|
mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
1664
1712
|
}
|
|
1665
1713
|
|
|
1666
|
-
/** Draw text on overlay canvas in
|
|
1714
|
+
/** Draw text on overlay canvas in screen space
|
|
1715
|
+
* Automatically splits new lines into rows
|
|
1667
1716
|
* @param {String} text
|
|
1668
1717
|
* @param {Vector2} pos
|
|
1669
1718
|
* @param {Number} [size=1]
|
|
@@ -1672,20 +1721,37 @@ function setBlendMode(additive, useWebGL=glEnable)
|
|
|
1672
1721
|
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1673
1722
|
* @param {String} [textAlign='center']
|
|
1674
1723
|
* @memberof Draw */
|
|
1675
|
-
function
|
|
1724
|
+
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
|
|
1676
1725
|
{
|
|
1677
|
-
|
|
1678
|
-
overlayContext.
|
|
1726
|
+
overlayContext.fillStyle = color;
|
|
1727
|
+
overlayContext.lineWidth = lineWidth *= cameraScale;
|
|
1728
|
+
overlayContext.strokeStyle = lineColor;
|
|
1679
1729
|
overlayContext.textAlign = textAlign;
|
|
1730
|
+
overlayContext.font = size + 'px '+ font;
|
|
1680
1731
|
overlayContext.textBaseline = 'middle';
|
|
1681
|
-
|
|
1732
|
+
|
|
1733
|
+
pos = pos.copy();
|
|
1734
|
+
text.split('\n').forEach(line=>
|
|
1682
1735
|
{
|
|
1683
|
-
overlayContext.
|
|
1684
|
-
overlayContext.
|
|
1685
|
-
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1736
|
+
lineWidth && overlayContext.strokeText(line, pos.x, pos.y);
|
|
1737
|
+
overlayContext.fillText(line, pos.x, pos.y);
|
|
1738
|
+
pos.y += size;
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
/** Draw text on overlay canvas in world space
|
|
1743
|
+
* Automatically splits new lines into rows
|
|
1744
|
+
* @param {String} text
|
|
1745
|
+
* @param {Vector2} pos
|
|
1746
|
+
* @param {Number} [size=1]
|
|
1747
|
+
* @param {Color} [color=new Color(1,1,1)]
|
|
1748
|
+
* @param {Number} [lineWidth=0]
|
|
1749
|
+
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1750
|
+
* @param {String} [textAlign='center']
|
|
1751
|
+
* @memberof Draw */
|
|
1752
|
+
function drawText(text, pos, size=1, color, lineWidth, lineColor, textAlign, font)
|
|
1753
|
+
{
|
|
1754
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth, lineColor, textAlign, font);
|
|
1689
1755
|
}
|
|
1690
1756
|
|
|
1691
1757
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1702,6 +1768,9 @@ function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new
|
|
|
1702
1768
|
* // draw text
|
|
1703
1769
|
* font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
|
|
1704
1770
|
*/
|
|
1771
|
+
|
|
1772
|
+
let engineFontImage;
|
|
1773
|
+
|
|
1705
1774
|
class FontImage
|
|
1706
1775
|
{
|
|
1707
1776
|
/** Create an image font
|
|
@@ -1713,14 +1782,14 @@ class FontImage
|
|
|
1713
1782
|
*/
|
|
1714
1783
|
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
|
|
1715
1784
|
{
|
|
1716
|
-
if (!image)
|
|
1785
|
+
if (!image && !engineFontImage)
|
|
1717
1786
|
{
|
|
1718
|
-
//
|
|
1719
|
-
|
|
1720
|
-
|
|
1787
|
+
// load default font image
|
|
1788
|
+
engineFontImage = new Image();
|
|
1789
|
+
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
1721
1790
|
}
|
|
1722
1791
|
|
|
1723
|
-
this.image = image;
|
|
1792
|
+
this.image = image || engineFontImage;
|
|
1724
1793
|
this.tileSize = tileSize;
|
|
1725
1794
|
this.paddingSize = paddingSize;
|
|
1726
1795
|
this.startTileIndex = startTileIndex;
|
|
@@ -2087,7 +2156,7 @@ if (isTouchDevice)
|
|
|
2087
2156
|
// touch gamepad, virtual on screen gamepad emulator for touch devices
|
|
2088
2157
|
|
|
2089
2158
|
// touch input internal variables
|
|
2090
|
-
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2()
|
|
2159
|
+
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2();
|
|
2091
2160
|
|
|
2092
2161
|
// create the touch gamepad, called automatically by the engine
|
|
2093
2162
|
function touchGamepadCreate()
|
|
@@ -2134,7 +2203,7 @@ function touchGamepadCreate()
|
|
|
2134
2203
|
{
|
|
2135
2204
|
// virtual analog stick
|
|
2136
2205
|
if (touchGamepadAnalog)
|
|
2137
|
-
touchGamepadStick = touchPos.subtract(stickCenter).
|
|
2206
|
+
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
2138
2207
|
else
|
|
2139
2208
|
{
|
|
2140
2209
|
// 8 way dpad
|
|
@@ -2175,12 +2244,28 @@ function touchGamepadRender()
|
|
|
2175
2244
|
overlayContext.lineWidth = 3;
|
|
2176
2245
|
|
|
2177
2246
|
// draw left analog stick
|
|
2178
|
-
overlayContext.fillStyle =
|
|
2247
|
+
overlayContext.fillStyle = touchGamepadStick.lengthSquared() > 0 ? '#fff' : '#000';
|
|
2179
2248
|
overlayContext.beginPath();
|
|
2180
|
-
overlayContext.arc(touchGamepadSize, mainCanvasSize.y-touchGamepadSize, touchGamepadSize/2, 0,9);
|
|
2181
|
-
overlayContext.fill();
|
|
2182
|
-
overlayContext.stroke();
|
|
2183
2249
|
|
|
2250
|
+
const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2251
|
+
if (touchGamepadAnalog)
|
|
2252
|
+
{
|
|
2253
|
+
overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
|
|
2254
|
+
overlayContext.fill();
|
|
2255
|
+
overlayContext.stroke();
|
|
2256
|
+
}
|
|
2257
|
+
else // draw cross shaped gamepad
|
|
2258
|
+
{
|
|
2259
|
+
for(let i=10; i--;)
|
|
2260
|
+
{
|
|
2261
|
+
const angle = i*PI/4;
|
|
2262
|
+
overlayContext.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
|
|
2263
|
+
i%2 && overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize*.33, angle, angle);
|
|
2264
|
+
i==1 && overlayContext.fill();
|
|
2265
|
+
}
|
|
2266
|
+
overlayContext.stroke();
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2184
2269
|
// draw right face buttons
|
|
2185
2270
|
const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2186
2271
|
for (let i=4; i--;)
|
|
@@ -2821,14 +2906,14 @@ class TileLayerData
|
|
|
2821
2906
|
*/
|
|
2822
2907
|
class TileLayer extends EngineObject
|
|
2823
2908
|
{
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2909
|
+
/** Create a tile layer object
|
|
2910
|
+
* @param {Vector2} [position=new Vector2()] - World space position
|
|
2911
|
+
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
2912
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
|
|
2913
|
+
* @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
|
|
2914
|
+
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
2915
|
+
*/
|
|
2916
|
+
constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
|
|
2832
2917
|
{
|
|
2833
2918
|
super(pos, size, -1, tileSize, 0, undefined, renderOrder);
|
|
2834
2919
|
|
|
@@ -2988,7 +3073,7 @@ class TileLayer extends EngineObject
|
|
|
2988
3073
|
if (tileIndex < 0)
|
|
2989
3074
|
{
|
|
2990
3075
|
// untextured
|
|
2991
|
-
context.fillStyle = color
|
|
3076
|
+
context.fillStyle = color;
|
|
2992
3077
|
context.fillRect(-.5, -.5, 1, 1);
|
|
2993
3078
|
}
|
|
2994
3079
|
else
|
|
@@ -3007,7 +3092,8 @@ class TileLayer extends EngineObject
|
|
|
3007
3092
|
* @param {Vector2} [size=new Vector2(1,1)]
|
|
3008
3093
|
* @param {Color} [color=new Color(1,1,1)]
|
|
3009
3094
|
* @param {Number} [angle=0] */
|
|
3010
|
-
drawRect(pos, size, color, angle)
|
|
3095
|
+
drawRect(pos, size, color, angle)
|
|
3096
|
+
{ this.drawTile(pos, size, -1, 0, color, angle); }
|
|
3011
3097
|
}
|
|
3012
3098
|
/*
|
|
3013
3099
|
LittleJS Particle System
|
|
@@ -3026,9 +3112,9 @@ class TileLayer extends EngineObject
|
|
|
3026
3112
|
* let pos = vec2(2,3);
|
|
3027
3113
|
* let particleEmiter = new ParticleEmitter
|
|
3028
3114
|
* (
|
|
3029
|
-
* pos, 1, 0, 500, PI, // pos, emitSize, emitTime, emitRate, emiteCone
|
|
3115
|
+
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
|
|
3030
3116
|
* 0, vec2(16), // tileIndex, tileSize
|
|
3031
|
-
* new Color,
|
|
3117
|
+
* new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
|
|
3032
3118
|
* new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
|
|
3033
3119
|
* 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
3034
3120
|
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
|
|
@@ -3039,6 +3125,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3039
3125
|
{
|
|
3040
3126
|
/** Create a particle system with the given settings
|
|
3041
3127
|
* @param {Vector2} position - World space position of the emitter
|
|
3128
|
+
* @param {Number} [angle=0] - Angle to emit the particles
|
|
3042
3129
|
* @param {Number} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
3043
3130
|
* @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
|
|
3044
3131
|
* @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
|
|
@@ -3068,6 +3155,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3068
3155
|
constructor
|
|
3069
3156
|
(
|
|
3070
3157
|
pos,
|
|
3158
|
+
angle,
|
|
3071
3159
|
emitSize = 0,
|
|
3072
3160
|
emitTime = 0,
|
|
3073
3161
|
emitRate = 100,
|
|
@@ -3095,7 +3183,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3095
3183
|
renderOrder = additive ? 1e9 : 0
|
|
3096
3184
|
)
|
|
3097
3185
|
{
|
|
3098
|
-
super(pos, new Vector2, tileIndex, tileSize,
|
|
3186
|
+
super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
|
|
3099
3187
|
|
|
3100
3188
|
// emitter settings
|
|
3101
3189
|
/** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
@@ -3328,7 +3416,7 @@ function medalsInit(saveName)
|
|
|
3328
3416
|
{
|
|
3329
3417
|
// check if medals are unlocked
|
|
3330
3418
|
medalsSaveName = saveName;
|
|
3331
|
-
debugMedals || medals.forEach(medal=>
|
|
3419
|
+
debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
|
|
3332
3420
|
}
|
|
3333
3421
|
|
|
3334
3422
|
/**
|
|
@@ -3517,6 +3605,9 @@ class Newgrounds
|
|
|
3517
3605
|
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
3518
3606
|
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
3519
3607
|
debugMedals && console.log(this.scoreboards);
|
|
3608
|
+
|
|
3609
|
+
const keepAliveMS = 5 * 60 * 1e3;
|
|
3610
|
+
setInterval(()=>this.call('Gateway.ping', 0, 1), keepAliveMS);
|
|
3520
3611
|
}
|
|
3521
3612
|
|
|
3522
3613
|
/** Send message to unlock a medal by id
|
|
@@ -3528,9 +3619,6 @@ class Newgrounds
|
|
|
3528
3619
|
* @param {Number} value - The score value */
|
|
3529
3620
|
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, 1); }
|
|
3530
3621
|
|
|
3531
|
-
/** Send message to log a view */
|
|
3532
|
-
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
3533
|
-
|
|
3534
3622
|
/** Get scores from a scoreboard
|
|
3535
3623
|
* @param {Number} id - The scoreboard id
|
|
3536
3624
|
* @param {String} [user=0] - A user's id or name
|
|
@@ -3542,6 +3630,9 @@ class Newgrounds
|
|
|
3542
3630
|
getScores(id, user=0, social=0, skip=0, limit=10)
|
|
3543
3631
|
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
3544
3632
|
|
|
3633
|
+
/** Send message to log a view */
|
|
3634
|
+
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
3635
|
+
|
|
3545
3636
|
/** Send a message to call a component of the Newgrounds API
|
|
3546
3637
|
* @param {String} component - Name of the component
|
|
3547
3638
|
* @param {Object} [parameters=0] - Parameters to use for call
|
|
@@ -3638,24 +3729,24 @@ function glInit()
|
|
|
3638
3729
|
|
|
3639
3730
|
// setup vertex and fragment shaders
|
|
3640
3731
|
glShader = glCreateProgram(
|
|
3641
|
-
'precision highp float;'+
|
|
3642
|
-
'uniform mat4 m;'+
|
|
3643
|
-
'attribute vec2 p,t;'+
|
|
3644
|
-
'attribute vec4 c,a;'+
|
|
3645
|
-
'varying vec2 v;'+
|
|
3646
|
-
'varying vec4 d,e;'+
|
|
3647
|
-
'void main(){'+
|
|
3648
|
-
'gl_Position=m*vec4(p,1,1);'
|
|
3649
|
-
'v=t;d=c;e=a;'+
|
|
3650
|
-
'}'
|
|
3732
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
3733
|
+
'uniform mat4 m;'+ // transform matrix
|
|
3734
|
+
'attribute vec2 p,t;'+ // position, uv
|
|
3735
|
+
'attribute vec4 c,a;'+ // color, additiveColor
|
|
3736
|
+
'varying vec2 v;'+ // return uv
|
|
3737
|
+
'varying vec4 d,e;'+ // return color, additiveColor
|
|
3738
|
+
'void main(){'+ // shader entry point
|
|
3739
|
+
'gl_Position=m*vec4(p,1,1);'+ // transform position
|
|
3740
|
+
'v=t;d=c;e=a;'+ // pass stuff to fragment shader
|
|
3741
|
+
'}' // end of shader
|
|
3651
3742
|
,
|
|
3652
|
-
'precision highp float;'+
|
|
3653
|
-
'varying vec2 v;'+
|
|
3654
|
-
'varying vec4 d,e;'+
|
|
3655
|
-
'uniform sampler2D
|
|
3656
|
-
'void main(){'+
|
|
3657
|
-
'gl_FragColor=texture2D(
|
|
3658
|
-
'}'
|
|
3743
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
3744
|
+
'varying vec2 v;'+ // uv
|
|
3745
|
+
'varying vec4 d,e;'+ // color, additiveColor
|
|
3746
|
+
'uniform sampler2D s;'+ // texture
|
|
3747
|
+
'void main(){'+ // shader entry point
|
|
3748
|
+
'gl_FragColor=texture2D(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
3749
|
+
'}' // end of shader
|
|
3659
3750
|
);
|
|
3660
3751
|
|
|
3661
3752
|
// init buffers
|
|
@@ -3867,44 +3958,44 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
3867
3958
|
|
|
3868
3959
|
// prepare to create the verts from size and angle
|
|
3869
3960
|
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
3870
|
-
const
|
|
3961
|
+
const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
|
|
3871
3962
|
|
|
3872
3963
|
// setup 2 triangles to form a quad
|
|
3873
3964
|
let offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT;
|
|
3874
3965
|
|
|
3875
3966
|
// vertex 0
|
|
3876
|
-
glPositionData[offset++] = x -
|
|
3877
|
-
glPositionData[offset++] = y -
|
|
3967
|
+
glPositionData[offset++] = x - cx - sy;
|
|
3968
|
+
glPositionData[offset++] = y - cy + sx;
|
|
3878
3969
|
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3879
3970
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3880
3971
|
|
|
3881
3972
|
// vertex 1
|
|
3882
|
-
glPositionData[offset++] = x +
|
|
3883
|
-
glPositionData[offset++] = y +
|
|
3973
|
+
glPositionData[offset++] = x + cx + sy;
|
|
3974
|
+
glPositionData[offset++] = y + cy - sx;
|
|
3884
3975
|
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3885
3976
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3886
3977
|
|
|
3887
3978
|
// vertex 2
|
|
3888
|
-
glPositionData[offset++] = x -
|
|
3889
|
-
glPositionData[offset++] = y +
|
|
3979
|
+
glPositionData[offset++] = x - cx + sy;
|
|
3980
|
+
glPositionData[offset++] = y + cy + sx;
|
|
3890
3981
|
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv0Y;
|
|
3891
3982
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3892
3983
|
|
|
3893
3984
|
// vertex 0
|
|
3894
|
-
glPositionData[offset++] = x -
|
|
3895
|
-
glPositionData[offset++] = y -
|
|
3985
|
+
glPositionData[offset++] = x - cx - sy;
|
|
3986
|
+
glPositionData[offset++] = y - cy + sx;
|
|
3896
3987
|
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3897
3988
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3898
3989
|
|
|
3899
3990
|
// vertex 3
|
|
3900
|
-
glPositionData[offset++] = x +
|
|
3901
|
-
glPositionData[offset++] = y -
|
|
3991
|
+
glPositionData[offset++] = x + cx - sy;
|
|
3992
|
+
glPositionData[offset++] = y - cy - sx;
|
|
3902
3993
|
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv1Y;
|
|
3903
3994
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3904
3995
|
|
|
3905
3996
|
// vertex 1
|
|
3906
|
-
glPositionData[offset++] = x +
|
|
3907
|
-
glPositionData[offset++] = y +
|
|
3997
|
+
glPositionData[offset++] = x + cx + sy;
|
|
3998
|
+
glPositionData[offset++] = y + cy - sx;
|
|
3908
3999
|
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3909
4000
|
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3910
4001
|
}
|
|
@@ -3965,7 +4056,7 @@ gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|
|
|
3965
4056
|
const engineName = 'LittleJS';
|
|
3966
4057
|
|
|
3967
4058
|
/** Version of engine */
|
|
3968
|
-
const engineVersion = '1.
|
|
4059
|
+
const engineVersion = '1.3.0';
|
|
3969
4060
|
|
|
3970
4061
|
/** Frames per second to update objects
|
|
3971
4062
|
* @default */
|
|
@@ -4002,7 +4093,7 @@ let averageFPS, drawCount;
|
|
|
4002
4093
|
// css text used for elements created by engine
|
|
4003
4094
|
const styleBody = 'margin:0;overflow:hidden;background:#000' +
|
|
4004
4095
|
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
4005
|
-
const styleCanvas = 'position:absolute';
|
|
4096
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
4006
4097
|
|
|
4007
4098
|
///////////////////////////////////////////////////////////////////////////////
|
|
4008
4099
|
|
|
@@ -4046,8 +4137,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4046
4137
|
// main update loop
|
|
4047
4138
|
const engineUpdate = (frameTimeMS=0)=>
|
|
4048
4139
|
{
|
|
4049
|
-
requestAnimationFrame(engineUpdate);
|
|
4050
|
-
|
|
4051
4140
|
// update time keeping
|
|
4052
4141
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
4053
4142
|
frameTimeLastMS = frameTimeMS;
|
|
@@ -4103,24 +4192,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4103
4192
|
|
|
4104
4193
|
// fit to window by adding space on top or bottom if necessary
|
|
4105
4194
|
const aspect = innerWidth / innerHeight;
|
|
4106
|
-
const fixedAspect =
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
mainCanvas.style.height = overlayCanvas.style.height =
|
|
4111
|
-
mainCanvas.style.top = overlayCanvas.style.top =
|
|
4112
|
-
mainCanvas.style.left = overlayCanvas.style.left = '';
|
|
4113
|
-
|
|
4114
|
-
// letterbox center canvas
|
|
4115
|
-
if (aspect < fixedAspect)
|
|
4195
|
+
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
4196
|
+
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
4197
|
+
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
4198
|
+
if (glCanvas)
|
|
4116
4199
|
{
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
}
|
|
4120
|
-
else
|
|
4121
|
-
{
|
|
4122
|
-
mainCanvas.style.height = overlayCanvas.style.height = innerHeight;
|
|
4123
|
-
mainCanvas.style.left = overlayCanvas.style.left = (innerWidth - innerHeight*fixedAspect)/2;
|
|
4200
|
+
glCanvas.style.width = mainCanvas.style.width;
|
|
4201
|
+
glCanvas.style.height = mainCanvas.style.height;
|
|
4124
4202
|
}
|
|
4125
4203
|
}
|
|
4126
4204
|
else
|
|
@@ -4128,18 +4206,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4128
4206
|
// clear and set size to same as window
|
|
4129
4207
|
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
4130
4208
|
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
4131
|
-
|
|
4132
|
-
// center canvas
|
|
4133
|
-
overlayCanvas.style.top = mainCanvas.style.top = (innerHeight - mainCanvas.height)/2;
|
|
4134
|
-
overlayCanvas.style.left = mainCanvas.style.left = (innerWidth - mainCanvas.width)/2;
|
|
4135
|
-
}
|
|
4136
|
-
|
|
4137
|
-
if (glEnable)
|
|
4138
|
-
{
|
|
4139
|
-
glCanvas.style.width = mainCanvas.style.width;
|
|
4140
|
-
glCanvas.style.height = mainCanvas.style.height;
|
|
4141
|
-
glCanvas.style.top = mainCanvas.style.top;
|
|
4142
|
-
glCanvas.style.left = mainCanvas.style.left;
|
|
4143
4209
|
}
|
|
4144
4210
|
|
|
4145
4211
|
// render sort then render while removing destroyed objects
|
|
@@ -4169,6 +4235,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4169
4235
|
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
4170
4236
|
drawCount = 0;
|
|
4171
4237
|
}
|
|
4238
|
+
|
|
4239
|
+
requestAnimationFrame(engineUpdate);
|
|
4172
4240
|
}
|
|
4173
4241
|
|
|
4174
4242
|
// set tile image source to load the image and start the engine
|