littlejsengine 1.12.6 → 1.13.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/dist/littlejs.d.ts +387 -177
- package/dist/littlejs.esm.js +5934 -5294
- package/dist/littlejs.esm.min.js +4 -1
- package/dist/littlejs.js +5887 -5263
- package/dist/littlejs.min.js +4 -1
- package/dist/littlejs.release.js +5426 -4829
- package/examples/box2d/game.js +1 -1
- package/examples/box2d/gameObjects.js +7 -6
- package/examples/box2d/index.html +1 -1
- package/examples/box2d/scenes.js +7 -7
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakout/index.html +1 -1
- package/examples/breakoutTutorial/README.md +2 -2
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/breakoutTutorial/index.html +1 -1
- package/examples/empty/index.html +1 -1
- package/examples/htmlMenu/index.html +1 -1
- package/examples/index.html +396 -153
- package/examples/module/game.js +1 -1
- package/examples/module/index.html +1 -1
- package/examples/platformer/game.js +4 -4
- package/examples/platformer/gameCharacter.js +6 -6
- package/examples/platformer/gameEffects.js +74 -57
- package/examples/platformer/gameLevel.js +61 -70
- package/examples/platformer/gameObjects.js +4 -4
- package/examples/platformer/index.html +1 -1
- package/examples/puzzle/index.html +1 -1
- package/examples/shorts/base.html +23 -3
- package/examples/shorts/box2dCar.js +8 -12
- package/examples/shorts/flappyGame.js +52 -0
- package/examples/shorts/helloWorld.js +6 -3
- package/examples/shorts/hillGlideGame.js +56 -0
- package/examples/shorts/landerGame.js +32 -0
- package/examples/shorts/maze.js +47 -0
- package/examples/shorts/medals.js +42 -0
- package/examples/shorts/nineSlice.js +21 -0
- package/examples/shorts/piano.js +52 -0
- package/examples/shorts/platformer.js +24 -20
- package/examples/shorts/{pong.js → pongGame.js} +1 -1
- package/examples/shorts/raycasting.js +71 -0
- package/examples/shorts/slidingPuzzle.js +42 -0
- package/examples/shorts/spaceGame.js +56 -0
- package/examples/shorts/starfield.js +17 -0
- package/examples/shorts/tileLayer.js +3 -5
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +8 -7
- package/examples/shorts/topDown.js +18 -26
- package/examples/shorts/uiSystem.js +4 -7
- package/examples/starter/build.bat +6 -1
- package/examples/starter/game.js +2 -2
- package/examples/starter/index.html +23 -13
- package/examples/stress/index.html +2 -3
- package/examples/style.css +136 -0
- package/examples/typescript/build.js +1 -2
- package/examples/typescript/game.js +2 -2
- package/examples/typescript/game.ts +1 -1
- package/examples/typescript/index.html +1 -1
- package/examples/uiSystem/game.js +8 -24
- package/examples/uiSystem/index.html +1 -1
- package/package.json +1 -1
- package/plugins/box2d.js +8 -8
- package/plugins/drawUtilities.js +126 -0
- package/plugins/newgrounds.js +1 -1
- package/plugins/pluginExport.js +7 -1
- package/plugins/postProcess.js +2 -2
- package/plugins/uiSystem.js +153 -65
- package/plugins/zzfxm.js +1 -1
- package/reference.md +10 -10
- package/src/engine.js +50 -29
- package/src/engineAudio.js +14 -5
- package/src/engineBuild.bat +6 -1
- package/src/engineBuild.js +20 -5
- package/src/engineDebug.js +53 -26
- package/src/engineDraw.js +108 -57
- package/src/engineExport.js +21 -9
- package/src/engineInput.js +109 -37
- package/src/engineObject.js +68 -67
- package/src/engineParticles.js +26 -9
- package/src/engineSettings.js +15 -16
- package/src/engineTileLayer.js +312 -153
- package/src/engineUtilities.js +182 -130
- package/src/engineWebGL.js +47 -36
package/plugins/uiSystem.js
CHANGED
|
@@ -43,10 +43,20 @@ class UISystemPlugin
|
|
|
43
43
|
this.defaultButtonColor = hsl(0,0,.5);
|
|
44
44
|
/** @property {Color} - Default hover color for UI elements */
|
|
45
45
|
this.defaultHoverColor = hsl(0,0,.7);
|
|
46
|
+
/** @property {Color} - Default color for disabled UI elements */
|
|
47
|
+
this.defaultDisabledColor = hsl(0,0,.2);
|
|
46
48
|
/** @property {number} - Default line width for UI elements */
|
|
47
49
|
this.defaultLineWidth = 4;
|
|
50
|
+
/** @property {number} - Default rounded rect corner radius for UI elements */
|
|
51
|
+
this.defaultCornerRadius = 0;
|
|
48
52
|
/** @property {string} - Default font for UI elements */
|
|
49
53
|
this.defaultFont = 'arial';
|
|
54
|
+
/** @property {Sound} - Default sound when interactive UI element is pressed */
|
|
55
|
+
this.defaultSoundPress = undefined;
|
|
56
|
+
/** @property {Sound} - Default sound when interactive UI element is released */
|
|
57
|
+
this.defaultSoundRelease = undefined;
|
|
58
|
+
/** @property {Sound} - Default sound when interactive UI element is clicked */
|
|
59
|
+
this.defaultSoundClick = undefined;
|
|
50
60
|
/** @property {Array<UIObject>} - List of all UI elements */
|
|
51
61
|
this.uiObjects = [];
|
|
52
62
|
/** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
|
|
@@ -54,18 +64,30 @@ class UISystemPlugin
|
|
|
54
64
|
|
|
55
65
|
engineAddPlugin(uiUpdate, uiRender);
|
|
56
66
|
|
|
67
|
+
function updateInvisible(o)
|
|
68
|
+
{
|
|
69
|
+
for(const c of o.children)
|
|
70
|
+
updateInvisible(c);
|
|
71
|
+
o.updateInvisible();
|
|
72
|
+
}
|
|
73
|
+
|
|
57
74
|
// setup recursive update and render
|
|
58
75
|
function uiUpdate()
|
|
59
76
|
{
|
|
60
77
|
function updateObject(o)
|
|
61
78
|
{
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
if (o.visible)
|
|
80
|
+
{
|
|
81
|
+
// set position in parent space
|
|
82
|
+
if (o.parent)
|
|
83
|
+
o.pos = o.localPos.add(o.parent.pos);
|
|
84
|
+
// update in reverse order to detect mouse enter/leave
|
|
85
|
+
for(let i=o.children.length; i--;)
|
|
86
|
+
updateObject(o.children[i]);
|
|
87
|
+
o.update();
|
|
88
|
+
}
|
|
89
|
+
else
|
|
90
|
+
updateInvisible(o);
|
|
69
91
|
}
|
|
70
92
|
uiSystem.uiObjects.forEach(o=> o.parent || updateObject(o));
|
|
71
93
|
}
|
|
@@ -90,18 +112,23 @@ class UISystemPlugin
|
|
|
90
112
|
* @param {Vector2} size
|
|
91
113
|
* @param {Color} [color=uiSystem.defaultColor]
|
|
92
114
|
* @param {number} [lineWidth=uiSystem.defaultLineWidth]
|
|
93
|
-
* @param {Color} [lineColor=uiSystem.defaultLineColor]
|
|
94
|
-
|
|
115
|
+
* @param {Color} [lineColor=uiSystem.defaultLineColor]
|
|
116
|
+
* @param {number} [lineWidth=uiSystem.defaultCornerRadius] */
|
|
117
|
+
drawRect(pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, cornerRadius=uiSystem.defaultCornerRadius)
|
|
95
118
|
{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
119
|
+
const context = uiSystem.uiContext;
|
|
120
|
+
context.fillStyle = color.toString();
|
|
121
|
+
context.beginPath();
|
|
122
|
+
if (cornerRadius && context['roundRect'])
|
|
123
|
+
context['roundRect'](pos.x-size.x/2, pos.y-size.y/2, size.x, size.y, cornerRadius);
|
|
124
|
+
else
|
|
125
|
+
context.rect(pos.x-size.x/2, pos.y-size.y/2, size.x, size.y);
|
|
126
|
+
context.fill();
|
|
100
127
|
if (lineWidth)
|
|
101
128
|
{
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
129
|
+
context.strokeStyle = lineColor.toString();
|
|
130
|
+
context.lineWidth = lineWidth;
|
|
131
|
+
context.stroke();
|
|
105
132
|
}
|
|
106
133
|
}
|
|
107
134
|
|
|
@@ -112,12 +139,13 @@ class UISystemPlugin
|
|
|
112
139
|
* @param {Color} [lineColor=uiSystem.defaultLineColor] */
|
|
113
140
|
drawLine(posA, posB, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor)
|
|
114
141
|
{
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
142
|
+
const context = uiSystem.uiContext;
|
|
143
|
+
context.strokeStyle = lineColor.toString();
|
|
144
|
+
context.lineWidth = lineWidth;
|
|
145
|
+
context.beginPath();
|
|
146
|
+
context.lineTo(posA.x, posA.y);
|
|
147
|
+
context.lineTo(posB.x, posB.y);
|
|
148
|
+
context.stroke();
|
|
121
149
|
}
|
|
122
150
|
|
|
123
151
|
/** Draw a tile to the UI context
|
|
@@ -165,26 +193,40 @@ class UIObject
|
|
|
165
193
|
this.pos = pos.copy();
|
|
166
194
|
/** @property {Vector2} - Screen space size of the object */
|
|
167
195
|
this.size = size.copy();
|
|
168
|
-
/** @property {Color} */
|
|
196
|
+
/** @property {Color} - color of the object */
|
|
169
197
|
this.color = uiSystem.defaultColor;
|
|
170
|
-
/** @property {Color} */
|
|
171
|
-
this.lineColor = uiSystem.defaultLineColor;
|
|
172
|
-
/** @property {Color} */
|
|
198
|
+
/** @property {Color} - color for text */
|
|
173
199
|
this.textColor = uiSystem.defaultTextColor;
|
|
174
|
-
/** @property {Color} */
|
|
200
|
+
/** @property {Color} - color used when hovering over the object */
|
|
175
201
|
this.hoverColor = uiSystem.defaultHoverColor;
|
|
176
|
-
/** @property {
|
|
202
|
+
/** @property {Color} - color for line drawing */
|
|
203
|
+
this.lineColor = uiSystem.defaultLineColor;
|
|
204
|
+
/** @property {number} - width for line drawing */
|
|
177
205
|
this.lineWidth = uiSystem.defaultLineWidth;
|
|
178
|
-
/** @property {string} */
|
|
206
|
+
/** @property {string} - font for this objecct */
|
|
179
207
|
this.font = uiSystem.defaultFont;
|
|
180
208
|
/** @property {number} - override for text height */
|
|
181
209
|
this.textHeight = undefined;
|
|
182
|
-
/** @property {boolean} */
|
|
210
|
+
/** @property {boolean} - should this object be drawn */
|
|
183
211
|
this.visible = true;
|
|
184
|
-
/** @property {Array<UIObject>} */
|
|
212
|
+
/** @property {Array<UIObject>} - a list of this object's children */
|
|
185
213
|
this.children = [];
|
|
186
|
-
/** @property {UIObject} */
|
|
214
|
+
/** @property {UIObject} - this object's parent, position is in parent space */
|
|
187
215
|
this.parent = undefined;
|
|
216
|
+
/** @property {number} - Extra size added when checking if element is touched */
|
|
217
|
+
this.extraTouchSize = 0;
|
|
218
|
+
/** @property {Sound} - Sound when interactive element is pressed */
|
|
219
|
+
this.soundPress = uiSystem.defaultSoundPress;
|
|
220
|
+
/** @property {Sound} - Sound when interactive element is released */
|
|
221
|
+
this.soundRelease = uiSystem.defaultSoundRelease;
|
|
222
|
+
/** @property {Sound} - Sound when interactive element is clicked */
|
|
223
|
+
this.soundClick = uiSystem.defaultSoundClick;
|
|
224
|
+
/** @property {boolean} - Is the mouse over this element */
|
|
225
|
+
this.mouseIsOver = false;
|
|
226
|
+
/** @property {boolean} - Is this element interactive */
|
|
227
|
+
this.interactive = false;
|
|
228
|
+
/** @property {boolean} - Is the mouse held on this element (was pressed while over and hasn't been released) */
|
|
229
|
+
this.mouseIsHeld = false;
|
|
188
230
|
uiSystem.uiObjects.push(this);
|
|
189
231
|
}
|
|
190
232
|
|
|
@@ -211,55 +253,93 @@ class UIObject
|
|
|
211
253
|
/** Update the object, called automatically by plugin once each frame */
|
|
212
254
|
update()
|
|
213
255
|
{
|
|
214
|
-
// track mouse input
|
|
215
256
|
const mouseWasOver = this.mouseIsOver;
|
|
257
|
+
const mousePress = mouseWasPressed(0);
|
|
216
258
|
const mouseDown = mouseIsDown(0);
|
|
217
|
-
if (!mouseDown ||
|
|
259
|
+
if (mousePress || !mouseDown || this.mouseIsHeld)
|
|
218
260
|
{
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
this.mouseIsOver = false;
|
|
222
|
-
if (this.mouseIsOver && !mouseWasOver)
|
|
223
|
-
this.onEnter();
|
|
224
|
-
if (!this.mouseIsOver && mouseWasOver)
|
|
225
|
-
this.onLeave();
|
|
261
|
+
const size = this.size.add(vec2(isTouchDevice && this.extraTouchSize || 0));
|
|
262
|
+
this.mouseIsOver = isOverlapping(this.pos, size, mousePosScreen);
|
|
226
263
|
}
|
|
227
|
-
if (
|
|
264
|
+
if (this.mouseIsOver)
|
|
228
265
|
{
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (
|
|
232
|
-
|
|
266
|
+
if (mousePress)
|
|
267
|
+
inputClearKey(0,0,0,1,0); // clear mouse was pressed state
|
|
268
|
+
if (!this.disabled)
|
|
269
|
+
{
|
|
270
|
+
if (mousePress)
|
|
271
|
+
{
|
|
272
|
+
if (this.interactive)
|
|
273
|
+
{
|
|
274
|
+
this.onPress();
|
|
275
|
+
if (this.soundPress)
|
|
276
|
+
this.soundPress.play();
|
|
277
|
+
}
|
|
278
|
+
this.mouseIsHeld = true;
|
|
279
|
+
}
|
|
280
|
+
if (!mouseDown && this.mouseIsHeld && this.interactive)
|
|
281
|
+
{
|
|
282
|
+
this.onClick();
|
|
283
|
+
if (this.soundClick)
|
|
284
|
+
this.soundClick.play();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
233
287
|
}
|
|
234
|
-
|
|
288
|
+
if (!mouseDown)
|
|
235
289
|
{
|
|
290
|
+
if (this.mouseIsHeld && this.interactive)
|
|
291
|
+
{
|
|
292
|
+
this.onRelease();
|
|
293
|
+
if (this.soundRelease)
|
|
294
|
+
this.soundRelease.play();
|
|
295
|
+
}
|
|
296
|
+
if (isTouchDevice)
|
|
297
|
+
this.mouseIsOver = false;
|
|
236
298
|
this.mouseIsHeld = false;
|
|
237
|
-
this.onRelease();
|
|
238
299
|
}
|
|
300
|
+
|
|
301
|
+
if (this.mouseIsOver != mouseWasOver)
|
|
302
|
+
this.mouseIsOver ? this.onEnter() : this.onLeave();
|
|
239
303
|
}
|
|
240
304
|
|
|
241
305
|
/** Render the object, called automatically by plugin once each frame */
|
|
242
306
|
render()
|
|
243
307
|
{
|
|
244
308
|
if (this.size.x && this.size.y)
|
|
245
|
-
uiSystem.drawRect(this.pos, this.size, this.color, this.lineWidth, this.lineColor);
|
|
309
|
+
uiSystem.drawRect(this.pos, this.size, this.color, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** Special update for when object is invisible */
|
|
313
|
+
updateInvisible()
|
|
314
|
+
{
|
|
315
|
+
// reset input state when not visible
|
|
316
|
+
this.mouseIsOver = this.mouseIsHeld = false;
|
|
246
317
|
}
|
|
247
318
|
|
|
248
319
|
/** Called when the mouse enters the object */
|
|
249
|
-
onEnter()
|
|
320
|
+
onEnter()
|
|
321
|
+
{}
|
|
250
322
|
|
|
251
323
|
/** Called when the mouse leaves the object */
|
|
252
|
-
onLeave()
|
|
324
|
+
onLeave()
|
|
325
|
+
{}
|
|
253
326
|
|
|
254
327
|
/** Called when the mouse is pressed while over the object */
|
|
255
|
-
onPress()
|
|
328
|
+
onPress()
|
|
329
|
+
{}
|
|
256
330
|
|
|
257
331
|
/** Called when the mouse is released while over the object */
|
|
258
|
-
onRelease()
|
|
332
|
+
onRelease()
|
|
333
|
+
{}
|
|
334
|
+
|
|
335
|
+
/** Called when user clicks on this object */
|
|
336
|
+
onClick()
|
|
337
|
+
{}
|
|
259
338
|
|
|
260
339
|
/** Called when the state of this object changes */
|
|
261
|
-
onChange()
|
|
262
|
-
}
|
|
340
|
+
onChange()
|
|
341
|
+
{}
|
|
342
|
+
};
|
|
263
343
|
|
|
264
344
|
///////////////////////////////////////////////////////////////////////////////
|
|
265
345
|
/**
|
|
@@ -346,13 +426,18 @@ class UIButton extends UIObject
|
|
|
346
426
|
|
|
347
427
|
/** @property {string} */
|
|
348
428
|
this.text = text;
|
|
429
|
+
/** @property {Color} */
|
|
430
|
+
this.disabledColor = uiSystem.defaultDisabledColor;
|
|
431
|
+
/** @property {boolean} */
|
|
432
|
+
this.disabled = false;
|
|
433
|
+
this.interactive = true;
|
|
349
434
|
this.color = color;
|
|
350
435
|
}
|
|
351
436
|
render()
|
|
352
437
|
{
|
|
353
|
-
const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
|
|
354
|
-
const color = this.mouseIsOver? this.hoverColor : this.color;
|
|
355
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor);
|
|
438
|
+
const lineColor = this.mouseIsHeld && !this.disabled ? this.color : this.lineColor;
|
|
439
|
+
const color = this.disabled ? this.disabledColor : this.mouseIsOver ? this.hoverColor : this.color;
|
|
440
|
+
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
356
441
|
|
|
357
442
|
const textScale = .8; // scale text to fit in button
|
|
358
443
|
const textSize = vec2(this.size.x, this.textHeight || this.size.y*textScale);
|
|
@@ -379,8 +464,9 @@ class UICheckbox extends UIObject
|
|
|
379
464
|
|
|
380
465
|
/** @property {boolean} */
|
|
381
466
|
this.checked = checked;
|
|
467
|
+
this.interactive = true;
|
|
382
468
|
}
|
|
383
|
-
|
|
469
|
+
onClick()
|
|
384
470
|
{
|
|
385
471
|
this.checked = !this.checked;
|
|
386
472
|
this.onChange();
|
|
@@ -388,7 +474,7 @@ class UICheckbox extends UIObject
|
|
|
388
474
|
render()
|
|
389
475
|
{
|
|
390
476
|
const color = this.mouseIsOver? this.hoverColor : this.color;
|
|
391
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, this.lineColor);
|
|
477
|
+
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
392
478
|
if (this.checked)
|
|
393
479
|
{
|
|
394
480
|
// draw an X if checked
|
|
@@ -421,8 +507,10 @@ class UIScrollbar extends UIObject
|
|
|
421
507
|
this.value = value;
|
|
422
508
|
/** @property {string} */
|
|
423
509
|
this.text = text;
|
|
424
|
-
|
|
510
|
+
/** @property {Color} */
|
|
425
511
|
this.handleColor = handleColor;
|
|
512
|
+
this.color = color;
|
|
513
|
+
this.interactive = true;
|
|
426
514
|
}
|
|
427
515
|
update()
|
|
428
516
|
{
|
|
@@ -442,19 +530,19 @@ class UIScrollbar extends UIObject
|
|
|
442
530
|
{
|
|
443
531
|
const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
|
|
444
532
|
const color = this.mouseIsOver? this.hoverColor : this.color;
|
|
445
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor);
|
|
533
|
+
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
446
534
|
|
|
447
535
|
const handleSize = vec2(this.size.y);
|
|
448
536
|
const handleWidth = this.size.x - handleSize.x;
|
|
449
537
|
const p1 = this.pos.x - handleWidth/2;
|
|
450
538
|
const p2 = this.pos.x + handleWidth/2;
|
|
451
|
-
const handlePos = vec2(lerp(
|
|
539
|
+
const handlePos = vec2(lerp(p1, p2, this.value), this.pos.y);
|
|
452
540
|
const barColor = this.mouseIsHeld ? this.color : this.handleColor;
|
|
453
|
-
uiSystem.drawRect(handlePos, handleSize, barColor, this.lineWidth, this.lineColor);
|
|
541
|
+
uiSystem.drawRect(handlePos, handleSize, barColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
454
542
|
|
|
455
543
|
const textScale = .8; // scale text to fit in scrollbar
|
|
456
544
|
const textSize = vec2(this.size.x, this.textHeight || this.size.y*textScale);
|
|
457
545
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
458
546
|
this.textColor, 0, undefined, this.align, this.font);
|
|
459
547
|
}
|
|
460
|
-
}
|
|
548
|
+
}
|
package/plugins/zzfxm.js
CHANGED
package/reference.md
CHANGED
|
@@ -39,11 +39,11 @@ sign(value) // Get the sign of value
|
|
|
39
39
|
mod(dividend, divisor=1) // Get remainder of division
|
|
40
40
|
clamp(value, min=0, max=1) // Clamps between values
|
|
41
41
|
percent(value, valueA, valueB) // Get percentage between values
|
|
42
|
-
lerp(
|
|
42
|
+
lerp(valueA, valueB, percent) // Linearly interpolates between values
|
|
43
43
|
distanceWrap(valueA, valueB, wrapSize=1) // Signed wrapped distance between values
|
|
44
|
-
lerpWrap(
|
|
44
|
+
lerpWrap(valueA, valueB, percent, wrapSize=1) // Linearly interpolates with wrapping
|
|
45
45
|
distanceAngle(angleA, angleB) // Signed wrapped distance between angles
|
|
46
|
-
lerpAngle(
|
|
46
|
+
lerpAngle(angleA, angleB, percent) // Linearly interpolates with wrapping
|
|
47
47
|
smoothStep(percent) // Applies smoothstep function
|
|
48
48
|
nearestPowerOfTwo(value) // Returns the nearest power of two
|
|
49
49
|
isOverlapping(pointA, sizeA, pointB, sizeB) // Checks if bounding boxes overlap
|
|
@@ -55,7 +55,7 @@ formatTime(t) // Formats seconds for display
|
|
|
55
55
|
rand(valueA=1, valueB=0) // Random float between values
|
|
56
56
|
randInt(valueA, valueB=0) // Random integer between values
|
|
57
57
|
randSign() // Randomly bool either -1 or 1
|
|
58
|
-
|
|
58
|
+
randVec2(length=1) // Random Vector2 with the passed in length
|
|
59
59
|
randInCircle(radius=1, minRadius=0) // Random Vector2 within a circle
|
|
60
60
|
randColor(colorA, colorB, linear) // Random color between values
|
|
61
61
|
|
|
@@ -148,7 +148,7 @@ TileInfo.size // Size of tile in pixels
|
|
|
148
148
|
TileInfo.textureIndex // Texture index to use
|
|
149
149
|
TileInfo.offset(offset) // Offset this tile by a certain amount in pixels
|
|
150
150
|
TileInfo.frame(frame) // Offset this tile by a number of animation frames
|
|
151
|
-
TileInfo.
|
|
151
|
+
TileInfo.textureInfo // The texture info for this tile
|
|
152
152
|
|
|
153
153
|
// Texture Info Object
|
|
154
154
|
TextureInfo(image) // Created automatically for each image
|
|
@@ -176,7 +176,6 @@ canvasPixelated = true // Use nearest neighbor canvas scaling for more pi
|
|
|
176
176
|
tilesPixelated = true // Disable filtering for crisper pixel art
|
|
177
177
|
showSplashScreen = false // Show the LittleJS splash screen on startup
|
|
178
178
|
glEnable = true // Enable fast WebGL rendering
|
|
179
|
-
glOverlay = true // Prevent compositing the WebGL canvas
|
|
180
179
|
|
|
181
180
|
// Tile sheet settings
|
|
182
181
|
tileSizeDefault = (16,16) // Default size of tiles in pixels
|
|
@@ -307,7 +306,7 @@ EngineObject.mirror // Should it flip along y axis when rendered
|
|
|
307
306
|
EngineObject.mass // Weight of object, static if 0
|
|
308
307
|
EngineObject.damping // How much to slow velocity each frame (0-1)
|
|
309
308
|
EngineObject.angleDamping // How much to slow rotation each frame (0-1)
|
|
310
|
-
EngineObject.
|
|
309
|
+
EngineObject.restitution // How bouncy is it when colliding (0-1)
|
|
311
310
|
EngineObject.friction // How much friction when sliding (0-1)
|
|
312
311
|
EngineObject.gravityScale // How much to scale gravity by
|
|
313
312
|
EngineObject.renderOrder // Objects are sorted by render order
|
|
@@ -319,7 +318,7 @@ enablePhysicsSolver = true // Enable collisions between objects?
|
|
|
319
318
|
objectDefaultMass = 1 // Default object mass for collisions
|
|
320
319
|
objectDefaultDamping = 1 // How much to slow velocity by each frame (0-1)
|
|
321
320
|
objectDefaultAngleDamping = 1 // How much to slow angular velocity each frame (0-1)
|
|
322
|
-
|
|
321
|
+
objectDefaultRestitution = 0 // How much to bounce when a collision occurs (0-1)
|
|
323
322
|
objectDefaultFriction = .8 // How much to slow when touching (0-1)
|
|
324
323
|
objectMaxSpeed = 1 // Clamp max speed to avoid fast objects missing collisions
|
|
325
324
|
gravity = (0,0) // How much gravity to apply to objects
|
|
@@ -358,11 +357,12 @@ TileLayerData.clear() // Clear this tile data
|
|
|
358
357
|
|
|
359
358
|
// Tile Collision Layer
|
|
360
359
|
TileCollisionLayer(position, size, tileInfo=tile()) // Create a tile collision layer object
|
|
361
|
-
TileCollisionLayer.size // Size of the tile collision layer
|
|
362
360
|
TileCollisionLayer.setCollisionData(pos, data=1) // Set tile collision data at pos
|
|
363
|
-
|
|
361
|
+
tileCollisionGetData(pos) // Get tile collision data at pos
|
|
364
362
|
tileCollisionTest(pos, size=(0,0), object) // Check if collision should occur
|
|
365
363
|
tileCollisionRaycast(posStart, posEnd, object) // Return the center of tile if hit
|
|
364
|
+
tileCollisionLoad(tileMapData, tileInfo) // Load tile layers from exported data
|
|
365
|
+
|
|
366
366
|
```
|
|
367
367
|
|
|
368
368
|
## LittleJS Particle System
|
package/src/engine.js
CHANGED
|
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
|
|
|
30
30
|
* @type {string}
|
|
31
31
|
* @default
|
|
32
32
|
* @memberof Engine */
|
|
33
|
-
const engineVersion = '1.
|
|
33
|
+
const engineVersion = '1.13.1';
|
|
34
34
|
|
|
35
35
|
/** Frames per second to update
|
|
36
36
|
* @type {number}
|
|
@@ -75,10 +75,15 @@ let timeReal = 0;
|
|
|
75
75
|
* @memberof Engine */
|
|
76
76
|
let paused = false;
|
|
77
77
|
|
|
78
|
+
/** Get if game is paused
|
|
79
|
+
* @return {boolean}
|
|
80
|
+
* @memberof Engine */
|
|
81
|
+
function getPaused() { return paused; }
|
|
82
|
+
|
|
78
83
|
/** Set if game is paused
|
|
79
|
-
* @param {boolean} isPaused
|
|
84
|
+
* @param {boolean} [isPaused]
|
|
80
85
|
* @memberof Engine */
|
|
81
|
-
function setPaused(isPaused) { paused = isPaused; }
|
|
86
|
+
function setPaused(isPaused=true) { paused = isPaused; }
|
|
82
87
|
|
|
83
88
|
// Frame time tracking
|
|
84
89
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
@@ -101,16 +106,26 @@ function engineAddPlugin(updateFunction, renderFunction)
|
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
///////////////////////////////////////////////////////////////////////////////
|
|
104
|
-
// Main
|
|
109
|
+
// Main Engine Functions
|
|
105
110
|
|
|
106
111
|
/** Startup LittleJS engine with your callback functions
|
|
107
|
-
* @param {Function|function():Promise} gameInit - Called once after the engine starts up
|
|
108
|
-
* @param {Function} gameUpdate - Called every frame before objects are updated
|
|
109
|
-
* @param {Function} gameUpdatePost - Called after physics and objects are updated, even when paused
|
|
110
|
-
* @param {Function} gameRender - Called before objects are rendered, for drawing
|
|
111
|
-
* @param {Function} gameRenderPost - Called after objects are rendered,
|
|
112
|
-
* @param {Array<string>} [imageSources=[]] - List of
|
|
113
|
-
* @param {HTMLElement} [rootElement] - Root element to attach to,
|
|
112
|
+
* @param {Function|function():Promise} gameInit - Called once after the engine starts up, can be async for loading
|
|
113
|
+
* @param {Function} gameUpdate - Called every frame before objects are updated (60fps), use for game logic
|
|
114
|
+
* @param {Function} gameUpdatePost - Called after physics and objects are updated, even when paused, use for UI updates
|
|
115
|
+
* @param {Function} gameRender - Called before objects are rendered, use for drawing backgrounds/world elements
|
|
116
|
+
* @param {Function} gameRenderPost - Called after objects are rendered, use for drawing UI/overlays
|
|
117
|
+
* @param {Array<string>} [imageSources=[]] - List of image file paths to preload (e.g., ['player.png', 'tiles.png'])
|
|
118
|
+
* @param {HTMLElement} [rootElement] - Root DOM element to attach canvas to, defaults to document.body
|
|
119
|
+
* @example
|
|
120
|
+
* // Basic engine startup
|
|
121
|
+
* engineInit(
|
|
122
|
+
* () => { console.log('Game initialized!'); }, // gameInit
|
|
123
|
+
* () => { updatePlayer(); }, // gameUpdate
|
|
124
|
+
* () => { updateUI(); }, // gameUpdatePost
|
|
125
|
+
* () => { drawBackground(); }, // gameRender
|
|
126
|
+
* () => { drawHUD(); }, // gameRenderPost
|
|
127
|
+
* ['tiles.png', 'tilesLevel.png'] // images to load
|
|
128
|
+
* );
|
|
114
129
|
* @memberof Engine */
|
|
115
130
|
async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
116
131
|
{
|
|
@@ -145,7 +160,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
145
160
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
146
161
|
frameTimeLastMS = frameTimeMS;
|
|
147
162
|
if (debug || showWatermark)
|
|
148
|
-
averageFPS = lerp(
|
|
163
|
+
averageFPS = lerp(averageFPS, 1e3/(frameTimeDeltaMS||1), .05);
|
|
149
164
|
const debugSpeedUp = debug && keyIsDown('Equal'); // +
|
|
150
165
|
const debugSpeedDown = debug && keyIsDown('Minus'); // -
|
|
151
166
|
if (debug) // +/- to speed/slow time
|
|
@@ -154,9 +169,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
154
169
|
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
155
170
|
if (!debugSpeedUp)
|
|
156
171
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp min framerate
|
|
157
|
-
if (
|
|
158
|
-
frameTimeBufferMS = 0; //
|
|
159
|
-
|
|
172
|
+
if (debugVideoCaptureIsActive())
|
|
173
|
+
frameTimeBufferMS = 0; // no time smoothing when capturing video
|
|
160
174
|
updateCanvas();
|
|
161
175
|
|
|
162
176
|
if (paused)
|
|
@@ -215,11 +229,12 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
215
229
|
pluginRenderList.forEach(f=>f());
|
|
216
230
|
touchGamepadRender();
|
|
217
231
|
debugRender();
|
|
218
|
-
|
|
232
|
+
glFlush();
|
|
233
|
+
debugVideoCaptureUpdate();
|
|
219
234
|
|
|
220
|
-
if (showWatermark)
|
|
235
|
+
if (showWatermark && !debugVideoCaptureIsActive())
|
|
221
236
|
{
|
|
222
|
-
// update fps
|
|
237
|
+
// update fps display
|
|
223
238
|
overlayContext.textAlign = 'right';
|
|
224
239
|
overlayContext.textBaseline = 'top';
|
|
225
240
|
overlayContext.font = '1em monospace';
|
|
@@ -230,11 +245,10 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
230
245
|
overlayContext.fillText(text, mainCanvas.width-3, 3);
|
|
231
246
|
overlayContext.fillStyle = '#fff';
|
|
232
247
|
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
233
|
-
drawCount = 0;
|
|
234
248
|
}
|
|
249
|
+
if (debug || showWatermark)
|
|
250
|
+
drawCount = 0;
|
|
235
251
|
}
|
|
236
|
-
|
|
237
|
-
debugVideoCaptureUpdate();
|
|
238
252
|
requestAnimationFrame(engineUpdate);
|
|
239
253
|
}
|
|
240
254
|
|
|
@@ -281,6 +295,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
281
295
|
// setup html
|
|
282
296
|
const styleRoot =
|
|
283
297
|
'margin:0;' + // fill the window
|
|
298
|
+
'overflow:hidden;' + // no scroll bars
|
|
284
299
|
'background:#000;' + // set background color
|
|
285
300
|
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
286
301
|
'user-select:none;' + // prevent hold to select
|
|
@@ -289,8 +304,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
289
304
|
'touch-action:none;' + // prevent mobile pinch to resize
|
|
290
305
|
'-webkit-touch-callout:none');// compatibility for ios
|
|
291
306
|
rootElement.style.cssText = styleRoot;
|
|
292
|
-
|
|
293
|
-
|
|
307
|
+
drawCanvas = mainCanvas = document.createElement('canvas');
|
|
308
|
+
rootElement.appendChild(mainCanvas);
|
|
309
|
+
drawContext = mainContext = mainCanvas.getContext('2d');
|
|
294
310
|
|
|
295
311
|
// init stuff and start engine
|
|
296
312
|
inputInit();
|
|
@@ -299,7 +315,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
299
315
|
glInit();
|
|
300
316
|
|
|
301
317
|
// create overlay canvas for hud to appear above gl canvas
|
|
302
|
-
|
|
318
|
+
overlayCanvas = document.createElement('canvas')
|
|
319
|
+
rootElement.appendChild(overlayCanvas);
|
|
303
320
|
overlayContext = overlayCanvas.getContext('2d');
|
|
304
321
|
|
|
305
322
|
// set canvas style
|
|
@@ -317,7 +334,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
317
334
|
const image = new Image;
|
|
318
335
|
image.onerror = image.onload = ()=>
|
|
319
336
|
{
|
|
320
|
-
|
|
337
|
+
const textureInfo = new TextureInfo(image);
|
|
338
|
+
textureInfo.createWebGLTexture();
|
|
339
|
+
textureInfos[textureIndex] = textureInfo;
|
|
321
340
|
resolve();
|
|
322
341
|
}
|
|
323
342
|
image.crossOrigin = 'anonymous';
|
|
@@ -330,7 +349,9 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
330
349
|
// no images to load
|
|
331
350
|
promises.push(new Promise(resolve =>
|
|
332
351
|
{
|
|
333
|
-
|
|
352
|
+
const textureInfo = new TextureInfo(new Image);
|
|
353
|
+
textureInfos[0] = textureInfo;
|
|
354
|
+
textureInfo.createWebGLTexture();
|
|
334
355
|
resolve();
|
|
335
356
|
}));
|
|
336
357
|
}
|
|
@@ -345,7 +366,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
345
366
|
updateSplash();
|
|
346
367
|
function updateSplash()
|
|
347
368
|
{
|
|
348
|
-
|
|
369
|
+
inputClear();
|
|
349
370
|
drawEngineSplashScreen(t+=.01);
|
|
350
371
|
t>1 ? resolve() : setTimeout(updateSplash, 16);
|
|
351
372
|
}
|
|
@@ -470,7 +491,7 @@ function drawEngineSplashScreen(t)
|
|
|
470
491
|
const p3 = percent(t, 1, .8);
|
|
471
492
|
const p4 = percent(t, 0, .5);
|
|
472
493
|
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,Math.hypot(w,h)*.7);
|
|
473
|
-
g.addColorStop(0,hsl(0,0,lerp(
|
|
494
|
+
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
474
495
|
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
475
496
|
x.save();
|
|
476
497
|
x.fillStyle = g;
|
|
@@ -621,4 +642,4 @@ function drawEngineSplashScreen(t)
|
|
|
621
642
|
}
|
|
622
643
|
|
|
623
644
|
x.restore();
|
|
624
|
-
}
|
|
645
|
+
}
|
package/src/engineAudio.js
CHANGED
|
@@ -122,11 +122,20 @@ class Sound
|
|
|
122
122
|
this.gainNode.gain.value = volume;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
/** Stop the last instance of this sound that was played
|
|
126
|
-
|
|
125
|
+
/** Stop the last instance of this sound that was played
|
|
126
|
+
* @param {number} [fadeTime] - How long to fade out (seconds)
|
|
127
|
+
*/
|
|
128
|
+
stop(fadeTime=0)
|
|
127
129
|
{
|
|
128
|
-
if (this.source)
|
|
129
|
-
|
|
130
|
+
if (!this.source)
|
|
131
|
+
return;
|
|
132
|
+
|
|
133
|
+
// ramp off gain
|
|
134
|
+
const startFade = audioContext.currentTime;
|
|
135
|
+
const endFade = startFade + fadeTime;
|
|
136
|
+
this.gainNode.gain.linearRampToValueAtTime(1, startFade);
|
|
137
|
+
this.gainNode.gain.linearRampToValueAtTime(0, endFade);
|
|
138
|
+
this.source.stop(endFade);
|
|
130
139
|
this.source = undefined;
|
|
131
140
|
}
|
|
132
141
|
|
|
@@ -469,4 +478,4 @@ function zzfxG
|
|
|
469
478
|
}
|
|
470
479
|
|
|
471
480
|
return b; // return sample buffer
|
|
472
|
-
}
|
|
481
|
+
}
|