littlejsengine 1.14.7 → 1.14.9
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 +15 -7
- package/dist/littlejs.esm.js +72 -54
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +72 -54
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +72 -54
- package/examples/index.html +10 -5
- package/examples/platformer/gameEffects.js +6 -2
- package/examples/platformer/gameLevel.js +1 -7
- package/examples/shorts/box2dCar.js +2 -1
- package/examples/shorts/piano.js +24 -25
- package/examples/shorts/sound.js +21 -33
- package/examples/shorts/uiSystem.js +13 -6
- package/package.json +1 -1
- package/plugins/uiSystem.js +49 -41
- package/reference.md +1 -0
- package/src/engine.js +1 -1
- package/src/engineDraw.js +1 -1
- package/src/engineInput.js +7 -0
- package/src/engineObject.js +4 -4
- package/src/engineSettings.js +1 -1
- package/src/engineTileLayer.js +2 -2
- package/src/engineUtilities.js +5 -2
- package/src/engineWebGL.js +2 -2
package/plugins/uiSystem.js
CHANGED
|
@@ -63,6 +63,10 @@ class UISystemPlugin
|
|
|
63
63
|
this.uiObjects = [];
|
|
64
64
|
/** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
|
|
65
65
|
this.uiContext = context;
|
|
66
|
+
/** @property {UIObject} - Top most object user is over */
|
|
67
|
+
this.hoverObject = undefined;
|
|
68
|
+
/** @property {UIObject} - Object user is currently interacting with */
|
|
69
|
+
this.activeObject = undefined;
|
|
66
70
|
|
|
67
71
|
engineAddPlugin(uiUpdate, uiRender);
|
|
68
72
|
|
|
@@ -92,6 +96,8 @@ class UISystemPlugin
|
|
|
92
96
|
else
|
|
93
97
|
updateInvisibleObject(o);
|
|
94
98
|
}
|
|
99
|
+
// reset hover object at start of update
|
|
100
|
+
uiSystem.hoverObject = undefined;
|
|
95
101
|
for (let i = uiSystem.uiObjects.length; i--;)
|
|
96
102
|
{
|
|
97
103
|
const o = uiSystem.uiObjects[i];
|
|
@@ -203,6 +209,8 @@ class UIObject
|
|
|
203
209
|
this.size = size.copy();
|
|
204
210
|
/** @property {Color} - Color of the object */
|
|
205
211
|
this.color = uiSystem.defaultColor;
|
|
212
|
+
/** @property {Color} - Color of the object when active, uses color if undefined */
|
|
213
|
+
this.activeColor = undefined;
|
|
206
214
|
/** @property {string} - Text for this ui object */
|
|
207
215
|
this.text = undefined;
|
|
208
216
|
/** @property {Color} - Color when disabled */
|
|
@@ -233,7 +241,7 @@ class UIObject
|
|
|
233
241
|
this.children = [];
|
|
234
242
|
/** @property {UIObject} - This object's parent, position is in parent space */
|
|
235
243
|
this.parent = undefined;
|
|
236
|
-
/** @property {number} -
|
|
244
|
+
/** @property {number} - Added size to make small buttons easier to touch on mobile devices */
|
|
237
245
|
this.extraTouchSize = 0;
|
|
238
246
|
/** @property {Sound} - Sound when interactive element is pressed */
|
|
239
247
|
this.soundPress = uiSystem.defaultSoundPress;
|
|
@@ -241,12 +249,10 @@ class UIObject
|
|
|
241
249
|
this.soundRelease = uiSystem.defaultSoundRelease;
|
|
242
250
|
/** @property {Sound} - Sound when interactive element is clicked */
|
|
243
251
|
this.soundClick = uiSystem.defaultSoundClick;
|
|
244
|
-
/** @property {boolean} - Is the mouse over this element */
|
|
245
|
-
this.mouseIsOver = false;
|
|
246
252
|
/** @property {boolean} - Is this element interactive */
|
|
247
253
|
this.interactive = false;
|
|
248
|
-
/** @property {boolean} -
|
|
249
|
-
this.
|
|
254
|
+
/** @property {boolean} - Activate when dragged over with mouse held down */
|
|
255
|
+
this.dragActivate = false;
|
|
250
256
|
uiSystem.uiObjects.push(this);
|
|
251
257
|
}
|
|
252
258
|
|
|
@@ -273,15 +279,18 @@ class UIObject
|
|
|
273
279
|
/** Update the object, called automatically by plugin once each frame */
|
|
274
280
|
update()
|
|
275
281
|
{
|
|
276
|
-
const
|
|
277
|
-
const
|
|
282
|
+
const wasHover = this.isHoverObject();
|
|
283
|
+
const isActive = this.isActiveObject();
|
|
278
284
|
const mouseDown = mouseIsDown(0);
|
|
279
|
-
|
|
285
|
+
const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
|
|
286
|
+
if (!uiSystem.hoverObject)
|
|
287
|
+
if (mousePress || !mouseDown || isActive)
|
|
280
288
|
{
|
|
281
289
|
const size = this.size.add(vec2(isTouchDevice && this.extraTouchSize || 0));
|
|
282
|
-
|
|
290
|
+
if (isOverlapping(this.pos, size, mousePosScreen))
|
|
291
|
+
uiSystem.hoverObject = this;
|
|
283
292
|
}
|
|
284
|
-
if (this.
|
|
293
|
+
if (this.isHoverObject())
|
|
285
294
|
{
|
|
286
295
|
if (mousePress)
|
|
287
296
|
inputClearKey(0,0,0,1,0); // clear mouse was pressed state
|
|
@@ -294,10 +303,12 @@ class UIObject
|
|
|
294
303
|
this.onPress();
|
|
295
304
|
if (this.soundPress)
|
|
296
305
|
this.soundPress.play();
|
|
306
|
+
if (uiSystem.activeObject && !isActive)
|
|
307
|
+
uiSystem.activeObject.onRelease();
|
|
308
|
+
uiSystem.activeObject = this;
|
|
297
309
|
}
|
|
298
|
-
this.mouseIsHeld = true;
|
|
299
310
|
}
|
|
300
|
-
if (!mouseDown &&
|
|
311
|
+
if (!mouseDown && uiSystem.activeObject === this && this.interactive)
|
|
301
312
|
{
|
|
302
313
|
this.onClick();
|
|
303
314
|
if (this.soundClick)
|
|
@@ -305,35 +316,35 @@ class UIObject
|
|
|
305
316
|
}
|
|
306
317
|
}
|
|
307
318
|
}
|
|
308
|
-
if (
|
|
319
|
+
if (isActive)
|
|
320
|
+
if (!mouseDown || (this.dragActivate && !this.isHoverObject()))
|
|
309
321
|
{
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
this.
|
|
313
|
-
|
|
314
|
-
this.soundRelease.play();
|
|
315
|
-
}
|
|
316
|
-
if (isTouchDevice)
|
|
317
|
-
this.mouseIsOver = false;
|
|
318
|
-
this.mouseIsHeld = false;
|
|
322
|
+
this.onRelease();
|
|
323
|
+
if (this.soundRelease)
|
|
324
|
+
this.soundRelease.play();
|
|
325
|
+
uiSystem.activeObject = undefined;
|
|
319
326
|
}
|
|
320
327
|
|
|
321
|
-
if (this.
|
|
322
|
-
this.
|
|
328
|
+
if (this.isHoverObject() !== wasHover)
|
|
329
|
+
this.isHoverObject() ? this.onEnter() : this.onLeave();
|
|
323
330
|
}
|
|
324
331
|
|
|
325
332
|
/** Render the object, called automatically by plugin once each frame */
|
|
326
333
|
render()
|
|
327
334
|
{
|
|
328
|
-
if (this.size.x
|
|
329
|
-
|
|
335
|
+
if (!this.size.x || !this.size.y) return;
|
|
336
|
+
|
|
337
|
+
const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
|
|
338
|
+
const color = this.interactive ? this.disabled ? this.disabledColor : this.isActiveObject() ? this.activeColor || this.color : this.isHoverObject() ? this.hoverColor : this.color : this.color;
|
|
339
|
+
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
330
340
|
}
|
|
331
341
|
|
|
332
342
|
/** Special update when object is not visible */
|
|
333
343
|
updateInvisible()
|
|
334
344
|
{
|
|
335
345
|
// reset input state when not visible
|
|
336
|
-
|
|
346
|
+
if (this.isActiveObject())
|
|
347
|
+
uiSystem.activeObject = undefined;
|
|
337
348
|
}
|
|
338
349
|
|
|
339
350
|
/** Get the size for text with overrides and scale
|
|
@@ -346,6 +357,12 @@ class UIObject
|
|
|
346
357
|
this.textHeight || this.textScale * this.size.y);
|
|
347
358
|
}
|
|
348
359
|
|
|
360
|
+
/** @return {boolean} - Is the mouse hovering over this element */
|
|
361
|
+
isHoverObject() { return uiSystem.hoverObject === this; }
|
|
362
|
+
|
|
363
|
+
/** @return {boolean} - Is the mouse held onto this element */
|
|
364
|
+
isActiveObject() { return uiSystem.activeObject === this; }
|
|
365
|
+
|
|
349
366
|
/** Called when the mouse enters the object */
|
|
350
367
|
onEnter() {}
|
|
351
368
|
|
|
@@ -456,10 +473,7 @@ class UIButton extends UIObject
|
|
|
456
473
|
}
|
|
457
474
|
render()
|
|
458
475
|
{
|
|
459
|
-
|
|
460
|
-
const lineColor = this.mouseIsHeld && !this.disabled ? this.color : this.lineColor;
|
|
461
|
-
const color = this.disabled ? this.disabledColor : this.mouseIsOver ? this.hoverColor : this.color;
|
|
462
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
476
|
+
super.render();
|
|
463
477
|
|
|
464
478
|
// draw the text scaled to fit
|
|
465
479
|
const textSize = this.getTextSize();
|
|
@@ -500,8 +514,7 @@ class UICheckbox extends UIObject
|
|
|
500
514
|
}
|
|
501
515
|
render()
|
|
502
516
|
{
|
|
503
|
-
|
|
504
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
517
|
+
super.render();
|
|
505
518
|
if (this.checked)
|
|
506
519
|
{
|
|
507
520
|
const p = this.cornerRadius / min(this.size.x, this.size.y) * 2;
|
|
@@ -551,7 +564,7 @@ class UIScrollbar extends UIObject
|
|
|
551
564
|
update()
|
|
552
565
|
{
|
|
553
566
|
super.update();
|
|
554
|
-
if (this.
|
|
567
|
+
if (this.isActiveObject() && this.interactive)
|
|
555
568
|
{
|
|
556
569
|
// check if value changed
|
|
557
570
|
const handleSize = vec2(this.size.y);
|
|
@@ -565,12 +578,7 @@ class UIScrollbar extends UIObject
|
|
|
565
578
|
}
|
|
566
579
|
render()
|
|
567
580
|
{
|
|
568
|
-
|
|
569
|
-
const lineColor = this.interactive && this.mouseIsHeld && !this.disabled ?
|
|
570
|
-
this.color : this.lineColor;
|
|
571
|
-
const color = this.disabled ? this.disabledColor :
|
|
572
|
-
this.interactive && this.mouseIsHeld ? this.hoverColor : this.color;
|
|
573
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
581
|
+
super.render();
|
|
574
582
|
|
|
575
583
|
// draw the scrollbar handle
|
|
576
584
|
const handleSize = vec2(this.size.y);
|
|
@@ -579,7 +587,7 @@ class UIScrollbar extends UIObject
|
|
|
579
587
|
const p2 = this.pos.x + handleWidth/2;
|
|
580
588
|
const handlePos = vec2(lerp(p1, p2, this.value), this.pos.y);
|
|
581
589
|
const handleColor = this.disabled ? this.disabledColor :
|
|
582
|
-
this.interactive && this.
|
|
590
|
+
this.interactive && this.isActiveObject() ? this.color : this.handleColor;
|
|
583
591
|
uiSystem.drawRect(handlePos, handleSize, handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
584
592
|
|
|
585
593
|
// draw the text scaled to fit on the scrollbar
|
package/reference.md
CHANGED
|
@@ -186,6 +186,7 @@ getCameraSize() // Get the camera's visible area in world space
|
|
|
186
186
|
// Display settings
|
|
187
187
|
canvasMaxSize = (1920, 1200) // The max size of the canvas
|
|
188
188
|
canvasFixedSize = (0, 0) // Fixed size of the canvas
|
|
189
|
+
canvasClearColor = BLACK // Color used to clear the canvas at start of frame
|
|
189
190
|
fontDefault = 'arial' // Default font used for text rendering
|
|
190
191
|
canvasPixelated = true // Use nearest neighbor canvas scaling for more pixelated look
|
|
191
192
|
tilesPixelated = true // Disable filtering for crisper pixel art
|
package/src/engine.js
CHANGED
package/src/engineDraw.js
CHANGED
|
@@ -906,7 +906,7 @@ class FontImage
|
|
|
906
906
|
* @param {Vector2} pos
|
|
907
907
|
* @param {number} [scale=.25]
|
|
908
908
|
* @param {boolean} [center]
|
|
909
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D}[context=drawContext]
|
|
909
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
910
910
|
*/
|
|
911
911
|
drawText(text, pos, scale=1, center, context=drawContext)
|
|
912
912
|
{
|
package/src/engineInput.js
CHANGED
|
@@ -216,6 +216,7 @@ function inputInit()
|
|
|
216
216
|
document.addEventListener('wheel', onMouseWheel);
|
|
217
217
|
document.addEventListener('contextmenu', onContextMenu);
|
|
218
218
|
document.addEventListener('blur', onBlur);
|
|
219
|
+
document.addEventListener('mouseleave', onMouseLeave);
|
|
219
220
|
|
|
220
221
|
// init touch input
|
|
221
222
|
if (isTouchDevice && touchInputEnable)
|
|
@@ -274,6 +275,12 @@ function inputInit()
|
|
|
274
275
|
function onMouseWheel(e) { mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY); }
|
|
275
276
|
function onContextMenu(e) { e.preventDefault(); } // prevent right click menu
|
|
276
277
|
function onBlur() { inputClear(); } // reset input when focus is lost
|
|
278
|
+
function onMouseLeave()
|
|
279
|
+
{
|
|
280
|
+
// set mouse position and delta when leaving canvas
|
|
281
|
+
mousePosScreen = vec2(Infinity);
|
|
282
|
+
mouseDeltaScreen = vec2(0);
|
|
283
|
+
}
|
|
277
284
|
}
|
|
278
285
|
|
|
279
286
|
// convert a mouse or touch event position to screen space
|
package/src/engineObject.js
CHANGED
|
@@ -52,7 +52,7 @@ class EngineObject
|
|
|
52
52
|
/** @property {Vector2} - World space position of the object */
|
|
53
53
|
this.pos = pos.copy();
|
|
54
54
|
/** @property {Vector2} - World space width and height of the object */
|
|
55
|
-
this.size = size;
|
|
55
|
+
this.size = size.copy();
|
|
56
56
|
/** @property {Vector2} - Size of object used for drawing, uses size if not set */
|
|
57
57
|
this.drawSize = undefined;
|
|
58
58
|
/** @property {TileInfo} - Tile info to render object (undefined is untextured) */
|
|
@@ -60,7 +60,7 @@ class EngineObject
|
|
|
60
60
|
/** @property {number} - Angle to rotate the object */
|
|
61
61
|
this.angle = angle;
|
|
62
62
|
/** @property {Color} - Color to apply when rendered */
|
|
63
|
-
this.color = color;
|
|
63
|
+
this.color = color.copy();
|
|
64
64
|
/** @property {Color} - Additive color to apply when rendered */
|
|
65
65
|
this.additiveColor = undefined;
|
|
66
66
|
/** @property {boolean} - Should it flip along y axis when rendered */
|
|
@@ -184,7 +184,7 @@ class EngineObject
|
|
|
184
184
|
for (const o of engineObjectsCollide)
|
|
185
185
|
{
|
|
186
186
|
// non solid objects don't collide with each other
|
|
187
|
-
if (!this.isSolid && !o.isSolid || o.destroyed || o.parent || o === this)
|
|
187
|
+
if ((!this.isSolid && !o.isSolid) || o.destroyed || o.parent || o === this)
|
|
188
188
|
continue;
|
|
189
189
|
|
|
190
190
|
// check collision
|
|
@@ -223,7 +223,7 @@ class EngineObject
|
|
|
223
223
|
{
|
|
224
224
|
// push outside object collision
|
|
225
225
|
this.pos.y = o.pos.y + (sizeBoth.y/2 + epsilon) * sign(oldPos.y - o.pos.y);
|
|
226
|
-
if (o.groundObject && wasMovingDown || !o.mass)
|
|
226
|
+
if ((o.groundObject && wasMovingDown) || !o.mass)
|
|
227
227
|
{
|
|
228
228
|
// set ground object if landed on something
|
|
229
229
|
if (wasMovingDown)
|
package/src/engineSettings.js
CHANGED
|
@@ -390,7 +390,7 @@ function setGLEnable(enable)
|
|
|
390
390
|
glCanvas.style.visibility = enable ? 'visible' : 'hidden';
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
-
/** Set how many sided polygons to use when drawing circles and
|
|
393
|
+
/** Set how many sided polygons to use when drawing circles and ellipses with WebGL
|
|
394
394
|
* @param {number} sides
|
|
395
395
|
* @memberof Settings */
|
|
396
396
|
function setGLCircleSides(sides) { glCircleSides = sides; }
|
package/src/engineTileLayer.js
CHANGED
|
@@ -156,7 +156,7 @@ class TileLayerData
|
|
|
156
156
|
/** @property {boolean} - If the tile should be mirrored along the x axis */
|
|
157
157
|
this.mirror = mirror;
|
|
158
158
|
/** @property {Color} - Color of the tile */
|
|
159
|
-
this.color = color;
|
|
159
|
+
this.color = color.copy();
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
/** Set this tile to clear, it will not be rendered */
|
|
@@ -255,7 +255,7 @@ class CanvasLayer extends EngineObject
|
|
|
255
255
|
* @param {TileInfo} [tileInfo]
|
|
256
256
|
* @param {Color} [color=(1,1,1,1)]
|
|
257
257
|
* @param {number} [angle=0]
|
|
258
|
-
* @param {boolean} [mirror=
|
|
258
|
+
* @param {boolean} [mirror=false] */
|
|
259
259
|
drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle, mirror)
|
|
260
260
|
{
|
|
261
261
|
this.drawCanvas2D(pos, size, angle, mirror, (context)=>
|
package/src/engineUtilities.js
CHANGED
|
@@ -155,8 +155,11 @@ function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
|
|
|
155
155
|
* @memberof Utilities */
|
|
156
156
|
function isOverlapping(posA, sizeA, posB, sizeB=vec2())
|
|
157
157
|
{
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
const dx = (posA.x - posB.x)*2;
|
|
159
|
+
const dy = (posA.y - posB.y)*2;
|
|
160
|
+
const sx = sizeA.x + sizeB.x;
|
|
161
|
+
const sy = sizeA.y + sizeB.y;
|
|
162
|
+
return dx >= -sx && dx < sx && dy >= -sy && dy < sy;
|
|
160
163
|
}
|
|
161
164
|
|
|
162
165
|
/** Returns true if a line segment is intersecting an axis aligned box
|
package/src/engineWebGL.js
CHANGED
|
@@ -679,7 +679,7 @@ function glPolyStrip(points)
|
|
|
679
679
|
while (indices.length > 3 && attempts++ < maxAttempts)
|
|
680
680
|
{
|
|
681
681
|
let foundEar = false;
|
|
682
|
-
for (let i = indices.length;
|
|
682
|
+
for (let i = 0; i < indices.length; i++)
|
|
683
683
|
{
|
|
684
684
|
const i0 = indices[(i + indices.length - 1) % indices.length];
|
|
685
685
|
const i1 = indices[i];
|
|
@@ -716,7 +716,7 @@ function glPolyStrip(points)
|
|
|
716
716
|
if (!foundEar)
|
|
717
717
|
{
|
|
718
718
|
let worstIndex = -1, worstValue = Infinity;
|
|
719
|
-
for (let i = indices.length;
|
|
719
|
+
for (let i = 0; i < indices.length; i++)
|
|
720
720
|
{
|
|
721
721
|
const i0 = indices[(i + indices.length - 1) % indices.length];
|
|
722
722
|
const i1 = indices[i];
|