littlejsengine 1.12.4 → 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/box2d.wasm.js +630 -0
- package/dist/box2d.wasm.wasm +0 -0
- package/dist/littlejs.d.ts +437 -219
- package/dist/littlejs.esm.js +5950 -5307
- package/dist/littlejs.esm.min.js +4 -1
- package/dist/littlejs.js +5921 -5295
- package/dist/littlejs.min.js +4 -1
- package/dist/littlejs.release.js +5485 -4886
- package/examples/box2d/game.js +37 -42
- package/examples/box2d/gameObjects.js +41 -37
- package/examples/box2d/index.html +2 -2
- package/examples/box2d/scenes.js +24 -20
- package/examples/box2d/tiles.png +0 -0
- 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 +410 -159
- package/examples/module/game.js +1 -1
- package/examples/module/index.html +1 -1
- package/examples/platformer/game.js +6 -15
- 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 +24 -3
- package/examples/shorts/box2d.js +46 -0
- package/examples/shorts/box2dCar.js +45 -0
- 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/postProcess.js +45 -0
- 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 +36 -0
- package/examples/starter/build.bat +6 -1
- package/examples/starter/game.js +4 -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 +9 -25
- package/examples/uiSystem/index.html +1 -1
- package/package.json +1 -1
- package/plugins/box2d.js +29 -35
- package/plugins/drawUtilities.js +126 -0
- package/plugins/newgrounds.js +1 -1
- package/plugins/pluginExport.js +8 -2
- package/plugins/postProcess.js +2 -2
- package/plugins/uiSystem.js +162 -68
- package/plugins/zzfxm.js +1 -1
- package/reference.md +10 -10
- package/src/engine.js +59 -39
- package/src/engineAudio.js +38 -20
- package/src/engineBuild.bat +6 -1
- package/src/engineBuild.js +32 -7
- package/src/engineDebug.js +53 -26
- package/src/engineDraw.js +108 -57
- package/src/engineExport.js +22 -9
- package/src/engineInput.js +112 -40
- 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 +198 -130
- package/src/engineWebGL.js +58 -35
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,24 +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
|
-
/** @property {
|
|
208
|
+
/** @property {number} - override for text height */
|
|
209
|
+
this.textHeight = undefined;
|
|
210
|
+
/** @property {boolean} - should this object be drawn */
|
|
181
211
|
this.visible = true;
|
|
182
|
-
/** @property {Array<UIObject>} */
|
|
212
|
+
/** @property {Array<UIObject>} - a list of this object's children */
|
|
183
213
|
this.children = [];
|
|
184
|
-
/** @property {UIObject} */
|
|
214
|
+
/** @property {UIObject} - this object's parent, position is in parent space */
|
|
185
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;
|
|
186
230
|
uiSystem.uiObjects.push(this);
|
|
187
231
|
}
|
|
188
232
|
|
|
@@ -209,55 +253,93 @@ class UIObject
|
|
|
209
253
|
/** Update the object, called automatically by plugin once each frame */
|
|
210
254
|
update()
|
|
211
255
|
{
|
|
212
|
-
// track mouse input
|
|
213
256
|
const mouseWasOver = this.mouseIsOver;
|
|
257
|
+
const mousePress = mouseWasPressed(0);
|
|
214
258
|
const mouseDown = mouseIsDown(0);
|
|
215
|
-
if (!mouseDown ||
|
|
259
|
+
if (mousePress || !mouseDown || this.mouseIsHeld)
|
|
216
260
|
{
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
this.mouseIsOver = false;
|
|
220
|
-
if (this.mouseIsOver && !mouseWasOver)
|
|
221
|
-
this.onEnter();
|
|
222
|
-
if (!this.mouseIsOver && mouseWasOver)
|
|
223
|
-
this.onLeave();
|
|
261
|
+
const size = this.size.add(vec2(isTouchDevice && this.extraTouchSize || 0));
|
|
262
|
+
this.mouseIsOver = isOverlapping(this.pos, size, mousePosScreen);
|
|
224
263
|
}
|
|
225
|
-
if (
|
|
264
|
+
if (this.mouseIsOver)
|
|
226
265
|
{
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
|
|
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
|
+
}
|
|
231
287
|
}
|
|
232
|
-
|
|
288
|
+
if (!mouseDown)
|
|
233
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;
|
|
234
298
|
this.mouseIsHeld = false;
|
|
235
|
-
this.onRelease();
|
|
236
299
|
}
|
|
300
|
+
|
|
301
|
+
if (this.mouseIsOver != mouseWasOver)
|
|
302
|
+
this.mouseIsOver ? this.onEnter() : this.onLeave();
|
|
237
303
|
}
|
|
238
304
|
|
|
239
305
|
/** Render the object, called automatically by plugin once each frame */
|
|
240
306
|
render()
|
|
241
307
|
{
|
|
242
308
|
if (this.size.x && this.size.y)
|
|
243
|
-
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;
|
|
244
317
|
}
|
|
245
318
|
|
|
246
319
|
/** Called when the mouse enters the object */
|
|
247
|
-
onEnter()
|
|
320
|
+
onEnter()
|
|
321
|
+
{}
|
|
248
322
|
|
|
249
323
|
/** Called when the mouse leaves the object */
|
|
250
|
-
onLeave()
|
|
324
|
+
onLeave()
|
|
325
|
+
{}
|
|
251
326
|
|
|
252
327
|
/** Called when the mouse is pressed while over the object */
|
|
253
|
-
onPress()
|
|
328
|
+
onPress()
|
|
329
|
+
{}
|
|
254
330
|
|
|
255
331
|
/** Called when the mouse is released while over the object */
|
|
256
|
-
onRelease()
|
|
332
|
+
onRelease()
|
|
333
|
+
{}
|
|
334
|
+
|
|
335
|
+
/** Called when user clicks on this object */
|
|
336
|
+
onClick()
|
|
337
|
+
{}
|
|
257
338
|
|
|
258
339
|
/** Called when the state of this object changes */
|
|
259
|
-
onChange()
|
|
260
|
-
}
|
|
340
|
+
onChange()
|
|
341
|
+
{}
|
|
342
|
+
};
|
|
261
343
|
|
|
262
344
|
///////////////////////////////////////////////////////////////////////////////
|
|
263
345
|
/**
|
|
@@ -287,7 +369,8 @@ class UIText extends UIObject
|
|
|
287
369
|
}
|
|
288
370
|
render()
|
|
289
371
|
{
|
|
290
|
-
|
|
372
|
+
const textSize = vec2(this.size.x, this.textHeight || this.size.y);
|
|
373
|
+
uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
|
|
291
374
|
}
|
|
292
375
|
}
|
|
293
376
|
|
|
@@ -343,14 +426,21 @@ class UIButton extends UIObject
|
|
|
343
426
|
|
|
344
427
|
/** @property {string} */
|
|
345
428
|
this.text = text;
|
|
429
|
+
/** @property {Color} */
|
|
430
|
+
this.disabledColor = uiSystem.defaultDisabledColor;
|
|
431
|
+
/** @property {boolean} */
|
|
432
|
+
this.disabled = false;
|
|
433
|
+
this.interactive = true;
|
|
346
434
|
this.color = color;
|
|
347
435
|
}
|
|
348
436
|
render()
|
|
349
437
|
{
|
|
350
|
-
const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
|
|
351
|
-
const color = this.mouseIsOver? this.hoverColor : this.color;
|
|
352
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor);
|
|
353
|
-
|
|
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);
|
|
441
|
+
|
|
442
|
+
const textScale = .8; // scale text to fit in button
|
|
443
|
+
const textSize = vec2(this.size.x, this.textHeight || this.size.y*textScale);
|
|
354
444
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
355
445
|
this.textColor, 0, undefined, this.align, this.font);
|
|
356
446
|
}
|
|
@@ -374,8 +464,9 @@ class UICheckbox extends UIObject
|
|
|
374
464
|
|
|
375
465
|
/** @property {boolean} */
|
|
376
466
|
this.checked = checked;
|
|
467
|
+
this.interactive = true;
|
|
377
468
|
}
|
|
378
|
-
|
|
469
|
+
onClick()
|
|
379
470
|
{
|
|
380
471
|
this.checked = !this.checked;
|
|
381
472
|
this.onChange();
|
|
@@ -383,7 +474,7 @@ class UICheckbox extends UIObject
|
|
|
383
474
|
render()
|
|
384
475
|
{
|
|
385
476
|
const color = this.mouseIsOver? this.hoverColor : this.color;
|
|
386
|
-
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);
|
|
387
478
|
if (this.checked)
|
|
388
479
|
{
|
|
389
480
|
// draw an X if checked
|
|
@@ -416,8 +507,10 @@ class UIScrollbar extends UIObject
|
|
|
416
507
|
this.value = value;
|
|
417
508
|
/** @property {string} */
|
|
418
509
|
this.text = text;
|
|
419
|
-
|
|
510
|
+
/** @property {Color} */
|
|
420
511
|
this.handleColor = handleColor;
|
|
512
|
+
this.color = color;
|
|
513
|
+
this.interactive = true;
|
|
421
514
|
}
|
|
422
515
|
update()
|
|
423
516
|
{
|
|
@@ -437,18 +530,19 @@ class UIScrollbar extends UIObject
|
|
|
437
530
|
{
|
|
438
531
|
const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
|
|
439
532
|
const color = this.mouseIsOver? this.hoverColor : this.color;
|
|
440
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor);
|
|
533
|
+
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
441
534
|
|
|
442
535
|
const handleSize = vec2(this.size.y);
|
|
443
536
|
const handleWidth = this.size.x - handleSize.x;
|
|
444
537
|
const p1 = this.pos.x - handleWidth/2;
|
|
445
538
|
const p2 = this.pos.x + handleWidth/2;
|
|
446
|
-
const handlePos = vec2(lerp(
|
|
539
|
+
const handlePos = vec2(lerp(p1, p2, this.value), this.pos.y);
|
|
447
540
|
const barColor = this.mouseIsHeld ? this.color : this.handleColor;
|
|
448
|
-
uiSystem.drawRect(handlePos, handleSize, barColor, this.lineWidth, this.lineColor);
|
|
541
|
+
uiSystem.drawRect(handlePos, handleSize, barColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
449
542
|
|
|
450
|
-
const
|
|
543
|
+
const textScale = .8; // scale text to fit in scrollbar
|
|
544
|
+
const textSize = vec2(this.size.x, this.textHeight || this.size.y*textScale);
|
|
451
545
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
452
546
|
this.textColor, 0, undefined, this.align, this.font);
|
|
453
547
|
}
|
|
454
|
-
}
|
|
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,18 +106,28 @@ 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
|
-
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
130
|
+
async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
116
131
|
{
|
|
117
132
|
ASSERT(!mainContext, 'engine already initialized');
|
|
118
133
|
ASSERT(Array.isArray(imageSources), 'pass in images as array');
|
|
@@ -145,7 +160,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
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 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
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 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
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 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
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
|
|
|
@@ -269,20 +283,19 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
269
283
|
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
270
284
|
}
|
|
271
285
|
|
|
272
|
-
|
|
286
|
+
// wait for gameInit to load
|
|
287
|
+
async function startEngine()
|
|
273
288
|
{
|
|
274
|
-
|
|
289
|
+
await gameInit();
|
|
290
|
+
engineUpdate();
|
|
275
291
|
}
|
|
276
|
-
|
|
277
292
|
if (headlessMode)
|
|
278
|
-
|
|
279
|
-
startEngine();
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
293
|
+
return startEngine();
|
|
282
294
|
|
|
283
295
|
// setup html
|
|
284
296
|
const styleRoot =
|
|
285
297
|
'margin:0;' + // fill the window
|
|
298
|
+
'overflow:hidden;' + // no scroll bars
|
|
286
299
|
'background:#000;' + // set background color
|
|
287
300
|
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
288
301
|
'user-select:none;' + // prevent hold to select
|
|
@@ -291,8 +304,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
291
304
|
'touch-action:none;' + // prevent mobile pinch to resize
|
|
292
305
|
'-webkit-touch-callout:none');// compatibility for ios
|
|
293
306
|
rootElement.style.cssText = styleRoot;
|
|
294
|
-
|
|
295
|
-
|
|
307
|
+
drawCanvas = mainCanvas = document.createElement('canvas');
|
|
308
|
+
rootElement.appendChild(mainCanvas);
|
|
309
|
+
drawContext = mainContext = mainCanvas.getContext('2d');
|
|
296
310
|
|
|
297
311
|
// init stuff and start engine
|
|
298
312
|
inputInit();
|
|
@@ -301,7 +315,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
301
315
|
glInit();
|
|
302
316
|
|
|
303
317
|
// create overlay canvas for hud to appear above gl canvas
|
|
304
|
-
|
|
318
|
+
overlayCanvas = document.createElement('canvas')
|
|
319
|
+
rootElement.appendChild(overlayCanvas);
|
|
305
320
|
overlayContext = overlayCanvas.getContext('2d');
|
|
306
321
|
|
|
307
322
|
// set canvas style
|
|
@@ -319,7 +334,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
319
334
|
const image = new Image;
|
|
320
335
|
image.onerror = image.onload = ()=>
|
|
321
336
|
{
|
|
322
|
-
|
|
337
|
+
const textureInfo = new TextureInfo(image);
|
|
338
|
+
textureInfo.createWebGLTexture();
|
|
339
|
+
textureInfos[textureIndex] = textureInfo;
|
|
323
340
|
resolve();
|
|
324
341
|
}
|
|
325
342
|
image.crossOrigin = 'anonymous';
|
|
@@ -332,7 +349,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
332
349
|
// no images to load
|
|
333
350
|
promises.push(new Promise(resolve =>
|
|
334
351
|
{
|
|
335
|
-
|
|
352
|
+
const textureInfo = new TextureInfo(new Image);
|
|
353
|
+
textureInfos[0] = textureInfo;
|
|
354
|
+
textureInfo.createWebGLTexture();
|
|
336
355
|
resolve();
|
|
337
356
|
}));
|
|
338
357
|
}
|
|
@@ -347,15 +366,16 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
347
366
|
updateSplash();
|
|
348
367
|
function updateSplash()
|
|
349
368
|
{
|
|
350
|
-
|
|
369
|
+
inputClear();
|
|
351
370
|
drawEngineSplashScreen(t+=.01);
|
|
352
371
|
t>1 ? resolve() : setTimeout(updateSplash, 16);
|
|
353
372
|
}
|
|
354
373
|
}));
|
|
355
374
|
}
|
|
356
375
|
|
|
357
|
-
//
|
|
358
|
-
Promise.all(promises)
|
|
376
|
+
// wait for all the promises to finish
|
|
377
|
+
await Promise.all(promises);
|
|
378
|
+
return startEngine();
|
|
359
379
|
}
|
|
360
380
|
|
|
361
381
|
/** Update each engine object, remove destroyed objects, and update time
|
|
@@ -471,7 +491,7 @@ function drawEngineSplashScreen(t)
|
|
|
471
491
|
const p3 = percent(t, 1, .8);
|
|
472
492
|
const p4 = percent(t, 0, .5);
|
|
473
493
|
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,Math.hypot(w,h)*.7);
|
|
474
|
-
g.addColorStop(0,hsl(0,0,lerp(
|
|
494
|
+
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
475
495
|
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
476
496
|
x.save();
|
|
477
497
|
x.fillStyle = g;
|
|
@@ -622,4 +642,4 @@ function drawEngineSplashScreen(t)
|
|
|
622
642
|
}
|
|
623
643
|
|
|
624
644
|
x.restore();
|
|
625
|
-
}
|
|
645
|
+
}
|