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/dist/littlejs.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.14.
|
|
36
|
+
const engineVersion = '1.14.8';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -1418,8 +1418,11 @@ function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
|
|
|
1418
1418
|
* @memberof Utilities */
|
|
1419
1419
|
function isOverlapping(posA, sizeA, posB, sizeB=vec2())
|
|
1420
1420
|
{
|
|
1421
|
-
|
|
1422
|
-
|
|
1421
|
+
const dx = (posA.x - posB.x)*2;
|
|
1422
|
+
const dy = (posA.y - posB.y)*2;
|
|
1423
|
+
const sx = sizeA.x + sizeB.x;
|
|
1424
|
+
const sy = sizeA.y + sizeB.y;
|
|
1425
|
+
return dx >= -sx && dx < sx && dy >= -sy && dy < sy;
|
|
1423
1426
|
}
|
|
1424
1427
|
|
|
1425
1428
|
/** Returns true if a line segment is intersecting an axis aligned box
|
|
@@ -2650,7 +2653,7 @@ function setGLEnable(enable)
|
|
|
2650
2653
|
glCanvas.style.visibility = enable ? 'visible' : 'hidden';
|
|
2651
2654
|
}
|
|
2652
2655
|
|
|
2653
|
-
/** Set how many sided polygons to use when drawing circles and
|
|
2656
|
+
/** Set how many sided polygons to use when drawing circles and ellipses with WebGL
|
|
2654
2657
|
* @param {number} sides
|
|
2655
2658
|
* @memberof Settings */
|
|
2656
2659
|
function setGLCircleSides(sides) { glCircleSides = sides; }
|
|
@@ -2861,7 +2864,7 @@ class EngineObject
|
|
|
2861
2864
|
/** @property {Vector2} - World space position of the object */
|
|
2862
2865
|
this.pos = pos.copy();
|
|
2863
2866
|
/** @property {Vector2} - World space width and height of the object */
|
|
2864
|
-
this.size = size;
|
|
2867
|
+
this.size = size.copy();
|
|
2865
2868
|
/** @property {Vector2} - Size of object used for drawing, uses size if not set */
|
|
2866
2869
|
this.drawSize = undefined;
|
|
2867
2870
|
/** @property {TileInfo} - Tile info to render object (undefined is untextured) */
|
|
@@ -2869,7 +2872,7 @@ class EngineObject
|
|
|
2869
2872
|
/** @property {number} - Angle to rotate the object */
|
|
2870
2873
|
this.angle = angle;
|
|
2871
2874
|
/** @property {Color} - Color to apply when rendered */
|
|
2872
|
-
this.color = color;
|
|
2875
|
+
this.color = color.copy();
|
|
2873
2876
|
/** @property {Color} - Additive color to apply when rendered */
|
|
2874
2877
|
this.additiveColor = undefined;
|
|
2875
2878
|
/** @property {boolean} - Should it flip along y axis when rendered */
|
|
@@ -2993,7 +2996,7 @@ class EngineObject
|
|
|
2993
2996
|
for (const o of engineObjectsCollide)
|
|
2994
2997
|
{
|
|
2995
2998
|
// non solid objects don't collide with each other
|
|
2996
|
-
if (!this.isSolid && !o.isSolid || o.destroyed || o.parent || o === this)
|
|
2999
|
+
if ((!this.isSolid && !o.isSolid) || o.destroyed || o.parent || o === this)
|
|
2997
3000
|
continue;
|
|
2998
3001
|
|
|
2999
3002
|
// check collision
|
|
@@ -3032,7 +3035,7 @@ class EngineObject
|
|
|
3032
3035
|
{
|
|
3033
3036
|
// push outside object collision
|
|
3034
3037
|
this.pos.y = o.pos.y + (sizeBoth.y/2 + epsilon) * sign(oldPos.y - o.pos.y);
|
|
3035
|
-
if (o.groundObject && wasMovingDown || !o.mass)
|
|
3038
|
+
if ((o.groundObject && wasMovingDown) || !o.mass)
|
|
3036
3039
|
{
|
|
3037
3040
|
// set ground object if landed on something
|
|
3038
3041
|
if (wasMovingDown)
|
|
@@ -4183,7 +4186,7 @@ class FontImage
|
|
|
4183
4186
|
* @param {Vector2} pos
|
|
4184
4187
|
* @param {number} [scale=.25]
|
|
4185
4188
|
* @param {boolean} [center]
|
|
4186
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D}[context=drawContext]
|
|
4189
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4187
4190
|
*/
|
|
4188
4191
|
drawText(text, pos, scale=1, center, context=drawContext)
|
|
4189
4192
|
{
|
|
@@ -4450,6 +4453,7 @@ function inputInit()
|
|
|
4450
4453
|
document.addEventListener('wheel', onMouseWheel);
|
|
4451
4454
|
document.addEventListener('contextmenu', onContextMenu);
|
|
4452
4455
|
document.addEventListener('blur', onBlur);
|
|
4456
|
+
document.addEventListener('mouseleave', onMouseLeave);
|
|
4453
4457
|
|
|
4454
4458
|
// init touch input
|
|
4455
4459
|
if (isTouchDevice && touchInputEnable)
|
|
@@ -4508,6 +4512,12 @@ function inputInit()
|
|
|
4508
4512
|
function onMouseWheel(e) { mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY); }
|
|
4509
4513
|
function onContextMenu(e) { e.preventDefault(); } // prevent right click menu
|
|
4510
4514
|
function onBlur() { inputClear(); } // reset input when focus is lost
|
|
4515
|
+
function onMouseLeave()
|
|
4516
|
+
{
|
|
4517
|
+
// set mouse position and delta when leaving canvas
|
|
4518
|
+
mousePosScreen = vec2(Infinity);
|
|
4519
|
+
mouseDeltaScreen = vec2(0);
|
|
4520
|
+
}
|
|
4511
4521
|
}
|
|
4512
4522
|
|
|
4513
4523
|
// convert a mouse or touch event position to screen space
|
|
@@ -5629,7 +5639,7 @@ class TileLayerData
|
|
|
5629
5639
|
/** @property {boolean} - If the tile should be mirrored along the x axis */
|
|
5630
5640
|
this.mirror = mirror;
|
|
5631
5641
|
/** @property {Color} - Color of the tile */
|
|
5632
|
-
this.color = color;
|
|
5642
|
+
this.color = color.copy();
|
|
5633
5643
|
}
|
|
5634
5644
|
|
|
5635
5645
|
/** Set this tile to clear, it will not be rendered */
|
|
@@ -5728,7 +5738,7 @@ class CanvasLayer extends EngineObject
|
|
|
5728
5738
|
* @param {TileInfo} [tileInfo]
|
|
5729
5739
|
* @param {Color} [color=(1,1,1,1)]
|
|
5730
5740
|
* @param {number} [angle=0]
|
|
5731
|
-
* @param {boolean} [mirror=
|
|
5741
|
+
* @param {boolean} [mirror=false] */
|
|
5732
5742
|
drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle, mirror)
|
|
5733
5743
|
{
|
|
5734
5744
|
this.drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
@@ -7326,7 +7336,7 @@ function glPolyStrip(points)
|
|
|
7326
7336
|
while (indices.length > 3 && attempts++ < maxAttempts)
|
|
7327
7337
|
{
|
|
7328
7338
|
let foundEar = false;
|
|
7329
|
-
for (let i = indices.length;
|
|
7339
|
+
for (let i = 0; i < indices.length; i++)
|
|
7330
7340
|
{
|
|
7331
7341
|
const i0 = indices[(i + indices.length - 1) % indices.length];
|
|
7332
7342
|
const i1 = indices[i];
|
|
@@ -7363,7 +7373,7 @@ function glPolyStrip(points)
|
|
|
7363
7373
|
if (!foundEar)
|
|
7364
7374
|
{
|
|
7365
7375
|
let worstIndex = -1, worstValue = Infinity;
|
|
7366
|
-
for (let i = indices.length;
|
|
7376
|
+
for (let i = 0; i < indices.length; i++)
|
|
7367
7377
|
{
|
|
7368
7378
|
const i0 = indices[(i + indices.length - 1) % indices.length];
|
|
7369
7379
|
const i1 = indices[i];
|
|
@@ -7926,6 +7936,10 @@ class UISystemPlugin
|
|
|
7926
7936
|
this.uiObjects = [];
|
|
7927
7937
|
/** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
|
|
7928
7938
|
this.uiContext = context;
|
|
7939
|
+
/** @property {UIObject} - Top most object user is over */
|
|
7940
|
+
this.hoverObject = undefined;
|
|
7941
|
+
/** @property {UIObject} - Object user is currently interacting with */
|
|
7942
|
+
this.activeObject = undefined;
|
|
7929
7943
|
|
|
7930
7944
|
engineAddPlugin(uiUpdate, uiRender);
|
|
7931
7945
|
|
|
@@ -7955,6 +7969,8 @@ class UISystemPlugin
|
|
|
7955
7969
|
else
|
|
7956
7970
|
updateInvisibleObject(o);
|
|
7957
7971
|
}
|
|
7972
|
+
// reset hover object at start of update
|
|
7973
|
+
uiSystem.hoverObject = undefined;
|
|
7958
7974
|
for (let i = uiSystem.uiObjects.length; i--;)
|
|
7959
7975
|
{
|
|
7960
7976
|
const o = uiSystem.uiObjects[i];
|
|
@@ -8066,6 +8082,8 @@ class UIObject
|
|
|
8066
8082
|
this.size = size.copy();
|
|
8067
8083
|
/** @property {Color} - Color of the object */
|
|
8068
8084
|
this.color = uiSystem.defaultColor;
|
|
8085
|
+
/** @property {Color} - Color of the object when active, uses color if undefined */
|
|
8086
|
+
this.activeColor = undefined;
|
|
8069
8087
|
/** @property {string} - Text for this ui object */
|
|
8070
8088
|
this.text = undefined;
|
|
8071
8089
|
/** @property {Color} - Color when disabled */
|
|
@@ -8096,7 +8114,7 @@ class UIObject
|
|
|
8096
8114
|
this.children = [];
|
|
8097
8115
|
/** @property {UIObject} - This object's parent, position is in parent space */
|
|
8098
8116
|
this.parent = undefined;
|
|
8099
|
-
/** @property {number} -
|
|
8117
|
+
/** @property {number} - Added size to make small buttons easier to touch on mobile devices */
|
|
8100
8118
|
this.extraTouchSize = 0;
|
|
8101
8119
|
/** @property {Sound} - Sound when interactive element is pressed */
|
|
8102
8120
|
this.soundPress = uiSystem.defaultSoundPress;
|
|
@@ -8104,12 +8122,10 @@ class UIObject
|
|
|
8104
8122
|
this.soundRelease = uiSystem.defaultSoundRelease;
|
|
8105
8123
|
/** @property {Sound} - Sound when interactive element is clicked */
|
|
8106
8124
|
this.soundClick = uiSystem.defaultSoundClick;
|
|
8107
|
-
/** @property {boolean} - Is the mouse over this element */
|
|
8108
|
-
this.mouseIsOver = false;
|
|
8109
8125
|
/** @property {boolean} - Is this element interactive */
|
|
8110
8126
|
this.interactive = false;
|
|
8111
|
-
/** @property {boolean} -
|
|
8112
|
-
this.
|
|
8127
|
+
/** @property {boolean} - Activate when dragged over with mouse held down */
|
|
8128
|
+
this.dragActivate = false;
|
|
8113
8129
|
uiSystem.uiObjects.push(this);
|
|
8114
8130
|
}
|
|
8115
8131
|
|
|
@@ -8136,15 +8152,18 @@ class UIObject
|
|
|
8136
8152
|
/** Update the object, called automatically by plugin once each frame */
|
|
8137
8153
|
update()
|
|
8138
8154
|
{
|
|
8139
|
-
const
|
|
8140
|
-
const
|
|
8155
|
+
const wasHover = this.isHoverObject();
|
|
8156
|
+
const isActive = this.isActiveObject();
|
|
8141
8157
|
const mouseDown = mouseIsDown(0);
|
|
8142
|
-
|
|
8158
|
+
const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
|
|
8159
|
+
if (!uiSystem.hoverObject)
|
|
8160
|
+
if (mousePress || !mouseDown || isActive)
|
|
8143
8161
|
{
|
|
8144
8162
|
const size = this.size.add(vec2(isTouchDevice && this.extraTouchSize || 0));
|
|
8145
|
-
|
|
8163
|
+
if (isOverlapping(this.pos, size, mousePosScreen))
|
|
8164
|
+
uiSystem.hoverObject = this;
|
|
8146
8165
|
}
|
|
8147
|
-
if (this.
|
|
8166
|
+
if (this.isHoverObject())
|
|
8148
8167
|
{
|
|
8149
8168
|
if (mousePress)
|
|
8150
8169
|
inputClearKey(0,0,0,1,0); // clear mouse was pressed state
|
|
@@ -8157,10 +8176,12 @@ class UIObject
|
|
|
8157
8176
|
this.onPress();
|
|
8158
8177
|
if (this.soundPress)
|
|
8159
8178
|
this.soundPress.play();
|
|
8179
|
+
if (uiSystem.activeObject && !isActive)
|
|
8180
|
+
uiSystem.activeObject.onRelease();
|
|
8181
|
+
uiSystem.activeObject = this;
|
|
8160
8182
|
}
|
|
8161
|
-
this.mouseIsHeld = true;
|
|
8162
8183
|
}
|
|
8163
|
-
if (!mouseDown &&
|
|
8184
|
+
if (!mouseDown && uiSystem.activeObject === this && this.interactive)
|
|
8164
8185
|
{
|
|
8165
8186
|
this.onClick();
|
|
8166
8187
|
if (this.soundClick)
|
|
@@ -8168,35 +8189,34 @@ class UIObject
|
|
|
8168
8189
|
}
|
|
8169
8190
|
}
|
|
8170
8191
|
}
|
|
8171
|
-
if (!mouseDown)
|
|
8192
|
+
if (isActive && (!mouseDown || !this.isHoverObject()))
|
|
8172
8193
|
{
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
this.
|
|
8176
|
-
|
|
8177
|
-
this.soundRelease.play();
|
|
8178
|
-
}
|
|
8179
|
-
if (isTouchDevice)
|
|
8180
|
-
this.mouseIsOver = false;
|
|
8181
|
-
this.mouseIsHeld = false;
|
|
8194
|
+
this.onRelease();
|
|
8195
|
+
if (this.soundRelease)
|
|
8196
|
+
this.soundRelease.play();
|
|
8197
|
+
uiSystem.activeObject = undefined;
|
|
8182
8198
|
}
|
|
8183
8199
|
|
|
8184
|
-
if (this.
|
|
8185
|
-
this.
|
|
8200
|
+
if (this.isHoverObject() !== wasHover)
|
|
8201
|
+
this.isHoverObject() ? this.onEnter() : this.onLeave();
|
|
8186
8202
|
}
|
|
8187
8203
|
|
|
8188
8204
|
/** Render the object, called automatically by plugin once each frame */
|
|
8189
8205
|
render()
|
|
8190
8206
|
{
|
|
8191
|
-
if (this.size.x
|
|
8192
|
-
|
|
8207
|
+
if (!this.size.x || !this.size.y) return;
|
|
8208
|
+
|
|
8209
|
+
const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
|
|
8210
|
+
const color = this.interactive ? this.disabled ? this.disabledColor : this.isActiveObject() ? this.activeColor || this.color : this.isHoverObject() ? this.hoverColor : this.color : this.color;
|
|
8211
|
+
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
8193
8212
|
}
|
|
8194
8213
|
|
|
8195
8214
|
/** Special update when object is not visible */
|
|
8196
8215
|
updateInvisible()
|
|
8197
8216
|
{
|
|
8198
8217
|
// reset input state when not visible
|
|
8199
|
-
|
|
8218
|
+
if (this.isActiveObject())
|
|
8219
|
+
uiSystem.activeObject = undefined;
|
|
8200
8220
|
}
|
|
8201
8221
|
|
|
8202
8222
|
/** Get the size for text with overrides and scale
|
|
@@ -8209,6 +8229,12 @@ class UIObject
|
|
|
8209
8229
|
this.textHeight || this.textScale * this.size.y);
|
|
8210
8230
|
}
|
|
8211
8231
|
|
|
8232
|
+
/** @return {boolean} - Is the mouse hovering over this element */
|
|
8233
|
+
isHoverObject() { return uiSystem.hoverObject === this; }
|
|
8234
|
+
|
|
8235
|
+
/** @return {boolean} - Is the mouse held onto this element */
|
|
8236
|
+
isActiveObject() { return uiSystem.activeObject === this; }
|
|
8237
|
+
|
|
8212
8238
|
/** Called when the mouse enters the object */
|
|
8213
8239
|
onEnter() {}
|
|
8214
8240
|
|
|
@@ -8319,10 +8345,7 @@ class UIButton extends UIObject
|
|
|
8319
8345
|
}
|
|
8320
8346
|
render()
|
|
8321
8347
|
{
|
|
8322
|
-
|
|
8323
|
-
const lineColor = this.mouseIsHeld && !this.disabled ? this.color : this.lineColor;
|
|
8324
|
-
const color = this.disabled ? this.disabledColor : this.mouseIsOver ? this.hoverColor : this.color;
|
|
8325
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
8348
|
+
super.render();
|
|
8326
8349
|
|
|
8327
8350
|
// draw the text scaled to fit
|
|
8328
8351
|
const textSize = this.getTextSize();
|
|
@@ -8363,8 +8386,7 @@ class UICheckbox extends UIObject
|
|
|
8363
8386
|
}
|
|
8364
8387
|
render()
|
|
8365
8388
|
{
|
|
8366
|
-
|
|
8367
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8389
|
+
super.render();
|
|
8368
8390
|
if (this.checked)
|
|
8369
8391
|
{
|
|
8370
8392
|
const p = this.cornerRadius / min(this.size.x, this.size.y) * 2;
|
|
@@ -8414,7 +8436,7 @@ class UIScrollbar extends UIObject
|
|
|
8414
8436
|
update()
|
|
8415
8437
|
{
|
|
8416
8438
|
super.update();
|
|
8417
|
-
if (this.
|
|
8439
|
+
if (this.isActiveObject() && this.interactive)
|
|
8418
8440
|
{
|
|
8419
8441
|
// check if value changed
|
|
8420
8442
|
const handleSize = vec2(this.size.y);
|
|
@@ -8428,12 +8450,7 @@ class UIScrollbar extends UIObject
|
|
|
8428
8450
|
}
|
|
8429
8451
|
render()
|
|
8430
8452
|
{
|
|
8431
|
-
|
|
8432
|
-
const lineColor = this.interactive && this.mouseIsHeld && !this.disabled ?
|
|
8433
|
-
this.color : this.lineColor;
|
|
8434
|
-
const color = this.disabled ? this.disabledColor :
|
|
8435
|
-
this.interactive && this.mouseIsHeld ? this.hoverColor : this.color;
|
|
8436
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
8453
|
+
super.render();
|
|
8437
8454
|
|
|
8438
8455
|
// draw the scrollbar handle
|
|
8439
8456
|
const handleSize = vec2(this.size.y);
|
|
@@ -8442,7 +8459,7 @@ class UIScrollbar extends UIObject
|
|
|
8442
8459
|
const p2 = this.pos.x + handleWidth/2;
|
|
8443
8460
|
const handlePos = vec2(lerp(p1, p2, this.value), this.pos.y);
|
|
8444
8461
|
const handleColor = this.disabled ? this.disabledColor :
|
|
8445
|
-
this.interactive && this.
|
|
8462
|
+
this.interactive && this.isActiveObject() ? this.color : this.handleColor;
|
|
8446
8463
|
uiSystem.drawRect(handlePos, handleSize, handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8447
8464
|
|
|
8448
8465
|
// draw the text scaled to fit on the scrollbar
|