littlejsengine 1.17.1 → 1.17.10
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/LICENSE +10 -10
- package/dist/littlejs.d.ts +254 -171
- package/dist/littlejs.esm.js +892 -628
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +889 -626
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +885 -622
- package/examples/box2d/gameObjects.js +3 -3
- package/examples/electron/index.html +2 -2
- package/examples/index.html +10 -9
- package/examples/logo.png +0 -0
- package/examples/logo2.png +0 -0
- package/examples/module/build.bat +7 -0
- package/examples/module/build.js +121 -0
- package/examples/particles/index.html +1 -1
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameEffects.js +5 -10
- package/examples/platformer/gameLevel.js +3 -3
- package/examples/puzzle/game.js +2 -2
- package/examples/shorts/base.html +4 -1
- package/examples/shorts/box2dPool.js +2 -2
- package/examples/shorts/box2dTileLayer.js +48 -0
- package/examples/shorts/colors.js +1 -1
- package/examples/shorts/fps.js +1 -1
- package/examples/shorts/music.js +5 -4
- package/examples/shorts/musicPlayer.js +4 -3
- package/examples/shorts/parallax.js +2 -2
- package/examples/shorts/tiltedView.js +1 -1
- package/examples/starter/index.html +2 -2
- package/package.json +5 -5
- package/plugins/box2d.js +166 -63
- package/plugins/pluginExport.js +1 -1
- package/plugins/postProcess.js +1 -1
- package/plugins/uiSystem.js +33 -20
- package/plugins/zzfxm.js +2 -2
- package/reference.md +9 -8
- package/src/engine.js +111 -134
- package/src/engineAudio.js +44 -58
- package/src/engineDebug.js +4 -4
- package/src/engineDraw.js +7 -4
- package/src/engineExport.js +2 -1
- package/src/engineInput.js +5 -5
- package/src/engineMath.js +9 -9
- package/src/engineObject.js +25 -25
- package/src/engineParticles.js +241 -118
- package/src/engineTileLayer.js +117 -88
- package/src/engineUtilities.js +105 -78
- package/src/engineWebGL.js +2 -2
- package/plugins/Box2D_License.txt +0 -17
package/src/engineTileLayer.js
CHANGED
|
@@ -19,50 +19,62 @@ const tileCollisionLayers = [];
|
|
|
19
19
|
|
|
20
20
|
/** Get tile collision data for a given cell in the grid
|
|
21
21
|
* @param {Vector2} pos
|
|
22
|
+
* @param {boolean} [solidOnly] - Only check solid layers?
|
|
22
23
|
* @return {number}
|
|
23
24
|
* @memberof TileLayers */
|
|
24
|
-
function tileCollisionGetData(pos)
|
|
25
|
+
function tileCollisionGetData(pos, solidOnly=true)
|
|
25
26
|
{
|
|
26
27
|
// check all tile collision layers
|
|
27
28
|
for (const layer of tileCollisionLayers)
|
|
29
|
+
if (!solidOnly || layer.isSolid)
|
|
28
30
|
if (pos.arrayCheck(layer.size))
|
|
29
|
-
|
|
31
|
+
{
|
|
32
|
+
const data = layer.getCollisionData(pos);
|
|
33
|
+
if (data) return data;
|
|
34
|
+
}
|
|
30
35
|
return 0;
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
/** Check if a tile layer collides with another object
|
|
34
|
-
* @param {Vector2}
|
|
35
|
-
* @param {Vector2}
|
|
36
|
-
* @param {EngineObject} [
|
|
37
|
-
* @param {boolean}
|
|
39
|
+
* @param {Vector2} pos
|
|
40
|
+
* @param {Vector2} [size=vec2()]
|
|
41
|
+
* @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
|
|
42
|
+
* @param {boolean} [solidOnly] - Only check solid layers?
|
|
38
43
|
* @return {TileCollisionLayer}
|
|
39
44
|
* @memberof TileLayers */
|
|
40
|
-
function tileCollisionTest(pos, size=vec2(),
|
|
45
|
+
function tileCollisionTest(pos, size=vec2(), callbackObject, solidOnly=true)
|
|
41
46
|
{
|
|
42
47
|
for (const layer of tileCollisionLayers)
|
|
43
48
|
{
|
|
44
49
|
if (!solidOnly || layer.isSolid)
|
|
45
|
-
if (layer.collisionTest(pos, size,
|
|
50
|
+
if (layer.collisionTest(pos, size, callbackObject))
|
|
46
51
|
return layer;
|
|
47
52
|
}
|
|
48
53
|
}
|
|
49
54
|
|
|
55
|
+
/**
|
|
56
|
+
* @callback TileCollisionCallback - Function to handle a tile collision test
|
|
57
|
+
* @param {number} tileData - the value of the tile at the position
|
|
58
|
+
* @param {Vector2} pos - world space position of tile where the collision occurred
|
|
59
|
+
* @memberof TileLayers
|
|
60
|
+
*/
|
|
61
|
+
|
|
50
62
|
/** Return the exact position of the boundary of first tile hit, undefined if nothing was hit.
|
|
51
|
-
* The point will be inside the colliding tile if it hits
|
|
52
|
-
* @param {Vector2}
|
|
53
|
-
* @param {Vector2}
|
|
54
|
-
* @param {EngineObject} [
|
|
55
|
-
* @param {Vector2}
|
|
56
|
-
* @param {boolean}
|
|
63
|
+
* The point will be inside the colliding tile if it hits
|
|
64
|
+
* @param {Vector2} posStart
|
|
65
|
+
* @param {Vector2} posEnd
|
|
66
|
+
* @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
|
|
67
|
+
* @param {Vector2} [normal] - Optional normal of the surface hit
|
|
68
|
+
* @param {boolean} [solidOnly=true] - Only check solid layers?
|
|
57
69
|
* @return {Vector2|undefined} - position of the center of the tile hit or undefined if no hit
|
|
58
70
|
* @memberof TileLayers */
|
|
59
|
-
function tileCollisionRaycast(posStart, posEnd,
|
|
71
|
+
function tileCollisionRaycast(posStart, posEnd, callbackObject, normal, solidOnly=true)
|
|
60
72
|
{
|
|
61
73
|
for (const layer of tileCollisionLayers)
|
|
62
74
|
{
|
|
63
75
|
if (!solidOnly || layer.isSolid)
|
|
64
76
|
{
|
|
65
|
-
const hitPos = layer.collisionRaycast(posStart, posEnd,
|
|
77
|
+
const hitPos = layer.collisionRaycast(posStart, posEnd, callbackObject, normal)
|
|
66
78
|
if (hitPos) return hitPos;
|
|
67
79
|
}
|
|
68
80
|
}
|
|
@@ -183,17 +195,17 @@ class TileLayerData
|
|
|
183
195
|
class CanvasLayer extends EngineObject
|
|
184
196
|
{
|
|
185
197
|
/** Create a canvas layer object
|
|
186
|
-
* @param {Vector2} [
|
|
198
|
+
* @param {Vector2} [pos] - World space position of the layer
|
|
187
199
|
* @param {Vector2} [size] - World space size of the layer
|
|
188
200
|
* @param {number} [angle] - Angle the layer is rotated by
|
|
189
201
|
* @param {number} [renderOrder] - Objects sorted by renderOrder
|
|
190
202
|
* @param {Vector2} [canvasSize] - Default size of canvas, can be changed later
|
|
191
203
|
* @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
|
|
192
204
|
*/
|
|
193
|
-
constructor(
|
|
205
|
+
constructor(pos, size, angle=0, renderOrder=0, canvasSize=vec2(512), useWebGL=true)
|
|
194
206
|
{
|
|
195
207
|
ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
|
|
196
|
-
super(
|
|
208
|
+
super(pos, size, undefined, angle, WHITE, renderOrder);
|
|
197
209
|
|
|
198
210
|
/** @property {HTMLCanvasElement} - The canvas used by this layer */
|
|
199
211
|
this.canvas = headlessMode ? undefined : new OffscreenCanvas(canvasSize.x, canvasSize.y);
|
|
@@ -297,16 +309,16 @@ class CanvasLayer extends EngineObject
|
|
|
297
309
|
class TileLayer extends CanvasLayer
|
|
298
310
|
{
|
|
299
311
|
/** Create a tile layer object
|
|
300
|
-
* @param {Vector2}
|
|
312
|
+
* @param {Vector2} pos - World space position
|
|
301
313
|
* @param {Vector2} size - World space size
|
|
302
314
|
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
303
315
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
316
|
+
* @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
|
|
304
317
|
*/
|
|
305
|
-
constructor(
|
|
318
|
+
constructor(pos, size, tileInfo=tile(), renderOrder=0, useWebGL=true)
|
|
306
319
|
{
|
|
307
320
|
const canvasSize = tileInfo ? size.multiply(tileInfo.size) : size;
|
|
308
|
-
|
|
309
|
-
super(position, size, 0, renderOrder, canvasSize, useWebGL);
|
|
321
|
+
super(pos, size, 0, renderOrder, canvasSize, useWebGL);
|
|
310
322
|
|
|
311
323
|
/** @property {TileInfo} - Default tile info for layer */
|
|
312
324
|
this.tileInfo = undefined;
|
|
@@ -318,15 +330,15 @@ class TileLayer extends CanvasLayer
|
|
|
318
330
|
if (headlessMode)
|
|
319
331
|
{
|
|
320
332
|
// disable rendering in headless mode
|
|
321
|
-
this.render = ()
|
|
322
|
-
this.redraw = ()
|
|
323
|
-
this.redrawStart = ()
|
|
324
|
-
this.redrawEnd = ()
|
|
325
|
-
this.drawTileData = ()
|
|
326
|
-
this.redrawTileData = ()
|
|
327
|
-
this.drawLayerTile = ()
|
|
328
|
-
this.drawLayerRect = ()
|
|
329
|
-
this.clearLayerRect = ()
|
|
333
|
+
this.render = ()=> {};
|
|
334
|
+
this.redraw = ()=> {};
|
|
335
|
+
this.redrawStart = ()=> {};
|
|
336
|
+
this.redrawEnd = ()=> {};
|
|
337
|
+
this.drawTileData = ()=> {};
|
|
338
|
+
this.redrawTileData = ()=> {};
|
|
339
|
+
this.drawLayerTile = ()=> {};
|
|
340
|
+
this.drawLayerRect = ()=> {};
|
|
341
|
+
this.clearLayerRect = ()=> {};
|
|
330
342
|
return;
|
|
331
343
|
}
|
|
332
344
|
|
|
@@ -360,6 +372,12 @@ class TileLayer extends CanvasLayer
|
|
|
360
372
|
isRedraw ? this.drawTileData(layerPos) : this.redrawTileData(layerPos);
|
|
361
373
|
}
|
|
362
374
|
|
|
375
|
+
/** Clear data at a given position in the array
|
|
376
|
+
* @param {Vector2} layerPos - Local position in array
|
|
377
|
+
* @param {boolean} [redraw] - Force the tile to redraw if true */
|
|
378
|
+
clearData(layerPos, redraw=false)
|
|
379
|
+
{ this.setData(layerPos, new TileLayerData, redraw) }
|
|
380
|
+
|
|
363
381
|
/** Get data at a given position in the array
|
|
364
382
|
* @param {Vector2} layerPos - Local position in array
|
|
365
383
|
* @return {TileLayerData} */
|
|
@@ -401,7 +419,7 @@ class TileLayer extends CanvasLayer
|
|
|
401
419
|
for (let x = this.size.x; x--;)
|
|
402
420
|
for (let y = this.size.y; y--;)
|
|
403
421
|
this.drawTileData(vec2(x,y), false);
|
|
404
|
-
this.
|
|
422
|
+
this.isUsingWebGL && glFlush();
|
|
405
423
|
this.onRedraw();
|
|
406
424
|
this.redrawEnd();
|
|
407
425
|
}
|
|
@@ -450,14 +468,13 @@ class TileLayer extends CanvasLayer
|
|
|
450
468
|
if (!this.context) return;
|
|
451
469
|
ASSERT(drawContext === this.context);
|
|
452
470
|
|
|
453
|
-
if (glEnable && this.textureInfo.glTexture)
|
|
454
|
-
glSetRenderTarget();
|
|
455
|
-
|
|
456
471
|
// set stuff back to normal
|
|
472
|
+
if (this.isUsingWebGL)
|
|
473
|
+
glSetRenderTarget();
|
|
457
474
|
[drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor] = this.savedRenderSettings;
|
|
458
475
|
}
|
|
459
476
|
|
|
460
|
-
/** Draw the tile at a given position in the tile
|
|
477
|
+
/** Draw the tile at a given position in the tile layer
|
|
461
478
|
* This can be used to clear out tiles when they are destroyed
|
|
462
479
|
* Tiles can also be redrawn if inside a redrawStart/End block
|
|
463
480
|
* @param {Vector2} layerPos
|
|
@@ -481,7 +498,7 @@ class TileLayer extends CanvasLayer
|
|
|
481
498
|
this.drawLayerTile(drawPos, drawSize, tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
482
499
|
}
|
|
483
500
|
|
|
484
|
-
/** Draw the tile at a given position in the tile
|
|
501
|
+
/** Draw the tile at a given position in the tile layer
|
|
485
502
|
* This can be used to clear tiles when they are destroyed
|
|
486
503
|
* For better performance use drawTileData inside a redrawStart/End block
|
|
487
504
|
* @param {Vector2} layerPos
|
|
@@ -509,7 +526,7 @@ class TileLayer extends CanvasLayer
|
|
|
509
526
|
angle=0, mirror, additiveColor)
|
|
510
527
|
{
|
|
511
528
|
const drawPos = pos.add(size.scale(.5));
|
|
512
|
-
drawTile(drawPos, size, tileInfo, color, angle, mirror, additiveColor);
|
|
529
|
+
drawTile(drawPos, size, tileInfo, color, angle, mirror, additiveColor, this.isUsingWebGL);
|
|
513
530
|
}
|
|
514
531
|
|
|
515
532
|
/** Clear a rectangle in layer space
|
|
@@ -549,14 +566,15 @@ class TileLayer extends CanvasLayer
|
|
|
549
566
|
class TileCollisionLayer extends TileLayer
|
|
550
567
|
{
|
|
551
568
|
/** Create a tile layer object
|
|
552
|
-
* @param {Vector2}
|
|
553
|
-
* @param {Vector2} size
|
|
554
|
-
* @param {TileInfo} [tileInfo]
|
|
569
|
+
* @param {Vector2} pos - World space position
|
|
570
|
+
* @param {Vector2} size - World space size
|
|
571
|
+
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
555
572
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
573
|
+
* @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
|
|
556
574
|
*/
|
|
557
|
-
constructor(
|
|
575
|
+
constructor(pos, size, tileInfo=tile(), renderOrder=0, useWebGL=true)
|
|
558
576
|
{
|
|
559
|
-
super(
|
|
577
|
+
super(pos, size.floor(), tileInfo, renderOrder, useWebGL);
|
|
560
578
|
|
|
561
579
|
/** @property {Array<number>} - The tile collision grid */
|
|
562
580
|
this.collisionData = [];
|
|
@@ -592,41 +610,50 @@ class TileCollisionLayer extends TileLayer
|
|
|
592
610
|
this.collisionData.fill(0);
|
|
593
611
|
}
|
|
594
612
|
|
|
595
|
-
/** Set tile collision data for a given cell in the
|
|
596
|
-
* @param {Vector2}
|
|
613
|
+
/** Set tile collision data for a given cell in the layer
|
|
614
|
+
* @param {Vector2} layerPos
|
|
597
615
|
* @param {number} [data] */
|
|
598
|
-
setCollisionData(
|
|
616
|
+
setCollisionData(layerPos, data=1)
|
|
599
617
|
{
|
|
600
|
-
ASSERT(isVector2(
|
|
601
|
-
const i = (
|
|
602
|
-
|
|
618
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
619
|
+
const i = (layerPos.y|0)*this.size.x + layerPos.x|0;
|
|
620
|
+
layerPos.arrayCheck(this.size) && (this.collisionData[i] = data);
|
|
603
621
|
}
|
|
604
622
|
|
|
605
|
-
/**
|
|
606
|
-
* @param {Vector2}
|
|
623
|
+
/** Clear tile collision data for a given cell in the layer
|
|
624
|
+
* @param {Vector2} layerPos */
|
|
625
|
+
clearCollisionData(layerPos)
|
|
626
|
+
{ this.setCollisionData(layerPos, 0); }
|
|
627
|
+
|
|
628
|
+
/** Get tile collision data for a given cell in the layer
|
|
629
|
+
* @param {Vector2} layerPos
|
|
607
630
|
* @return {number} */
|
|
608
|
-
getCollisionData(
|
|
631
|
+
getCollisionData(layerPos)
|
|
609
632
|
{
|
|
610
|
-
ASSERT(isVector2(
|
|
611
|
-
const i = (
|
|
612
|
-
return
|
|
633
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
634
|
+
const i = (layerPos.y|0)*this.size.x + layerPos.x|0;
|
|
635
|
+
return layerPos.arrayCheck(this.size) ? this.collisionData[i] : 0;
|
|
613
636
|
}
|
|
614
637
|
|
|
615
638
|
/** Check if collision with another object should occur
|
|
616
639
|
* @param {Vector2} pos
|
|
617
640
|
* @param {Vector2} [size=vec2()]
|
|
618
|
-
* @param {EngineObject} [object
|
|
641
|
+
* @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
|
|
619
642
|
* @return {boolean} */
|
|
620
|
-
collisionTest(pos, size=new Vector2,
|
|
643
|
+
collisionTest(pos, size=new Vector2, callbackObject)
|
|
621
644
|
{
|
|
622
645
|
ASSERT(isVector2(pos) && isVector2(size), 'pos and size must be Vector2s');
|
|
623
|
-
ASSERT(!
|
|
624
|
-
|
|
625
|
-
//
|
|
626
|
-
const
|
|
627
|
-
|
|
646
|
+
ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a funtion or EngineObject');
|
|
647
|
+
|
|
648
|
+
// make function to check for collision
|
|
649
|
+
const collisionTest = callbackObject ? typeof callbackObject === 'function' ?
|
|
650
|
+
(tileData, pos)=> callbackObject(tileData, pos) :
|
|
651
|
+
(tileData, pos)=> callbackObject.collideWithTile(tileData, pos) :
|
|
652
|
+
()=> true;
|
|
628
653
|
|
|
629
654
|
// check any tiles in the area for collision
|
|
655
|
+
const posX = pos.x - this.pos.x;
|
|
656
|
+
const posY = pos.y - this.pos.y;
|
|
630
657
|
const minX = max(posX - size.x/2|0, 0);
|
|
631
658
|
const minY = max(posY - size.y/2|0, 0);
|
|
632
659
|
const maxX = min(posX + size.x/2, this.size.x);
|
|
@@ -637,9 +664,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
637
664
|
{
|
|
638
665
|
// check if the object should collide with this tile
|
|
639
666
|
const tileData = this.collisionData[y*this.size.x+x];
|
|
640
|
-
if (tileData)
|
|
641
|
-
if (!object || object.collideWithTile(tileData,
|
|
642
|
-
hitPos.set(x + this.pos.x, y + this.pos.y)))
|
|
667
|
+
if (tileData && collisionTest(tileData, hitPos.set(x+this.pos.x, y+this.pos.y)))
|
|
643
668
|
return true;
|
|
644
669
|
}
|
|
645
670
|
return false;
|
|
@@ -647,35 +672,39 @@ class TileCollisionLayer extends TileLayer
|
|
|
647
672
|
|
|
648
673
|
/** Return the exact position of the boundary of first tile hit, undefined if nothing was hit.
|
|
649
674
|
* The point will be inside the colliding tile if it hits (may have a tiny shift)
|
|
650
|
-
* @param {Vector2}
|
|
651
|
-
* @param {Vector2}
|
|
652
|
-
* @param {EngineObject} [
|
|
653
|
-
* @param {Vector2}
|
|
675
|
+
* @param {Vector2} posStart
|
|
676
|
+
* @param {Vector2} posEnd
|
|
677
|
+
* @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
|
|
678
|
+
* @param {Vector2} [normal] - Optional normal of the surface hit
|
|
654
679
|
* @return {Vector2|undefined} */
|
|
655
|
-
collisionRaycast(posStart, posEnd,
|
|
680
|
+
collisionRaycast(posStart, posEnd, callbackObject, normal)
|
|
656
681
|
{
|
|
657
682
|
ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
|
|
658
|
-
ASSERT(!
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const collisionTest =
|
|
683
|
+
ASSERT(!callbackObject || typeof callbackObject === 'function' || callbackObject instanceof EngineObject, 'callbackObject must be a funtion or EngineObject');
|
|
684
|
+
|
|
685
|
+
// make function to check for collision
|
|
686
|
+
const collisionTest = callbackObject ? typeof callbackObject === 'function' ?
|
|
687
|
+
(tileData, pos)=> callbackObject(tileData, pos) :
|
|
688
|
+
(tileData, pos)=> callbackObject.collideWithTile(tileData, pos) :
|
|
689
|
+
(tileData)=> tileData > 0;
|
|
690
|
+
const testFunction = (pos)=>
|
|
662
691
|
{
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const tileData = this.getCollisionData(localPos);
|
|
666
|
-
return tileData && (!object || object.collideWithTile(tileData, pos));
|
|
692
|
+
const tileData = this.getCollisionData(localPos.set(pos.x - this.pos.x, pos.y - this.pos.y));
|
|
693
|
+
return tileData && collisionTest(tileData, pos);
|
|
667
694
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
695
|
+
|
|
696
|
+
// use line test against tile collision
|
|
697
|
+
const localPos = new Vector2;
|
|
698
|
+
const hitPos = lineTest(posStart, posEnd, testFunction, normal);
|
|
699
|
+
if (debugRaycast && hitPos)
|
|
671
700
|
{
|
|
672
701
|
const tilePos = hitPos.floor().add(vec2(.5));
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
return hitPos;
|
|
702
|
+
debugRect(tilePos, vec2(1), '#f008');
|
|
703
|
+
debugLine(posStart, posEnd, '#00f', .02);
|
|
704
|
+
debugLine(posStart, hitPos, '#f00', .02);
|
|
705
|
+
debugPoint(hitPos, '#0f0');
|
|
706
|
+
normal && debugLine(hitPos, hitPos.add(normal), '#ff0', .02);
|
|
679
707
|
}
|
|
708
|
+
return hitPos;
|
|
680
709
|
}
|
|
681
710
|
}
|
package/src/engineUtilities.js
CHANGED
|
@@ -7,6 +7,90 @@
|
|
|
7
7
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Timer object tracks how long has passed since it was set
|
|
12
|
+
* @memberof Engine
|
|
13
|
+
* @example
|
|
14
|
+
* let a = new Timer; // creates a timer that is not set
|
|
15
|
+
* a.set(3); // sets the timer to 3 seconds
|
|
16
|
+
*
|
|
17
|
+
* let b = new Timer(1); // creates a timer with 1 second left
|
|
18
|
+
* b.unset(); // unset the timer
|
|
19
|
+
*/
|
|
20
|
+
class Timer
|
|
21
|
+
{
|
|
22
|
+
/** Create a timer object set time passed in
|
|
23
|
+
* @param {number} [timeLeft] - How much time left before the timer
|
|
24
|
+
* @param {boolean} [useRealTime] - Should the timer keep running even when the game is paused? (useful for UI) */
|
|
25
|
+
constructor(timeLeft, useRealTime=false)
|
|
26
|
+
{
|
|
27
|
+
ASSERT(timeLeft === undefined || isNumber(timeLeft), 'Constructed Timer is invalid.', timeLeft);
|
|
28
|
+
this.useRealTime = useRealTime;
|
|
29
|
+
const globalTime = this.getGlobalTime();
|
|
30
|
+
this.time = timeLeft === undefined ? undefined : globalTime + timeLeft;
|
|
31
|
+
this.setTime = timeLeft;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Set the timer with seconds passed in
|
|
35
|
+
* @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds */
|
|
36
|
+
set(timeLeft=0)
|
|
37
|
+
{
|
|
38
|
+
ASSERT(isNumber(timeLeft), 'Timer is invalid.', timeLeft);
|
|
39
|
+
const globalTime = this.getGlobalTime();
|
|
40
|
+
this.time = globalTime + timeLeft;
|
|
41
|
+
this.setTime = timeLeft;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Set if the timer should keep running even when the game is paused
|
|
45
|
+
* @param {boolean} [useRealTime] */
|
|
46
|
+
setUseRealTime(useRealTime=true)
|
|
47
|
+
{
|
|
48
|
+
ASSERT(!this.isSet(), 'Cannot change global time setting while timer is set.');
|
|
49
|
+
this.useRealTime = useRealTime;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Unset the timer */
|
|
53
|
+
unset() { this.time = undefined; }
|
|
54
|
+
|
|
55
|
+
/** Returns true if set
|
|
56
|
+
* @return {boolean} */
|
|
57
|
+
isSet() { return this.time !== undefined; }
|
|
58
|
+
|
|
59
|
+
/** Returns true if set and has not elapsed
|
|
60
|
+
* @return {boolean} */
|
|
61
|
+
active() { return this.getGlobalTime() < this.time; }
|
|
62
|
+
|
|
63
|
+
/** Returns true if set and elapsed
|
|
64
|
+
* @return {boolean} */
|
|
65
|
+
elapsed() { return this.getGlobalTime() >= this.time; }
|
|
66
|
+
|
|
67
|
+
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
68
|
+
* @return {number} */
|
|
69
|
+
get() { return this.isSet()? this.getGlobalTime() - this.time : 0; }
|
|
70
|
+
|
|
71
|
+
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
72
|
+
* @return {number} */
|
|
73
|
+
getPercent() { return this.isSet()? 1-percent(this.time - this.getGlobalTime(), 0, this.setTime) : 0; }
|
|
74
|
+
|
|
75
|
+
/** Get the time this timer was set to, returns 0 if not set
|
|
76
|
+
* @return {number} */
|
|
77
|
+
getSetTime() { return this.isSet() ? this.setTime : 0; }
|
|
78
|
+
|
|
79
|
+
/** Get the current global time this timer is based on
|
|
80
|
+
* @return {number} */
|
|
81
|
+
getGlobalTime() { return this.useRealTime ? timeReal : time; }
|
|
82
|
+
|
|
83
|
+
/** Returns this timer expressed as a string
|
|
84
|
+
* @return {string} */
|
|
85
|
+
toString() { return this.isSet() ? abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
|
|
86
|
+
|
|
87
|
+
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
88
|
+
* @return {number} */
|
|
89
|
+
valueOf() { return this.get(); }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
93
|
+
|
|
10
94
|
/** Formats seconds to mm:ss style for display purposes
|
|
11
95
|
* @param {number} t - time in seconds
|
|
12
96
|
* @return {string}
|
|
@@ -93,84 +177,27 @@ function shareURL(title, url, callback)
|
|
|
93
177
|
|
|
94
178
|
///////////////////////////////////////////////////////////////////////////////
|
|
95
179
|
|
|
96
|
-
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
|
|
102
|
-
*
|
|
103
|
-
* let b = new Timer(1); // creates a timer with 1 second left
|
|
104
|
-
* b.unset(); // unset the timer
|
|
105
|
-
*/
|
|
106
|
-
class Timer
|
|
180
|
+
/** Read save data from local storage
|
|
181
|
+
* @param {string} saveName - unique name for the game/save
|
|
182
|
+
* @param {Object} [defaultSaveData] - default values for save
|
|
183
|
+
* @return {Object}
|
|
184
|
+
* @memberof Utilities */
|
|
185
|
+
function readSaveData(saveName, defaultSaveData)
|
|
107
186
|
{
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const globalTime = this.getGlobalTime();
|
|
116
|
-
this.time = timeLeft === undefined ? undefined : globalTime + timeLeft;
|
|
117
|
-
this.setTime = timeLeft;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Set the timer with seconds passed in
|
|
121
|
-
* @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds */
|
|
122
|
-
set(timeLeft=0)
|
|
123
|
-
{
|
|
124
|
-
ASSERT(isNumber(timeLeft), 'Timer is invalid.', timeLeft);
|
|
125
|
-
const globalTime = this.getGlobalTime();
|
|
126
|
-
this.time = globalTime + timeLeft;
|
|
127
|
-
this.setTime = timeLeft;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Set if the timer should keep running even when the game is paused
|
|
131
|
-
* @param {boolean} [useRealTime] */
|
|
132
|
-
setUseRealTime(useRealTime=true)
|
|
133
|
-
{
|
|
134
|
-
ASSERT(!this.isSet(), 'Cannot change global time setting while timer is set.');
|
|
135
|
-
this.useRealTime = useRealTime;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Unset the timer */
|
|
139
|
-
unset() { this.time = undefined; }
|
|
140
|
-
|
|
141
|
-
/** Returns true if set
|
|
142
|
-
* @return {boolean} */
|
|
143
|
-
isSet() { return this.time !== undefined; }
|
|
144
|
-
|
|
145
|
-
/** Returns true if set and has not elapsed
|
|
146
|
-
* @return {boolean} */
|
|
147
|
-
active() { return this.getGlobalTime() < this.time; }
|
|
148
|
-
|
|
149
|
-
/** Returns true if set and elapsed
|
|
150
|
-
* @return {boolean} */
|
|
151
|
-
elapsed() { return this.getGlobalTime() >= this.time; }
|
|
152
|
-
|
|
153
|
-
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
154
|
-
* @return {number} */
|
|
155
|
-
get() { return this.isSet()? this.getGlobalTime() - this.time : 0; }
|
|
156
|
-
|
|
157
|
-
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
158
|
-
* @return {number} */
|
|
159
|
-
getPercent() { return this.isSet()? 1-percent(this.time - this.getGlobalTime(), 0, this.setTime) : 0; }
|
|
160
|
-
|
|
161
|
-
/** Get the time this timer was set to, returns 0 if not set
|
|
162
|
-
* @return {number} */
|
|
163
|
-
getSetTime() { return this.isSet() ? this.setTime : 0; }
|
|
164
|
-
|
|
165
|
-
/** Get the current global time this timer is based on
|
|
166
|
-
* @return {number} */
|
|
167
|
-
getGlobalTime() { return this.useRealTime ? timeReal : time; }
|
|
168
|
-
|
|
169
|
-
/** Returns this timer expressed as a string
|
|
170
|
-
* @return {string} */
|
|
171
|
-
toString() { return this.isSet() ? abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
|
|
187
|
+
ASSERT(isString(saveName), 'loadData requires saveName string');
|
|
188
|
+
|
|
189
|
+
// replace undefined values with defaults
|
|
190
|
+
const data = localStorage[saveName];
|
|
191
|
+
const loadedData = data ? JSON.parse(data) : {};
|
|
192
|
+
return { ...defaultSaveData, ...loadedData };
|
|
193
|
+
}
|
|
172
194
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
195
|
+
/** Write save data to local storage
|
|
196
|
+
* @param {string} saveName - unique name for the game/save
|
|
197
|
+
* @param {Object} saveData - object containing data to be saved
|
|
198
|
+
* @memberof Utilities */
|
|
199
|
+
function writeSaveData(saveName, saveData)
|
|
200
|
+
{
|
|
201
|
+
ASSERT(isString(saveName), 'saveData requires saveName string');
|
|
202
|
+
localStorage[saveName] = JSON.stringify(saveData);
|
|
176
203
|
}
|
package/src/engineWebGL.js
CHANGED
|
@@ -259,7 +259,7 @@ function glPreRender(clear=true)
|
|
|
259
259
|
p.x, p.y, 0, 1];
|
|
260
260
|
|
|
261
261
|
// set the same transform matrix for both shaders
|
|
262
|
-
const initUniform = (program, uniform, value)
|
|
262
|
+
const initUniform = (program, uniform, value)=>
|
|
263
263
|
{
|
|
264
264
|
glContext.useProgram(program);
|
|
265
265
|
const location = glContext.getUniformLocation(program, uniform);
|
|
@@ -653,7 +653,7 @@ function glSetRenderTarget(texture, clear=false)
|
|
|
653
653
|
glContext.bindFramebuffer(glContext.FRAMEBUFFER, glFramebuffer);
|
|
654
654
|
glContext.framebufferTexture2D(glContext.FRAMEBUFFER,
|
|
655
655
|
glContext.COLOR_ATTACHMENT0, glContext.TEXTURE_2D, texture, 0);
|
|
656
|
-
|
|
656
|
+
glPreRender(clear);
|
|
657
657
|
}
|
|
658
658
|
else
|
|
659
659
|
{
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
Copyright (c) 2006-2010 Erin Catto http://www.gphysics.com
|
|
2
|
-
|
|
3
|
-
This software is provided 'as-is', without any express or implied
|
|
4
|
-
warranty. In no event will the authors be held liable for any damages
|
|
5
|
-
arising from the use of this software.
|
|
6
|
-
|
|
7
|
-
Permission is granted to anyone to use this software for any purpose,
|
|
8
|
-
including commercial applications, and to alter it and redistribute it
|
|
9
|
-
freely, subject to the following restrictions:
|
|
10
|
-
|
|
11
|
-
1. The origin of this software must not be misrepresented; you must not
|
|
12
|
-
claim that you wrote the original software. If you use this software
|
|
13
|
-
in a product, an acknowledgment in the product documentation would be
|
|
14
|
-
appreciated but is not required.
|
|
15
|
-
2. Altered source versions must be plainly marked as such, and must not be
|
|
16
|
-
misrepresented as being the original software.
|
|
17
|
-
3. This notice may not be removed or altered from any source distribution.
|