littlejsengine 1.17.5 → 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 +160 -88
- package/dist/littlejs.esm.js +586 -426
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +583 -425
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +582 -424
- package/examples/electron/index.html +2 -2
- package/examples/index.html +2 -2
- package/examples/logo.png +0 -0
- package/examples/logo2.png +0 -0
- package/examples/particles/index.html +1 -1
- package/examples/platformer/gameEffects.js +3 -3
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/colors.js +1 -1
- package/examples/shorts/fps.js +1 -1
- package/examples/shorts/music.js +3 -2
- package/examples/shorts/musicPlayer.js +2 -1
- package/examples/shorts/parallax.js +2 -2
- package/examples/shorts/tiltedView.js +1 -1
- package/examples/starter/index.html +2 -2
- package/package.json +1 -1
- package/plugins/box2d.js +6 -7
- package/plugins/pluginExport.js +1 -1
- package/plugins/postProcess.js +1 -1
- package/plugins/uiSystem.js +30 -17
- package/reference.md +6 -6
- package/src/engine.js +103 -127
- package/src/engineDraw.js +2 -2
- package/src/engineExport.js +2 -0
- package/src/engineInput.js +1 -1
- package/src/engineObject.js +24 -24
- package/src/engineParticles.js +241 -118
- package/src/engineTileLayer.js +66 -48
- package/src/engineUtilities.js +105 -78
- 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
|
}
|
|
@@ -626,18 +638,22 @@ class TileCollisionLayer extends TileLayer
|
|
|
626
638
|
/** Check if collision with another object should occur
|
|
627
639
|
* @param {Vector2} pos
|
|
628
640
|
* @param {Vector2} [size=vec2()]
|
|
629
|
-
* @param {EngineObject} [object
|
|
641
|
+
* @param {EngineObject|TileCollisionCallback} [callbackObject] - Callback, engine object, or undefined
|
|
630
642
|
* @return {boolean} */
|
|
631
|
-
collisionTest(pos, size=new Vector2,
|
|
643
|
+
collisionTest(pos, size=new Vector2, callbackObject)
|
|
632
644
|
{
|
|
633
645
|
ASSERT(isVector2(pos) && isVector2(size), 'pos and size must be Vector2s');
|
|
634
|
-
ASSERT(!
|
|
635
|
-
|
|
636
|
-
//
|
|
637
|
-
const
|
|
638
|
-
|
|
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;
|
|
639
653
|
|
|
640
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;
|
|
641
657
|
const minX = max(posX - size.x/2|0, 0);
|
|
642
658
|
const minY = max(posY - size.y/2|0, 0);
|
|
643
659
|
const maxX = min(posX + size.x/2, this.size.x);
|
|
@@ -648,9 +664,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
648
664
|
{
|
|
649
665
|
// check if the object should collide with this tile
|
|
650
666
|
const tileData = this.collisionData[y*this.size.x+x];
|
|
651
|
-
if (tileData)
|
|
652
|
-
if (!object || object.collideWithTile(tileData,
|
|
653
|
-
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)))
|
|
654
668
|
return true;
|
|
655
669
|
}
|
|
656
670
|
return false;
|
|
@@ -658,35 +672,39 @@ class TileCollisionLayer extends TileLayer
|
|
|
658
672
|
|
|
659
673
|
/** Return the exact position of the boundary of first tile hit, undefined if nothing was hit.
|
|
660
674
|
* The point will be inside the colliding tile if it hits (may have a tiny shift)
|
|
661
|
-
* @param {Vector2}
|
|
662
|
-
* @param {Vector2}
|
|
663
|
-
* @param {EngineObject} [
|
|
664
|
-
* @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
|
|
665
679
|
* @return {Vector2|undefined} */
|
|
666
|
-
collisionRaycast(posStart, posEnd,
|
|
680
|
+
collisionRaycast(posStart, posEnd, callbackObject, normal)
|
|
667
681
|
{
|
|
668
682
|
ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
|
|
669
|
-
ASSERT(!
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
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)=>
|
|
673
691
|
{
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const tileData = this.getCollisionData(localPos);
|
|
677
|
-
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);
|
|
678
694
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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)
|
|
682
700
|
{
|
|
683
701
|
const tilePos = hitPos.floor().add(vec2(.5));
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
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);
|
|
690
707
|
}
|
|
708
|
+
return hitPos;
|
|
691
709
|
}
|
|
692
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
|
}
|
|
@@ -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.
|