littlejsengine 1.14.7 → 1.14.8
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 +71 -54
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +71 -54
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +71 -54
- package/examples/index.html +10 -5
- 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 +48 -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,34 @@ class UIObject
|
|
|
305
316
|
}
|
|
306
317
|
}
|
|
307
318
|
}
|
|
308
|
-
if (!mouseDown)
|
|
319
|
+
if (isActive && (!mouseDown || !this.isHoverObject()))
|
|
309
320
|
{
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
this.
|
|
313
|
-
|
|
314
|
-
this.soundRelease.play();
|
|
315
|
-
}
|
|
316
|
-
if (isTouchDevice)
|
|
317
|
-
this.mouseIsOver = false;
|
|
318
|
-
this.mouseIsHeld = false;
|
|
321
|
+
this.onRelease();
|
|
322
|
+
if (this.soundRelease)
|
|
323
|
+
this.soundRelease.play();
|
|
324
|
+
uiSystem.activeObject = undefined;
|
|
319
325
|
}
|
|
320
326
|
|
|
321
|
-
if (this.
|
|
322
|
-
this.
|
|
327
|
+
if (this.isHoverObject() !== wasHover)
|
|
328
|
+
this.isHoverObject() ? this.onEnter() : this.onLeave();
|
|
323
329
|
}
|
|
324
330
|
|
|
325
331
|
/** Render the object, called automatically by plugin once each frame */
|
|
326
332
|
render()
|
|
327
333
|
{
|
|
328
|
-
if (this.size.x
|
|
329
|
-
|
|
334
|
+
if (!this.size.x || !this.size.y) return;
|
|
335
|
+
|
|
336
|
+
const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
|
|
337
|
+
const color = this.interactive ? this.disabled ? this.disabledColor : this.isActiveObject() ? this.activeColor || this.color : this.isHoverObject() ? this.hoverColor : this.color : this.color;
|
|
338
|
+
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
330
339
|
}
|
|
331
340
|
|
|
332
341
|
/** Special update when object is not visible */
|
|
333
342
|
updateInvisible()
|
|
334
343
|
{
|
|
335
344
|
// reset input state when not visible
|
|
336
|
-
|
|
345
|
+
if (this.isActiveObject())
|
|
346
|
+
uiSystem.activeObject = undefined;
|
|
337
347
|
}
|
|
338
348
|
|
|
339
349
|
/** Get the size for text with overrides and scale
|
|
@@ -346,6 +356,12 @@ class UIObject
|
|
|
346
356
|
this.textHeight || this.textScale * this.size.y);
|
|
347
357
|
}
|
|
348
358
|
|
|
359
|
+
/** @return {boolean} - Is the mouse hovering over this element */
|
|
360
|
+
isHoverObject() { return uiSystem.hoverObject === this; }
|
|
361
|
+
|
|
362
|
+
/** @return {boolean} - Is the mouse held onto this element */
|
|
363
|
+
isActiveObject() { return uiSystem.activeObject === this; }
|
|
364
|
+
|
|
349
365
|
/** Called when the mouse enters the object */
|
|
350
366
|
onEnter() {}
|
|
351
367
|
|
|
@@ -456,10 +472,7 @@ class UIButton extends UIObject
|
|
|
456
472
|
}
|
|
457
473
|
render()
|
|
458
474
|
{
|
|
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);
|
|
475
|
+
super.render();
|
|
463
476
|
|
|
464
477
|
// draw the text scaled to fit
|
|
465
478
|
const textSize = this.getTextSize();
|
|
@@ -500,8 +513,7 @@ class UICheckbox extends UIObject
|
|
|
500
513
|
}
|
|
501
514
|
render()
|
|
502
515
|
{
|
|
503
|
-
|
|
504
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
516
|
+
super.render();
|
|
505
517
|
if (this.checked)
|
|
506
518
|
{
|
|
507
519
|
const p = this.cornerRadius / min(this.size.x, this.size.y) * 2;
|
|
@@ -551,7 +563,7 @@ class UIScrollbar extends UIObject
|
|
|
551
563
|
update()
|
|
552
564
|
{
|
|
553
565
|
super.update();
|
|
554
|
-
if (this.
|
|
566
|
+
if (this.isActiveObject() && this.interactive)
|
|
555
567
|
{
|
|
556
568
|
// check if value changed
|
|
557
569
|
const handleSize = vec2(this.size.y);
|
|
@@ -565,12 +577,7 @@ class UIScrollbar extends UIObject
|
|
|
565
577
|
}
|
|
566
578
|
render()
|
|
567
579
|
{
|
|
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);
|
|
580
|
+
super.render();
|
|
574
581
|
|
|
575
582
|
// draw the scrollbar handle
|
|
576
583
|
const handleSize = vec2(this.size.y);
|
|
@@ -579,7 +586,7 @@ class UIScrollbar extends UIObject
|
|
|
579
586
|
const p2 = this.pos.x + handleWidth/2;
|
|
580
587
|
const handlePos = vec2(lerp(p1, p2, this.value), this.pos.y);
|
|
581
588
|
const handleColor = this.disabled ? this.disabledColor :
|
|
582
|
-
this.interactive && this.
|
|
589
|
+
this.interactive && this.isActiveObject() ? this.color : this.handleColor;
|
|
583
590
|
uiSystem.drawRect(handlePos, handleSize, handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
584
591
|
|
|
585
592
|
// 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];
|