littlejsengine 1.6.92 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -24
- package/build/littlejs.d.ts +87 -60
- package/build/littlejs.esm.js +322 -189
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +316 -185
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +296 -182
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/index.html +2 -2
- package/examples/js13k/index.html +13 -13
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +1 -1
- package/examples/platformer/gameEffects.js +11 -10
- package/examples/platformer/index.html +6 -6
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +4 -4
- package/examples/typescript/index.html +1 -1
- package/package.json +1 -1
- package/src/engine.js +2 -2
- package/src/engineAudio.js +43 -15
- package/src/engineBuild.js +18 -5
- package/src/engineDebug.js +21 -5
- package/src/engineDraw.js +61 -60
- package/src/engineExport.js +6 -4
- package/src/engineInput.js +26 -34
- package/src/engineObject.js +2 -2
- package/src/engineRelease.js +1 -2
- package/src/engineSettings.js +1 -0
- package/src/engineUtilities.js +95 -40
- package/src/engineWebGL.js +63 -27
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
// LittleJS - MIT License - Copyright 2021 Frank Force
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* LittleJS - Release
|
|
3
|
-
* MIT License - Copyright 2021 Frank Force
|
|
4
|
+
* LittleJS - Release Mode
|
|
4
5
|
* - This file is used for release builds in place of engineDebug.js
|
|
5
6
|
* - Debug functionality is disabled to reduce size and increase performance
|
|
6
7
|
*/
|
|
@@ -36,6 +37,7 @@ function debugSaveCanvas (){}
|
|
|
36
37
|
* - Vector2 - fast, simple, easy 2D vector class
|
|
37
38
|
* - Color - holds a rgba color with some math functions
|
|
38
39
|
* - Timer - tracks time automatically
|
|
40
|
+
* - RandomGenerator - seeded random number generator
|
|
39
41
|
* @namespace Utilities
|
|
40
42
|
*/
|
|
41
43
|
|
|
@@ -86,25 +88,58 @@ function mod(dividend, divisor=1) { return ((dividend % divisor) + divisor) % di
|
|
|
86
88
|
* @param {Number} [max=1]
|
|
87
89
|
* @return {Number}
|
|
88
90
|
* @memberof Utilities */
|
|
89
|
-
function clamp(value, min=0, max=1)
|
|
90
|
-
{ return value < min ? min : value > max ? max : value; }
|
|
91
|
+
function clamp(value, min=0, max=1) { return value < min ? min : value > max ? max : value; }
|
|
91
92
|
|
|
92
|
-
/** Returns what percentage the value is between
|
|
93
|
+
/** Returns what percentage the value is between valueA and valueB
|
|
93
94
|
* @param {Number} value
|
|
94
|
-
* @param {Number}
|
|
95
|
-
* @param {Number}
|
|
95
|
+
* @param {Number} valueA
|
|
96
|
+
* @param {Number} valueB
|
|
96
97
|
* @return {Number}
|
|
97
98
|
* @memberof Utilities */
|
|
98
|
-
function percent(value,
|
|
99
|
-
{ return
|
|
99
|
+
function percent(value, valueA, valueB)
|
|
100
|
+
{ return valueB-valueA ? clamp((value-valueA) / (valueB-valueA)) : 0; }
|
|
100
101
|
|
|
101
|
-
/** Linearly interpolates
|
|
102
|
+
/** Linearly interpolates between values passed in using percent
|
|
102
103
|
* @param {Number} percent
|
|
103
|
-
* @param {Number}
|
|
104
|
-
* @param {Number}
|
|
104
|
+
* @param {Number} valueA
|
|
105
|
+
* @param {Number} valueB
|
|
105
106
|
* @return {Number}
|
|
106
107
|
* @memberof Utilities */
|
|
107
|
-
function lerp(percent,
|
|
108
|
+
function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (valueB-valueA); }
|
|
109
|
+
|
|
110
|
+
/** Returns signed wrapped distance between the two values passed in
|
|
111
|
+
* @param {Number} valueA
|
|
112
|
+
* @param {Number} valueB
|
|
113
|
+
* @param {Number} [wrapSize=1]
|
|
114
|
+
* @returns {Number}
|
|
115
|
+
* @memberof Utilities */
|
|
116
|
+
function distanceWrap(valueA, valueB, wrapSize=1)
|
|
117
|
+
{ const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
|
|
118
|
+
|
|
119
|
+
/** Linearly interpolates between values passed in with wrappping
|
|
120
|
+
* @param {Number} percent
|
|
121
|
+
* @param {Number} valueA
|
|
122
|
+
* @param {Number} valueB
|
|
123
|
+
* @param {Number} [wrapSize=1]
|
|
124
|
+
* @returns {Number}
|
|
125
|
+
* @memberof Utilities */
|
|
126
|
+
function lerpWrap(percent, valueA, valueB, wrapSize=1)
|
|
127
|
+
{ return valueB + clamp(percent) * distanceWrap(valueA, valueB, wrapSize); }
|
|
128
|
+
|
|
129
|
+
/** Returns signed wrapped distance between the two angles passed in
|
|
130
|
+
* @param {Number} angleA
|
|
131
|
+
* @param {Number} angleB
|
|
132
|
+
* @returns {Number}
|
|
133
|
+
* @memberof Utilities */
|
|
134
|
+
function distanceAngle(angleA, angleB) { distanceWrap(angleA, angleB, 2*PI); }
|
|
135
|
+
|
|
136
|
+
/** Linearly interpolates between the angles passed in with wrappping
|
|
137
|
+
* @param {Number} percent
|
|
138
|
+
* @param {Number} angleA
|
|
139
|
+
* @param {Number} angleB
|
|
140
|
+
* @returns {Number}
|
|
141
|
+
* @memberof Utilities */
|
|
142
|
+
function lerpAngle(percent, angleA, angleB) { return lerpWrap(percent, angleA, angleB, 2*PI); }
|
|
108
143
|
|
|
109
144
|
/** Applies smoothstep function to the percentage value
|
|
110
145
|
* @param {Number} percent
|
|
@@ -159,17 +194,23 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
|
159
194
|
function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valueB); }
|
|
160
195
|
|
|
161
196
|
/** Returns a floored random value the two values passed in
|
|
162
|
-
* @param {Number}
|
|
197
|
+
* @param {Number} valueA
|
|
163
198
|
* @param {Number} [valueB=0]
|
|
164
199
|
* @return {Number}
|
|
165
200
|
* @memberof Random */
|
|
166
|
-
function randInt(valueA
|
|
201
|
+
function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
|
|
167
202
|
|
|
168
203
|
/** Randomly returns either -1 or 1
|
|
169
204
|
* @return {Number}
|
|
170
205
|
* @memberof Random */
|
|
171
206
|
function randSign() { return randInt(2) * 2 - 1; }
|
|
172
207
|
|
|
208
|
+
/** Returns a random Vector2 with the passed in length
|
|
209
|
+
* @param {Number} [length=1]
|
|
210
|
+
* @return {Vector2}
|
|
211
|
+
* @memberof Random */
|
|
212
|
+
function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
|
|
213
|
+
|
|
173
214
|
/** Returns a random Vector2 within a circular shape
|
|
174
215
|
* @param {Number} [radius=1]
|
|
175
216
|
* @param {Number} [minRadius=0]
|
|
@@ -178,12 +219,6 @@ function randSign() { return randInt(2) * 2 - 1; }
|
|
|
178
219
|
function randInCircle(radius=1, minRadius=0)
|
|
179
220
|
{ return radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
|
|
180
221
|
|
|
181
|
-
/** Returns a random Vector2 with the passed in length
|
|
182
|
-
* @param {Number} [length=1]
|
|
183
|
-
* @return {Vector2}
|
|
184
|
-
* @memberof Random */
|
|
185
|
-
function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
|
|
186
|
-
|
|
187
222
|
/** Returns a random color between the two passed in colors, combine components if linear
|
|
188
223
|
* @param {Color} [colorA=Color()]
|
|
189
224
|
* @param {Color} [colorB=Color(0,0,0,1)]
|
|
@@ -196,29 +231,50 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear)
|
|
|
196
231
|
new Color(rand(colorA.r,colorB.r), rand(colorA.g,colorB.g), rand(colorA.b,colorB.b), rand(colorA.a,colorB.a));
|
|
197
232
|
}
|
|
198
233
|
|
|
199
|
-
|
|
200
|
-
* @type {Number}
|
|
201
|
-
* @default
|
|
202
|
-
* @memberof Random */
|
|
203
|
-
let randSeed = 1;
|
|
204
|
-
|
|
205
|
-
/** Set seed used by the randSeeded function, should not be 0
|
|
206
|
-
* @param {Number} seed
|
|
207
|
-
* @memberof Random */
|
|
208
|
-
function setRandSeed(seed) { randSeed = seed; }
|
|
234
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
209
235
|
|
|
210
|
-
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
|
|
236
|
+
/**
|
|
237
|
+
* Seeded random number generator
|
|
238
|
+
* - Can be used to create a deterministic random number sequence
|
|
239
|
+
* @example
|
|
240
|
+
* let r = new RandomGenerator(123); // random number generator with seed 123
|
|
241
|
+
* let a = r.rand(); // random value between 0 and 1
|
|
242
|
+
* let b = r.randInt(10); // random integer between 0 and 9
|
|
243
|
+
* r.seed = 123; // reset the seed
|
|
244
|
+
* let c = r.rand(); // the same value as a
|
|
245
|
+
*/
|
|
246
|
+
class RandomGenerator
|
|
216
247
|
{
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
248
|
+
/** Create a random number generator with the seed passed in
|
|
249
|
+
* @param {Number} seed - Starting seed */
|
|
250
|
+
constructor(seed)
|
|
251
|
+
{
|
|
252
|
+
/** @property {Number} - random seed */
|
|
253
|
+
this.seed = seed;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Returns a seeded random value between the two values passed in
|
|
257
|
+
* @param {Number} [valueA=1]
|
|
258
|
+
* @param {Number} [valueB=0]
|
|
259
|
+
* @return {Number} */
|
|
260
|
+
float(valueA=1, valueB=0)
|
|
261
|
+
{
|
|
262
|
+
// xorshift algorithm
|
|
263
|
+
this.seed ^= this.seed << 13;
|
|
264
|
+
this.seed ^= this.seed >>> 17;
|
|
265
|
+
this.seed ^= this.seed << 5;
|
|
266
|
+
return valueB + (valueA - valueB) * abs(this.seed % 1e9) / 1e9;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Returns a floored seeded random value the two values passed in
|
|
270
|
+
* @param {Number} valueA
|
|
271
|
+
* @param {Number} [valueB=0]
|
|
272
|
+
* @return {Number} */
|
|
273
|
+
int(valueA, valueB=0) { return Math.floor(this.rand(valueA, valueB)); }
|
|
274
|
+
|
|
275
|
+
/** Randomly returns either -1 or 1 deterministically
|
|
276
|
+
* @return {Number} */
|
|
277
|
+
sign() { return this.randInt(2) * 2 - 1; }
|
|
222
278
|
}
|
|
223
279
|
|
|
224
280
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -636,6 +692,7 @@ class Timer
|
|
|
636
692
|
}
|
|
637
693
|
/**
|
|
638
694
|
* LittleJS Engine Settings
|
|
695
|
+
* - All settings for the engine are here
|
|
639
696
|
* @namespace Settings
|
|
640
697
|
*/
|
|
641
698
|
|
|
@@ -891,12 +948,12 @@ let medalsPreventUnlock;
|
|
|
891
948
|
|
|
892
949
|
/**
|
|
893
950
|
* LittleJS Object Base Object Class
|
|
894
|
-
* -
|
|
951
|
+
* - Top level object class used by the engine
|
|
895
952
|
* - Automatically adds self to object list
|
|
896
953
|
* - Will be updated and rendered each frame
|
|
897
954
|
* - Renders as a sprite from a tilesheet by default
|
|
898
955
|
* - Can have color and addtive color applied
|
|
899
|
-
* -
|
|
956
|
+
* - 2D Physics and collision system
|
|
900
957
|
* - Sorted by renderOrder
|
|
901
958
|
* - Objects can have children attached
|
|
902
959
|
* - Parents are updated before children, and set child transform
|
|
@@ -1263,9 +1320,10 @@ class EngineObject
|
|
|
1263
1320
|
}
|
|
1264
1321
|
/**
|
|
1265
1322
|
* LittleJS Drawing System
|
|
1266
|
-
* - Hybrid with both Canvas2D and WebGL available
|
|
1323
|
+
* - Hybrid system with both Canvas2D and WebGL available
|
|
1267
1324
|
* - Super fast tile sheet rendering with WebGL
|
|
1268
1325
|
* - Can apply rotation, mirror, color and additive color
|
|
1326
|
+
* - Font rendering system with built in engine font
|
|
1269
1327
|
* - Many useful utility functions
|
|
1270
1328
|
*
|
|
1271
1329
|
* LittleJS uses a hybrid rendering solution with the best of both Canvas2D and WebGL.
|
|
@@ -1275,7 +1333,6 @@ class EngineObject
|
|
|
1275
1333
|
* overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
|
|
1276
1334
|
*
|
|
1277
1335
|
* The WebGL rendering system is very fast with some caveats...
|
|
1278
|
-
* - The default setup supports only 1 tile sheet, to support more call glCreateTexture and glSetTexture
|
|
1279
1336
|
* - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
|
|
1280
1337
|
* - Group additive rendering together using renderOrder to mitigate this issue
|
|
1281
1338
|
*
|
|
@@ -1319,33 +1376,29 @@ const tileImage = new Image;
|
|
|
1319
1376
|
let tileImageSize, tileImageFixBleed, drawCount;
|
|
1320
1377
|
|
|
1321
1378
|
/** Convert from screen to world space coordinates
|
|
1322
|
-
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
1323
1379
|
* @param {Vector2} screenPos
|
|
1324
1380
|
* @return {Vector2}
|
|
1325
1381
|
* @memberof Draw */
|
|
1326
1382
|
function screenToWorld(screenPos)
|
|
1327
1383
|
{
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
.
|
|
1332
|
-
|
|
1333
|
-
.add(cameraPos);
|
|
1384
|
+
return new Vector2
|
|
1385
|
+
(
|
|
1386
|
+
(screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale + cameraPos.x,
|
|
1387
|
+
(screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale + cameraPos.y
|
|
1388
|
+
);
|
|
1334
1389
|
}
|
|
1335
1390
|
|
|
1336
1391
|
/** Convert from world to screen space coordinates
|
|
1337
|
-
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
1338
1392
|
* @param {Vector2} worldPos
|
|
1339
1393
|
* @return {Vector2}
|
|
1340
1394
|
* @memberof Draw */
|
|
1341
1395
|
function worldToScreen(worldPos)
|
|
1342
1396
|
{
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
.
|
|
1346
|
-
.
|
|
1347
|
-
|
|
1348
|
-
.subtract(vec2(.5));
|
|
1397
|
+
return new Vector2
|
|
1398
|
+
(
|
|
1399
|
+
(worldPos.x - cameraPos.x) * cameraScale + mainCanvasSize.x/2 - .5,
|
|
1400
|
+
(worldPos.y - cameraPos.y) * -cameraScale + mainCanvasSize.y/2 - .5
|
|
1401
|
+
);
|
|
1349
1402
|
}
|
|
1350
1403
|
|
|
1351
1404
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
@@ -1358,13 +1411,21 @@ function worldToScreen(worldPos)
|
|
|
1358
1411
|
* @param {Boolean} [mirror=0] - If true image is flipped along the Y axis
|
|
1359
1412
|
* @param {Color} [additiveColor=Color(0,0,0,0)] - Additive color to be applied
|
|
1360
1413
|
* @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
1414
|
+
* @param {Boolean} [screenSpace=0] - If true the pos and size are in screen space
|
|
1361
1415
|
* @memberof Draw */
|
|
1362
1416
|
function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, color=new Color,
|
|
1363
|
-
angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable)
|
|
1417
|
+
angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable, screenSpace)
|
|
1364
1418
|
{
|
|
1365
1419
|
showWatermark && ++drawCount;
|
|
1420
|
+
|
|
1366
1421
|
if (glEnable && useWebGL)
|
|
1367
1422
|
{
|
|
1423
|
+
if (screenSpace)
|
|
1424
|
+
{
|
|
1425
|
+
// convert to world space
|
|
1426
|
+
pos = screenToWorld(pos);
|
|
1427
|
+
size = size.scale(1/cameraScale);
|
|
1428
|
+
}
|
|
1368
1429
|
if (tileIndex < 0 || !tileImage.width)
|
|
1369
1430
|
{
|
|
1370
1431
|
// if negative tile index or image not found, force untextured
|
|
@@ -1406,7 +1467,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
1406
1467
|
context.globalAlpha = color.a; // only alpha is supported
|
|
1407
1468
|
context.drawImage(tileImage, sX, sY, sWidth, sHeight, -.5, -.5, 1, 1);
|
|
1408
1469
|
}
|
|
1409
|
-
});
|
|
1470
|
+
}, undefined, screenSpace);
|
|
1410
1471
|
}
|
|
1411
1472
|
}
|
|
1412
1473
|
|
|
@@ -1416,38 +1477,30 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
1416
1477
|
* @param {Color} [color=Color()]
|
|
1417
1478
|
* @param {Number} [angle=0]
|
|
1418
1479
|
* @param {Boolean} [useWebGL=glEnable]
|
|
1480
|
+
* @param {Boolean} [screenSpace=0]
|
|
1419
1481
|
* @memberof Draw */
|
|
1420
|
-
function drawRect(pos, size, color, angle, useWebGL)
|
|
1421
|
-
{
|
|
1422
|
-
drawTile(pos, size, -1, tileSizeDefault, color, angle, 0, 0, useWebGL);
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
/** Draw textured tile centered on pos in screen space
|
|
1426
|
-
* @param {Vector2} pos - Center of the tile
|
|
1427
|
-
* @param {Vector2} [size=Vector2(1,1)] - Size of the tile
|
|
1428
|
-
* @param {Number} [tileIndex=-1] - Tile index to use, negative is untextured
|
|
1429
|
-
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
|
|
1430
|
-
* @param {Color} [color=Color()]
|
|
1431
|
-
* @param {Number} [angle=0]
|
|
1432
|
-
* @param {Boolean} [mirror=0]
|
|
1433
|
-
* @param {Color} [additiveColor=Color(0,0,0,0)]
|
|
1434
|
-
* @param {Boolean} [useWebGL=glEnable]
|
|
1435
|
-
* @memberof Draw */
|
|
1436
|
-
function drawTileScreenSpace(pos, size=vec2(1), tileIndex, tileSize, color, angle, mirror, additiveColor, useWebGL)
|
|
1437
|
-
{
|
|
1438
|
-
drawTile(screenToWorld(pos), size.scale(1/cameraScale), tileIndex, tileSize, color, angle, mirror, additiveColor, useWebGL);
|
|
1439
|
-
}
|
|
1482
|
+
function drawRect(pos, size, color, angle, useWebGL, screenSpace)
|
|
1483
|
+
{ drawTile(pos, size, -1, tileSizeDefault, color, angle, 0, undefined, useWebGL, screenSpace); }
|
|
1440
1484
|
|
|
1441
|
-
/** Draw colored
|
|
1442
|
-
* @param {
|
|
1443
|
-
* @param {Vector2} [size=Vector2(1,1)]
|
|
1485
|
+
/** Draw colored polygon using passed in points
|
|
1486
|
+
* @param {Array} points - Array of Vector2 points
|
|
1444
1487
|
* @param {Color} [color=Color()]
|
|
1445
|
-
* @param {Number} [angle=0]
|
|
1446
1488
|
* @param {Boolean} [useWebGL=glEnable]
|
|
1489
|
+
* @param {Boolean} [screenSpace=0]
|
|
1447
1490
|
* @memberof Draw */
|
|
1448
|
-
function
|
|
1491
|
+
function drawPoly(points, color=new Color, useWebGL=glEnable, screenSpace)
|
|
1449
1492
|
{
|
|
1450
|
-
|
|
1493
|
+
if (useWebGL)
|
|
1494
|
+
glDrawPoints(screenSpace ? points.map(screenToWorld) : points, color.rgbaInt());
|
|
1495
|
+
else
|
|
1496
|
+
{
|
|
1497
|
+
// draw using canvas
|
|
1498
|
+
mainContext.fillStyle = color;
|
|
1499
|
+
mainContext.beginPath();
|
|
1500
|
+
for (const point of screenSpace ? points : points.map(worldToScreen))
|
|
1501
|
+
mainContext.lineTo(point.x, point.y);
|
|
1502
|
+
mainContext.fill();
|
|
1503
|
+
}
|
|
1451
1504
|
}
|
|
1452
1505
|
|
|
1453
1506
|
/** Draw colored line between two points
|
|
@@ -1456,12 +1509,13 @@ function drawRectScreenSpace(pos, size, color, angle, useWebGL)
|
|
|
1456
1509
|
* @param {Number} [thickness=.1]
|
|
1457
1510
|
* @param {Color} [color=Color()]
|
|
1458
1511
|
* @param {Boolean} [useWebGL=glEnable]
|
|
1512
|
+
* @param {Boolean} [screenSpace=0]
|
|
1459
1513
|
* @memberof Draw */
|
|
1460
1514
|
function drawLine(posA, posB, thickness=.1, color, useWebGL)
|
|
1461
1515
|
{
|
|
1462
1516
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
1463
1517
|
const size = vec2(thickness, halfDelta.length()*2);
|
|
1464
|
-
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL);
|
|
1518
|
+
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL, screenSpace);
|
|
1465
1519
|
}
|
|
1466
1520
|
|
|
1467
1521
|
/** Draw directly to a 2d canvas context in world space
|
|
@@ -1471,12 +1525,16 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL)
|
|
|
1471
1525
|
* @param {Boolean} mirror
|
|
1472
1526
|
* @param {Function} drawFunction
|
|
1473
1527
|
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
1528
|
+
* @param {Boolean} [screenSpace=0]
|
|
1474
1529
|
* @memberof Draw */
|
|
1475
|
-
function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainContext)
|
|
1530
|
+
function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainContext, screenSpace)
|
|
1476
1531
|
{
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1532
|
+
if (!screenSpace)
|
|
1533
|
+
{
|
|
1534
|
+
// create canvas transform from world space to screen space
|
|
1535
|
+
pos = worldToScreen(pos);
|
|
1536
|
+
size = size.scale(cameraScale);
|
|
1537
|
+
}
|
|
1480
1538
|
context.save();
|
|
1481
1539
|
context.translate(pos.x+.5|0, pos.y+.5|0);
|
|
1482
1540
|
context.rotate(angle);
|
|
@@ -1583,6 +1641,17 @@ class FontImage
|
|
|
1583
1641
|
this.context = context;
|
|
1584
1642
|
}
|
|
1585
1643
|
|
|
1644
|
+
/** Draw text in world space using the image font
|
|
1645
|
+
* @param {String} text
|
|
1646
|
+
* @param {Vector2} pos
|
|
1647
|
+
* @param {Number} [scale=.25]
|
|
1648
|
+
* @param {Boolean} [center]
|
|
1649
|
+
*/
|
|
1650
|
+
drawText(text, pos, scale=1, center)
|
|
1651
|
+
{
|
|
1652
|
+
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1586
1655
|
/** Draw text in screen space using the image font
|
|
1587
1656
|
* @param {String} text
|
|
1588
1657
|
* @param {Vector2} pos
|
|
@@ -1620,17 +1689,6 @@ class FontImage
|
|
|
1620
1689
|
|
|
1621
1690
|
context.restore();
|
|
1622
1691
|
}
|
|
1623
|
-
|
|
1624
|
-
/** Draw text in world space using the image font
|
|
1625
|
-
* @param {String} text
|
|
1626
|
-
* @param {Vector2} pos
|
|
1627
|
-
* @param {Number} [scale=.25]
|
|
1628
|
-
* @param {Boolean} [center]
|
|
1629
|
-
*/
|
|
1630
|
-
drawText(text, pos, scale=1, center)
|
|
1631
|
-
{
|
|
1632
|
-
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
|
|
1633
|
-
}
|
|
1634
1692
|
}
|
|
1635
1693
|
|
|
1636
1694
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1656,10 +1714,10 @@ function toggleFullscreen()
|
|
|
1656
1714
|
|
|
1657
1715
|
/**
|
|
1658
1716
|
* LittleJS Input System
|
|
1659
|
-
* - Tracks
|
|
1660
|
-
* -
|
|
1661
|
-
* -
|
|
1662
|
-
* - Virtual gamepad for touch devices
|
|
1717
|
+
* - Tracks keyboard down, pressed, and released
|
|
1718
|
+
* - Tracks mouse buttons, position, and wheel
|
|
1719
|
+
* - Tracks multiple analog gamepads
|
|
1720
|
+
* - Virtual gamepad for touch devices
|
|
1663
1721
|
* @namespace Input
|
|
1664
1722
|
*/
|
|
1665
1723
|
|
|
@@ -1931,40 +1989,32 @@ if (isTouchDevice)
|
|
|
1931
1989
|
let wasTouching, mouseDown = onmousedown, mouseUp = onmouseup;
|
|
1932
1990
|
onmousedown = onmouseup = ()=> 0;
|
|
1933
1991
|
|
|
1934
|
-
//
|
|
1935
|
-
ontouchstart = (e)=>
|
|
1992
|
+
// handle all touch events the same way
|
|
1993
|
+
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
1936
1994
|
{
|
|
1937
|
-
|
|
1938
|
-
zzfx(0);
|
|
1939
|
-
|
|
1940
|
-
// handle all touch events the same way
|
|
1941
|
-
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
1942
|
-
{
|
|
1943
|
-
e.button = 0; // all touches are left click
|
|
1995
|
+
e.button = 0; // all touches are left click
|
|
1944
1996
|
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
{
|
|
1949
|
-
// set event pos and pass it along
|
|
1950
|
-
e.x = e.touches[0].clientX;
|
|
1951
|
-
e.y = e.touches[0].clientY;
|
|
1952
|
-
wasTouching ? onmousemove(e) : mouseDown(e);
|
|
1953
|
-
}
|
|
1954
|
-
else if (wasTouching)
|
|
1955
|
-
mouseUp(e);
|
|
1997
|
+
// fix stalled audio on mobile
|
|
1998
|
+
if (soundEnable)
|
|
1999
|
+
audioContext ? audioContext.resume() : zzfx(0);
|
|
1956
2000
|
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
2001
|
+
// check if touching and pass to mouse events
|
|
2002
|
+
const touching = e.touches.length;
|
|
2003
|
+
if (touching)
|
|
2004
|
+
{
|
|
2005
|
+
// set event pos and pass it along
|
|
2006
|
+
e.x = e.touches[0].clientX;
|
|
2007
|
+
e.y = e.touches[0].clientY;
|
|
2008
|
+
wasTouching ? onmousemove(e) : mouseDown(e);
|
|
1962
2009
|
}
|
|
2010
|
+
else if (wasTouching)
|
|
2011
|
+
mouseUp(e);
|
|
1963
2012
|
|
|
1964
|
-
//
|
|
1965
|
-
|
|
2013
|
+
// set was touching
|
|
2014
|
+
wasTouching = touching;
|
|
1966
2015
|
|
|
1967
|
-
return
|
|
2016
|
+
// must return true so the document will get focus
|
|
2017
|
+
return true;
|
|
1968
2018
|
}
|
|
1969
2019
|
}
|
|
1970
2020
|
|
|
@@ -1975,13 +2025,13 @@ if (isTouchDevice)
|
|
|
1975
2025
|
let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
|
1976
2026
|
|
|
1977
2027
|
// create the touch gamepad, called automatically by the engine
|
|
1978
|
-
|
|
2028
|
+
if (touchGamepadEnable)
|
|
1979
2029
|
{
|
|
1980
2030
|
// touch input internal variables
|
|
1981
2031
|
touchGamepadButtons = [];
|
|
1982
2032
|
touchGamepadStick = vec2();
|
|
1983
2033
|
|
|
1984
|
-
|
|
2034
|
+
const touchHandler = ontouchstart;
|
|
1985
2035
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
1986
2036
|
{
|
|
1987
2037
|
// clear touch gamepad input
|
|
@@ -2100,12 +2150,12 @@ function touchGamepadRender()
|
|
|
2100
2150
|
}
|
|
2101
2151
|
/**
|
|
2102
2152
|
* LittleJS Audio System
|
|
2103
|
-
* - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a> - Sound Effect Generator
|
|
2104
|
-
* - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a> - Music System
|
|
2153
|
+
* - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a> - ZzFX Sound Effect Generator
|
|
2154
|
+
* - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a> - ZzFXM Music System
|
|
2105
2155
|
* - Caches sounds and music for fast playback
|
|
2106
2156
|
* - Can attenuate and apply stereo panning to sounds
|
|
2107
2157
|
* - Ability to play mp3, ogg, and wave files
|
|
2108
|
-
* - Speech synthesis
|
|
2158
|
+
* - Speech synthesis functions
|
|
2109
2159
|
* @namespace Audio
|
|
2110
2160
|
*/
|
|
2111
2161
|
|
|
@@ -2139,12 +2189,15 @@ class Sound
|
|
|
2139
2189
|
/** @property {Number} - At what percentage of range should it start tapering off */
|
|
2140
2190
|
this.taper = taper;
|
|
2141
2191
|
|
|
2142
|
-
|
|
2143
|
-
this.randomness =
|
|
2144
|
-
zzfxSound[1] = 0;
|
|
2192
|
+
/** @property {Number} - How much to randomize frequency each time sound plays */
|
|
2193
|
+
this.randomness = 0;
|
|
2145
2194
|
|
|
2146
|
-
|
|
2147
|
-
|
|
2195
|
+
if (zzfxSound)
|
|
2196
|
+
{
|
|
2197
|
+
// generate zzfx sound now for fast playback
|
|
2198
|
+
this.randomness = zzfxSound[1] || (zzfxSound[1] = 0);
|
|
2199
|
+
this.cachedSamples = zzfxSound && zzfxG(...zzfxSound);
|
|
2200
|
+
}
|
|
2148
2201
|
}
|
|
2149
2202
|
|
|
2150
2203
|
/** Play the sound
|
|
@@ -2156,7 +2209,7 @@ class Sound
|
|
|
2156
2209
|
*/
|
|
2157
2210
|
play(pos, volume=1, pitch=1, randomnessScale=1)
|
|
2158
2211
|
{
|
|
2159
|
-
if (!soundEnable) return;
|
|
2212
|
+
if (!soundEnable || !this.cachedSamples) return;
|
|
2160
2213
|
|
|
2161
2214
|
let pan;
|
|
2162
2215
|
if (pos)
|
|
@@ -2189,12 +2242,36 @@ class Sound
|
|
|
2189
2242
|
* @return {AudioBufferSourceNode} - The audio, can be used to stop sound later
|
|
2190
2243
|
*/
|
|
2191
2244
|
playNote(semitoneOffset, pos, volume)
|
|
2245
|
+
{ return this.play(pos, volume, 2**(semitoneOffset/12), 0); }
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
/**
|
|
2249
|
+
* Sound Wave Object - Stores a wave sound for later use and can be played positionally
|
|
2250
|
+
*/
|
|
2251
|
+
class SoundWave extends Sound
|
|
2252
|
+
{
|
|
2253
|
+
/** Create a sound object and cache the wave file for later use
|
|
2254
|
+
* @param {String} waveFilename - Filename of wave file to load
|
|
2255
|
+
* @param {Number} [randomness=.05] - How much to randomize frequency each time sound plays
|
|
2256
|
+
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
2257
|
+
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
|
|
2258
|
+
*/
|
|
2259
|
+
constructor(waveFilename, randomness=.05, range, taper)
|
|
2192
2260
|
{
|
|
2261
|
+
super(0, range, taper);
|
|
2262
|
+
this.randomness = randomness;
|
|
2263
|
+
|
|
2193
2264
|
if (!soundEnable) return;
|
|
2265
|
+
if (!soundWaveDecoderContext)
|
|
2266
|
+
soundDecoderContext = new AudioContext;
|
|
2194
2267
|
|
|
2195
|
-
|
|
2268
|
+
fetch(waveFilename)
|
|
2269
|
+
.then(response => response.arrayBuffer())
|
|
2270
|
+
.then(arrayBuffer => soundWaveDecoderContext.decodeAudioData(arrayBuffer))
|
|
2271
|
+
.then(audioBuffer => this.cachedSamples = audioBuffer.getChannelData(0));
|
|
2196
2272
|
}
|
|
2197
2273
|
}
|
|
2274
|
+
let soundDecoderContext; // audio context used only to decode audio files
|
|
2198
2275
|
|
|
2199
2276
|
/**
|
|
2200
2277
|
* Music Object - Stores a zzfx music track for later use
|
|
@@ -2337,16 +2414,17 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
2337
2414
|
{
|
|
2338
2415
|
if (!soundEnable) return;
|
|
2339
2416
|
|
|
2340
|
-
// create audio context
|
|
2417
|
+
// create audio context if needed
|
|
2341
2418
|
if (!audioContext)
|
|
2342
2419
|
audioContext = new AudioContext;
|
|
2343
2420
|
|
|
2344
|
-
// fix stalled audio
|
|
2345
|
-
audioContext.resume();
|
|
2346
|
-
|
|
2347
2421
|
// prevent sounds from building up if they can't be played
|
|
2348
2422
|
if (audioContext.state != 'running')
|
|
2423
|
+
{
|
|
2424
|
+
// fix stalled audio
|
|
2425
|
+
audioContext.resume();
|
|
2349
2426
|
return;
|
|
2427
|
+
}
|
|
2350
2428
|
|
|
2351
2429
|
// create buffer and source
|
|
2352
2430
|
const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length, zzfxR),
|
|
@@ -2551,7 +2629,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
2551
2629
|
|
|
2552
2630
|
// stop if end, different instrument or new note
|
|
2553
2631
|
stop = i == patternChannel.length + isSequenceEnd - 1 && isSequenceEnd ||
|
|
2554
|
-
instrument != (patternChannel[0] || 0) || note;
|
|
2632
|
+
instrument != (patternChannel[0] || 0) || note | 0;
|
|
2555
2633
|
|
|
2556
2634
|
// fill buffer with samples for previous beat, most cpu intensive part
|
|
2557
2635
|
for (j = 0; j < beatLength && notFirstBeat;
|
|
@@ -3789,8 +3867,8 @@ function glFlush()
|
|
|
3789
3867
|
|
|
3790
3868
|
// draw all the sprites in the batch and reset the buffer
|
|
3791
3869
|
glContext.bufferSubData(gl_ARRAY_BUFFER, 0,
|
|
3792
|
-
glPositionData.subarray(0, glBatchCount *
|
|
3793
|
-
glContext.drawArrays(
|
|
3870
|
+
glPositionData.subarray(0, glBatchCount * gl_INDICIES_PER_VERT));
|
|
3871
|
+
glContext.drawArrays(gl_TRIANGLE_STRIP, 0, glBatchCount);
|
|
3794
3872
|
glBatchCount = 0;
|
|
3795
3873
|
glBatchAdditive = glAdditive;
|
|
3796
3874
|
}
|
|
@@ -3811,39 +3889,75 @@ function glCopyToContext(context, forceDraw)
|
|
|
3811
3889
|
}
|
|
3812
3890
|
|
|
3813
3891
|
/** Add a sprite to the gl draw list, used by all gl draw functions
|
|
3814
|
-
* @param x
|
|
3815
|
-
* @param y
|
|
3816
|
-
* @param sizeX
|
|
3817
|
-
* @param sizeY
|
|
3818
|
-
* @param angle
|
|
3819
|
-
* @param uv0X
|
|
3820
|
-
* @param uv0Y
|
|
3821
|
-
* @param uv1X
|
|
3822
|
-
* @param uv1Y
|
|
3823
|
-
* @param rgba
|
|
3824
|
-
* @param [rgbaAdditive=0]
|
|
3892
|
+
* @param {Number} x
|
|
3893
|
+
* @param {Number} y
|
|
3894
|
+
* @param {Number} sizeX
|
|
3895
|
+
* @param {Number} sizeY
|
|
3896
|
+
* @param {Number} angle
|
|
3897
|
+
* @param {Number} uv0X
|
|
3898
|
+
* @param {Number} uv0Y
|
|
3899
|
+
* @param {Number} uv1X
|
|
3900
|
+
* @param {Number} uv1Y
|
|
3901
|
+
* @param {Number} rgba
|
|
3902
|
+
* @param {Number} [rgbaAdditive=0]
|
|
3825
3903
|
* @memberof WebGL */
|
|
3826
3904
|
function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdditive=0)
|
|
3827
3905
|
{
|
|
3828
|
-
// flush if there is
|
|
3829
|
-
|
|
3906
|
+
// flush if there is not enough room or if different blend mode
|
|
3907
|
+
const vertCount = 6;
|
|
3908
|
+
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
3830
3909
|
glFlush();
|
|
3831
3910
|
|
|
3832
3911
|
// prepare to create the verts from size and angle
|
|
3833
3912
|
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
3834
3913
|
const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3914
|
+
const positionData =
|
|
3915
|
+
[
|
|
3916
|
+
x-cx+sy, y+cy+sx, uv0X, uv0Y,
|
|
3917
|
+
x-cx-sy, y-cy+sx, uv0X, uv1Y,
|
|
3918
|
+
x+cx+sy, y+cy-sx, uv1X, uv0Y,
|
|
3919
|
+
x+cx-sy, y-cy-sx, uv1X, uv1Y,
|
|
3920
|
+
];
|
|
3921
|
+
|
|
3922
|
+
// setup 2 triangle strip quad
|
|
3923
|
+
for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
|
|
3838
3924
|
{
|
|
3839
|
-
|
|
3840
|
-
glPositionData[offset++] =
|
|
3841
|
-
glPositionData[offset++] =
|
|
3842
|
-
glPositionData[offset++] =
|
|
3843
|
-
glPositionData[offset++] =
|
|
3925
|
+
let j = clamp(i-1, 0, 3)*4; // degenerate tri at ends
|
|
3926
|
+
glPositionData[offset++] = positionData[j++];
|
|
3927
|
+
glPositionData[offset++] = positionData[j++];
|
|
3928
|
+
glPositionData[offset++] = positionData[j++];
|
|
3929
|
+
glPositionData[offset++] = positionData[j++];
|
|
3844
3930
|
glColorData[offset++] = rgba;
|
|
3845
3931
|
glColorData[offset++] = rgbaAdditive;
|
|
3846
3932
|
}
|
|
3933
|
+
glBatchCount += vertCount;
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3936
|
+
/** Add a convex polygon to the gl draw list
|
|
3937
|
+
* @param {Array} points - Array of Vector2 points
|
|
3938
|
+
* @param {Number} rgba - Color of the polygon
|
|
3939
|
+
* @memberof WebGL */
|
|
3940
|
+
function glDrawPoints(points, rgba)
|
|
3941
|
+
{
|
|
3942
|
+
// flush if there is not enough room or if different blend mode
|
|
3943
|
+
const vertCount = points.length + 2;
|
|
3944
|
+
if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
|
|
3945
|
+
glFlush();
|
|
3946
|
+
|
|
3947
|
+
// setup triangle strip from list of points
|
|
3948
|
+
for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
|
|
3949
|
+
{
|
|
3950
|
+
const j = clamp(i-1, 0, vertCount-3); // degenerate tri at ends
|
|
3951
|
+
const h = j>>1;
|
|
3952
|
+
const point = points[j%2? h : vertCount-3-h];
|
|
3953
|
+
glPositionData[offset++] = point.x;
|
|
3954
|
+
glPositionData[offset++] = point.y;
|
|
3955
|
+
glPositionData[offset++] = 0; // uvx
|
|
3956
|
+
glPositionData[offset++] = 0; // uvy
|
|
3957
|
+
glColorData[offset++] = 0; // nothing to tint
|
|
3958
|
+
glColorData[offset++] = rgba; // apply rgba via additive
|
|
3959
|
+
}
|
|
3960
|
+
glBatchCount += vertCount;
|
|
3847
3961
|
}
|
|
3848
3962
|
|
|
3849
3963
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -3937,14 +4051,14 @@ function glRenderPostProcess()
|
|
|
3937
4051
|
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
3938
4052
|
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
3939
4053
|
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
3940
|
-
glContext.drawArrays(
|
|
4054
|
+
glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 3);
|
|
3941
4055
|
}
|
|
3942
4056
|
|
|
3943
4057
|
///////////////////////////////////////////////////////////////////////////////
|
|
3944
4058
|
// store gl constants as integers so their name doesn't use space in minifed
|
|
3945
4059
|
const
|
|
3946
4060
|
gl_ONE = 1,
|
|
3947
|
-
|
|
4061
|
+
gl_TRIANGLE_STRIP = 5,
|
|
3948
4062
|
gl_SRC_ALPHA = 770,
|
|
3949
4063
|
gl_ONE_MINUS_SRC_ALPHA = 771,
|
|
3950
4064
|
gl_BLEND = 3042,
|
|
@@ -3975,9 +4089,9 @@ gl_UNPACK_FLIP_Y_WEBGL = 37440,
|
|
|
3975
4089
|
// constants for batch rendering
|
|
3976
4090
|
gl_VERTICES_PER_QUAD = 6,
|
|
3977
4091
|
gl_INDICIES_PER_VERT = 6,
|
|
3978
|
-
gl_MAX_BATCH =
|
|
4092
|
+
gl_MAX_BATCH = 1e5,
|
|
3979
4093
|
gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2, // vec2 * 2 + (char * 4) * 2
|
|
3980
|
-
gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH *
|
|
4094
|
+
gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTEX_BYTE_STRIDE;
|
|
3981
4095
|
/**
|
|
3982
4096
|
* LittleJS - The Tiny JavaScript Game Engine That Can!
|
|
3983
4097
|
* MIT License - Copyright 2021 Frank Force
|
|
@@ -4010,7 +4124,7 @@ const engineName = 'LittleJS';
|
|
|
4010
4124
|
* @type {String}
|
|
4011
4125
|
* @default
|
|
4012
4126
|
* @memberof Engine */
|
|
4013
|
-
const engineVersion = '1.
|
|
4127
|
+
const engineVersion = '1.7.01';
|
|
4014
4128
|
|
|
4015
4129
|
/** Frames per second to update objects
|
|
4016
4130
|
* @type {Number}
|
|
@@ -4108,7 +4222,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4108
4222
|
};
|
|
4109
4223
|
|
|
4110
4224
|
// frame time tracking
|
|
4111
|
-
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS;
|
|
4225
|
+
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
4112
4226
|
|
|
4113
4227
|
// main update loop
|
|
4114
4228
|
function engineUpdate(frameTimeMS=0)
|