littlejsengine 1.18.17 → 1.18.19
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 +79 -15
- package/dist/littlejs.esm.js +230 -64
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +222 -63
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +222 -63
- package/package.json +1 -1
- package/plugins/drawUtilities.js +23 -12
- package/plugins/lightSystem.js +2 -2
- package/plugins/uiSystem.js +35 -25
- package/src/engine.js +1 -1
- package/src/engineDraw.js +59 -4
- package/src/engineExport.js +8 -1
- package/src/engineInput.js +80 -13
- package/src/engineMath.js +15 -0
- package/src/engineObject.js +0 -2
- package/src/engineParticles.js +4 -4
- package/src/engineSettings.js +3 -0
package/dist/littlejs.esm.js
CHANGED
|
@@ -35,7 +35,7 @@ const engineName = 'LittleJS';
|
|
|
35
35
|
* @type {string}
|
|
36
36
|
* @default
|
|
37
37
|
* @memberof Engine */
|
|
38
|
-
const engineVersion = '1.18.
|
|
38
|
+
const engineVersion = '1.18.19';
|
|
39
39
|
|
|
40
40
|
/** Frames per second to update
|
|
41
41
|
* @type {number}
|
|
@@ -2233,10 +2233,25 @@ class Color
|
|
|
2233
2233
|
* @return {Color} */
|
|
2234
2234
|
setFrom(c) { return this.set(c.r, c.g, c.b, c.a); }
|
|
2235
2235
|
|
|
2236
|
+
/** Sets the alpha of this color and returns self
|
|
2237
|
+
* @param {number} [a] - alpha
|
|
2238
|
+
* @return {Color} */
|
|
2239
|
+
setAlpha(a=1)
|
|
2240
|
+
{
|
|
2241
|
+
this.a = a;
|
|
2242
|
+
ASSERT_COLOR_VALID(this);
|
|
2243
|
+
return this;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2236
2246
|
/** Returns a new color that is a copy of this
|
|
2237
2247
|
* @return {Color} */
|
|
2238
2248
|
copy() { return new Color(this.r, this.g, this.b, this.a); }
|
|
2239
2249
|
|
|
2250
|
+
/** Returns a copy of this color with the alpha set
|
|
2251
|
+
* @param {number} [a] - alpha
|
|
2252
|
+
* @return {Color} */
|
|
2253
|
+
withAlpha(a=1) { return new Color(this.r, this.g, this.b, a); }
|
|
2254
|
+
|
|
2240
2255
|
/** Returns a copy of this color plus the color passed in
|
|
2241
2256
|
* @param {Color} c - other color
|
|
2242
2257
|
* @return {Color} */
|
|
@@ -2831,6 +2846,8 @@ let canvasFixedSize = vec2();
|
|
|
2831
2846
|
let canvasPixelated = false;
|
|
2832
2847
|
|
|
2833
2848
|
/** Disables texture filtering for crisper pixel art
|
|
2849
|
+
* - Leave true for pixel art so sprites stay sharp when scaled (uses NEAREST filtering)
|
|
2850
|
+
* - Set false for smooth/high-resolution art to enable bilinear filtering and mipmaps
|
|
2834
2851
|
* @type {boolean}
|
|
2835
2852
|
* @default
|
|
2836
2853
|
* @memberof Settings */
|
|
@@ -3184,6 +3201,7 @@ function setCanvasPixelated(pixelated)
|
|
|
3184
3201
|
}
|
|
3185
3202
|
|
|
3186
3203
|
/** Disables texture filtering for crisper pixel art
|
|
3204
|
+
* - Leave true for pixel art; set false for smooth/high-resolution art
|
|
3187
3205
|
* @param {boolean} pixelated
|
|
3188
3206
|
* @memberof Settings */
|
|
3189
3207
|
function setTilesPixelated(pixelated) { tilesPixelated = pixelated; }
|
|
@@ -3651,8 +3669,6 @@ class EngineObject
|
|
|
3651
3669
|
|
|
3652
3670
|
// notify objects of collision and check if should be resolved
|
|
3653
3671
|
const collide1 = this.collideWithObject(o);
|
|
3654
|
-
// callback may have destroyed us; stop resolving against more objects
|
|
3655
|
-
if (this.destroyed) return;
|
|
3656
3672
|
const collide2 = o.collideWithObject(this);
|
|
3657
3673
|
if (!collide1 || !collide2) continue;
|
|
3658
3674
|
|
|
@@ -5075,6 +5091,62 @@ function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
|
|
|
5075
5091
|
* @memberof Draw */
|
|
5076
5092
|
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
5077
5093
|
|
|
5094
|
+
/** Fit the camera to a rectangle in world space by setting cameraPos and cameraScale
|
|
5095
|
+
* - worldMargin pads the content rectangle in world units, so the gap scales with the content on resize
|
|
5096
|
+
* - screenInset reserves space in screen pixels on each viewport edge (for example a HUD band) and
|
|
5097
|
+
* re-centers the content away from that edge, so the reserved band stays a fixed pixel size on resize
|
|
5098
|
+
* - worldMargin and screenInset may each be a number for all sides, a Vector2 (x=left/right, y=top/bottom),
|
|
5099
|
+
* or an object with any of {top, right, bottom, left}
|
|
5100
|
+
* @param {Vector2} center - Center of the rectangle in world space
|
|
5101
|
+
* @param {Vector2} size - Size of the rectangle in world space
|
|
5102
|
+
* @param {number|Vector2|Object} [worldMargin] - World space padding added around the content rectangle
|
|
5103
|
+
* @param {number|Vector2|Object} [screenInset] - Screen space padding in pixels reserved on each viewport edge
|
|
5104
|
+
* @return {number} - The new camera scale
|
|
5105
|
+
* @memberof Draw */
|
|
5106
|
+
function cameraFit(center, size, worldMargin, screenInset)
|
|
5107
|
+
{
|
|
5108
|
+
ASSERT(isVector2(center), 'center must be a vec2');
|
|
5109
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
5110
|
+
|
|
5111
|
+
// pad the content
|
|
5112
|
+
const margin = padSides(worldMargin);
|
|
5113
|
+
const inset = padSides(screenInset);
|
|
5114
|
+
const worldW = size.x + margin.left + margin.right;
|
|
5115
|
+
const worldH = size.y + margin.top + margin.bottom;
|
|
5116
|
+
const viewW = mainCanvasSize.x - inset.left - inset.right;
|
|
5117
|
+
const viewH = mainCanvasSize.y - inset.top - inset.bottom;
|
|
5118
|
+
|
|
5119
|
+
// bail on a degenerate rect or viewport rather than NaN the camera
|
|
5120
|
+
if (!(worldW > 0 && worldH > 0 && viewW > 0 && viewH > 0))
|
|
5121
|
+
return cameraScale;
|
|
5122
|
+
|
|
5123
|
+
// scale to fit the padded content
|
|
5124
|
+
cameraScale = min(viewW / worldW, viewH / worldH);
|
|
5125
|
+
|
|
5126
|
+
// calculate offset vectors
|
|
5127
|
+
const marginVector = vec2(margin.right - margin.left, margin.top - margin.bottom).scale(.5);
|
|
5128
|
+
const insetVector = vec2(inset.right - inset.left, inset.top - inset.bottom).scale(.5 / cameraScale);
|
|
5129
|
+
|
|
5130
|
+
// apply the offsets and return camera scale
|
|
5131
|
+
cameraPos = center.add(marginVector).add(insetVector);
|
|
5132
|
+
return cameraScale;
|
|
5133
|
+
|
|
5134
|
+
function padSides(p)
|
|
5135
|
+
{
|
|
5136
|
+
// normalize a padding option to {top, right, bottom, left}
|
|
5137
|
+
if (p === undefined || isNumber(p))
|
|
5138
|
+
p = vec2(p);
|
|
5139
|
+
if (isVector2(p))
|
|
5140
|
+
return { top: p.y, right: p.x, bottom: p.y, left: p.x };
|
|
5141
|
+
return {
|
|
5142
|
+
top: p.top || 0,
|
|
5143
|
+
right: p.right || 0,
|
|
5144
|
+
bottom: p.bottom || 0,
|
|
5145
|
+
left: p.left || 0,
|
|
5146
|
+
};
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
|
|
5078
5150
|
/** Check if a box, point, or circle is on screen with a circle test
|
|
5079
5151
|
* If size is a Vector2, uses the length as diameter
|
|
5080
5152
|
* This can be used to cull offscreen objects from render or update
|
|
@@ -5113,14 +5185,13 @@ function isOnScreen(pos, size=0)
|
|
|
5113
5185
|
y + size > -h && y - size < h;
|
|
5114
5186
|
}
|
|
5115
5187
|
|
|
5116
|
-
/** Enable
|
|
5188
|
+
/** Enable additive blending
|
|
5117
5189
|
* @param {boolean} [additive]
|
|
5118
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
5119
5190
|
* @memberof Draw */
|
|
5120
|
-
function
|
|
5191
|
+
function setAdditiveBlendMode(additive=true)
|
|
5121
5192
|
{
|
|
5122
5193
|
glAdditive = additive;
|
|
5123
|
-
|
|
5194
|
+
drawContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
5124
5195
|
}
|
|
5125
5196
|
|
|
5126
5197
|
/** Combines LittleJS canvases onto the main canvas
|
|
@@ -5446,11 +5517,30 @@ let mouseWheel = 0;
|
|
|
5446
5517
|
* @memberof Input */
|
|
5447
5518
|
let mouseInWindow = true;
|
|
5448
5519
|
|
|
5449
|
-
/**
|
|
5520
|
+
/** True if a gamepad is the most recently used input device.
|
|
5521
|
+
* Equivalent to usingGamepadInput(); derived from lastInputDevice each frame.
|
|
5450
5522
|
* @type {boolean}
|
|
5451
5523
|
* @memberof Input */
|
|
5452
5524
|
let isUsingGamepad = false;
|
|
5453
5525
|
|
|
5526
|
+
/** The most recently used input device: 'mouse' | 'keyboard' | 'gamepad'.
|
|
5527
|
+
* Sticky: it holds its value while every device is idle, so a mouse-follow
|
|
5528
|
+
* control (e.g. paddle = mousePos) won't snap back the instant the stick/keys
|
|
5529
|
+
* are released. With several devices in play at once (e.g. keyboard to move +
|
|
5530
|
+
* mouse to aim) it tracks whichever was touched last each frame, so it may
|
|
5531
|
+
* alternate — that's intended; use it to pick which control drives a shared
|
|
5532
|
+
* action. Updated every frame by inputUpdate().
|
|
5533
|
+
* @type {string}
|
|
5534
|
+
* @memberof Input */
|
|
5535
|
+
let lastInputDevice = 'mouse';
|
|
5536
|
+
|
|
5537
|
+
/** Screen-pixel mouse movement per frame that counts as "using the mouse"
|
|
5538
|
+
* (so sub-pixel hand jitter doesn't steal focus from the keyboard/gamepad).
|
|
5539
|
+
* @type {number}
|
|
5540
|
+
* @default
|
|
5541
|
+
* @memberof Input */
|
|
5542
|
+
let inputMouseMoveThreshold = 6;
|
|
5543
|
+
|
|
5454
5544
|
/** Prevents input continuing to the default browser handling (true by default)
|
|
5455
5545
|
* @type {boolean}
|
|
5456
5546
|
* @memberof Input */
|
|
@@ -5471,6 +5561,18 @@ const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
|
5471
5561
|
* @memberof Input */
|
|
5472
5562
|
function setInputPreventDefault(preventDefault=true) { inputPreventDefault = preventDefault; }
|
|
5473
5563
|
|
|
5564
|
+
/** Set the screen-pixel mouse movement per frame that counts as using the mouse
|
|
5565
|
+
* @param {number} threshold
|
|
5566
|
+
* @memberof Input */
|
|
5567
|
+
function setInputMouseMoveThreshold(threshold) { inputMouseMoveThreshold = threshold; }
|
|
5568
|
+
|
|
5569
|
+
/** @return {boolean} - Is the mouse the most recently used input device? @memberof Input */
|
|
5570
|
+
function usingMouseInput() { return lastInputDevice === 'mouse'; }
|
|
5571
|
+
/** @return {boolean} - Is the keyboard the most recently used input device? @memberof Input */
|
|
5572
|
+
function usingKeyboardInput() { return lastInputDevice === 'keyboard'; }
|
|
5573
|
+
/** @return {boolean} - Is a gamepad the most recently used input device? @memberof Input */
|
|
5574
|
+
function usingGamepadInput() { return lastInputDevice === 'gamepad'; }
|
|
5575
|
+
|
|
5474
5576
|
/** Clears an input key state
|
|
5475
5577
|
* @param {string|number} key
|
|
5476
5578
|
* @param {number} [device]
|
|
@@ -5771,7 +5873,6 @@ function inputInit()
|
|
|
5771
5873
|
{
|
|
5772
5874
|
if (!e.repeat)
|
|
5773
5875
|
{
|
|
5774
|
-
isUsingGamepad = false;
|
|
5775
5876
|
inputData[0][e.code] = 3;
|
|
5776
5877
|
if (inputWASDEmulateDirection)
|
|
5777
5878
|
inputData[0][remapKey(e.code)] = 3;
|
|
@@ -5830,7 +5931,6 @@ function inputInit()
|
|
|
5830
5931
|
if (soundEnable && !headlessMode && audioContext && !audioIsRunning())
|
|
5831
5932
|
audioContext.resume();
|
|
5832
5933
|
|
|
5833
|
-
isUsingGamepad = false;
|
|
5834
5934
|
inputData[0][e.button] = 3;
|
|
5835
5935
|
|
|
5836
5936
|
const mousePosScreenLast = mousePosScreen;
|
|
@@ -5921,10 +6021,7 @@ function inputInit()
|
|
|
5921
6021
|
if (wasTouching)
|
|
5922
6022
|
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
5923
6023
|
else
|
|
5924
|
-
{
|
|
5925
6024
|
inputData[0][button] = 3;
|
|
5926
|
-
isUsingGamepad = false; // a passthrough tap is mouse-style input
|
|
5927
|
-
}
|
|
5928
6025
|
}
|
|
5929
6026
|
else if (wasTouching)
|
|
5930
6027
|
inputData[0][button] = inputData[0][button] & 2 | 4;
|
|
@@ -5970,7 +6067,43 @@ function inputUpdate()
|
|
|
5970
6067
|
|
|
5971
6068
|
// update gamepads if enabled
|
|
5972
6069
|
gamepadsUpdate();
|
|
5973
|
-
|
|
6070
|
+
|
|
6071
|
+
// update most recently used input device
|
|
6072
|
+
updateLastInputDevice();
|
|
6073
|
+
|
|
6074
|
+
function updateLastInputDevice()
|
|
6075
|
+
{
|
|
6076
|
+
// mouse: any button held or moved
|
|
6077
|
+
const mouseActive = mouseIsDown(0) || mouseIsDown(1) || mouseIsDown(2) || mouseDeltaScreen.length() > inputMouseMoveThreshold;
|
|
6078
|
+
|
|
6079
|
+
// gamepad: any button held or stick moved
|
|
6080
|
+
let gamepadActive = false;
|
|
6081
|
+
for (let s = gamepadStickCount(); s-- && !gamepadActive;)
|
|
6082
|
+
gamepadActive = gamepadStick(s).lengthSquared() > .04;
|
|
6083
|
+
for (let b = 17; b-- && !gamepadActive;)
|
|
6084
|
+
gamepadActive = gamepadIsDown(b);
|
|
6085
|
+
|
|
6086
|
+
// keyboard: any non-mouse key down
|
|
6087
|
+
let keyboardActive = false;
|
|
6088
|
+
for (const k in inputData[0])
|
|
6089
|
+
if (isNaN(+k) && (inputData[0][k] & 1))
|
|
6090
|
+
{
|
|
6091
|
+
keyboardActive = true;
|
|
6092
|
+
break;
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6095
|
+
// update the last input
|
|
6096
|
+
if (gamepadActive)
|
|
6097
|
+
lastInputDevice = 'gamepad';
|
|
6098
|
+
else if (mouseActive)
|
|
6099
|
+
lastInputDevice = 'mouse';
|
|
6100
|
+
else if (keyboardActive)
|
|
6101
|
+
lastInputDevice = 'keyboard';
|
|
6102
|
+
|
|
6103
|
+
// set flag if gamepad is last device
|
|
6104
|
+
isUsingGamepad = lastInputDevice === 'gamepad';
|
|
6105
|
+
}
|
|
6106
|
+
|
|
5974
6107
|
// gamepads are updated by engine every frame automatically
|
|
5975
6108
|
function gamepadsUpdate()
|
|
5976
6109
|
{
|
|
@@ -6093,7 +6226,6 @@ function inputUpdate()
|
|
|
6093
6226
|
gamepadHadInput[i] = true;
|
|
6094
6227
|
if (!gamepadHadInput[gamepadPrimary])
|
|
6095
6228
|
gamepadPrimary = i;
|
|
6096
|
-
isUsingGamepad ||= (gamepadPrimary === i);
|
|
6097
6229
|
}
|
|
6098
6230
|
|
|
6099
6231
|
if (gamepad.mapping === 'standard')
|
|
@@ -6240,12 +6372,19 @@ function touchGamepadRelayout()
|
|
|
6240
6372
|
const setZone = (z, css)=> z.style.cssText =
|
|
6241
6373
|
'position:absolute;pointer-events:auto;touch-action:none;' + css;
|
|
6242
6374
|
|
|
6243
|
-
if (paused
|
|
6375
|
+
if (paused)
|
|
6244
6376
|
{
|
|
6245
|
-
// while paused,
|
|
6246
|
-
|
|
6377
|
+
// the gamepad is hidden while paused, so its side zones must not capture
|
|
6378
|
+
// touches - otherwise they silently steal taps from menus and dialogs
|
|
6247
6379
|
for (const zone of touchGamepadSideZones) zone.style.display = 'none';
|
|
6248
|
-
|
|
6380
|
+
if (touchGamepadCenterButtonSize)
|
|
6381
|
+
{
|
|
6382
|
+
// any touch presses start
|
|
6383
|
+
setZone(touchGamepadZoneC, 'inset:0');
|
|
6384
|
+
touchGamepadZoneC.style.display = '';
|
|
6385
|
+
}
|
|
6386
|
+
else
|
|
6387
|
+
touchGamepadZoneC.style.display = 'none';
|
|
6249
6388
|
}
|
|
6250
6389
|
else
|
|
6251
6390
|
{
|
|
@@ -6521,7 +6660,6 @@ function touchGamepadPointerDown(e, zone)
|
|
|
6521
6660
|
e.preventDefault();
|
|
6522
6661
|
zone.setPointerCapture(e.pointerId);
|
|
6523
6662
|
touchGamepadTimer.set();
|
|
6524
|
-
isUsingGamepad = true;
|
|
6525
6663
|
|
|
6526
6664
|
// resume audio on first interaction
|
|
6527
6665
|
if (soundEnable && !headlessMode && audioContext && !audioIsRunning())
|
|
@@ -8537,9 +8675,6 @@ class Particle
|
|
|
8537
8675
|
this.color.b = p2 * this.colorStart.b + p1 * this.colorEnd.b;
|
|
8538
8676
|
this.color.a = (p2 * this.colorStart.a + p1 * this.colorEnd.a) * alphaFade;
|
|
8539
8677
|
|
|
8540
|
-
// draw the particle
|
|
8541
|
-
additive && setBlendMode(true);
|
|
8542
|
-
|
|
8543
8678
|
// update the position and angle for drawing
|
|
8544
8679
|
const pos = particleDrawPos.set(this.pos.x, this.pos.y);
|
|
8545
8680
|
let angle = this.angle;
|
|
@@ -8552,6 +8687,9 @@ class Particle
|
|
|
8552
8687
|
emitter.pos.y + pos.x*s + pos.y*c);
|
|
8553
8688
|
angle += a;
|
|
8554
8689
|
}
|
|
8690
|
+
|
|
8691
|
+
// draw the particle
|
|
8692
|
+
additive && setAdditiveBlendMode();
|
|
8555
8693
|
if (trailScale)
|
|
8556
8694
|
{
|
|
8557
8695
|
// trail style particles
|
|
@@ -8569,7 +8707,7 @@ class Particle
|
|
|
8569
8707
|
}
|
|
8570
8708
|
else
|
|
8571
8709
|
drawTile(pos, size, this.tileInfo, this.color, angle, this.mirror);
|
|
8572
|
-
additive &&
|
|
8710
|
+
additive && setAdditiveBlendMode(false);
|
|
8573
8711
|
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
8574
8712
|
}
|
|
8575
8713
|
}
|
|
@@ -10479,7 +10617,7 @@ class LightSystemPlugin
|
|
|
10479
10617
|
|
|
10480
10618
|
// 3. walk engineObjects calling renderLight() — additive blend
|
|
10481
10619
|
// (lightmap accumulates raw additive color contributions)
|
|
10482
|
-
|
|
10620
|
+
setAdditiveBlendMode();
|
|
10483
10621
|
glContext.enable(glContext.BLEND);
|
|
10484
10622
|
glContext.blendFunc(glContext.ONE, glContext.ONE);
|
|
10485
10623
|
|
|
@@ -10513,7 +10651,7 @@ class LightSystemPlugin
|
|
|
10513
10651
|
// is, and any debug text / future draw could sample the lightmap)
|
|
10514
10652
|
if (glActiveTexture)
|
|
10515
10653
|
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
|
|
10516
|
-
|
|
10654
|
+
setAdditiveBlendMode(prevAdditive);
|
|
10517
10655
|
glSetInstancedMode(true);
|
|
10518
10656
|
}
|
|
10519
10657
|
function lightSystemContextLost()
|
|
@@ -10922,7 +11060,7 @@ class UISystemPlugin
|
|
|
10922
11060
|
let targetPos, targetSize;
|
|
10923
11061
|
if (o.parent)
|
|
10924
11062
|
{
|
|
10925
|
-
targetPos = o.parent.
|
|
11063
|
+
targetPos = o.parent.nativePos;
|
|
10926
11064
|
targetSize = o.parent.size;
|
|
10927
11065
|
}
|
|
10928
11066
|
else
|
|
@@ -10936,7 +11074,7 @@ class UISystemPlugin
|
|
|
10936
11074
|
}
|
|
10937
11075
|
|
|
10938
11076
|
const a = o.anchor;
|
|
10939
|
-
o.
|
|
11077
|
+
o.nativePos = targetPos
|
|
10940
11078
|
.add(targetSize.multiply(a).scale(.5)) // anchor point on target
|
|
10941
11079
|
.subtract(o.size.multiply(a).scale(.5)) // pivot shift on self
|
|
10942
11080
|
.add(o.localPos); // user offset
|
|
@@ -11042,13 +11180,18 @@ class UISystemPlugin
|
|
|
11042
11180
|
|
|
11043
11181
|
function updateObject(o)
|
|
11044
11182
|
{
|
|
11045
|
-
if (!o.visible) return;
|
|
11183
|
+
if (o.destroyed || !o.visible) return;
|
|
11046
11184
|
|
|
11047
11185
|
// update in reverse order to detect mouse enter/leave
|
|
11048
11186
|
updateTransforms(o);
|
|
11049
11187
|
for (let i=o.children.length; i--;)
|
|
11050
|
-
|
|
11051
|
-
|
|
11188
|
+
{
|
|
11189
|
+
// a child may destroy siblings mid-update (e.g. dialog close)
|
|
11190
|
+
const child = o.children[i];
|
|
11191
|
+
child && updateObject(child);
|
|
11192
|
+
}
|
|
11193
|
+
if (!o.destroyed)
|
|
11194
|
+
o.update();
|
|
11052
11195
|
}
|
|
11053
11196
|
}
|
|
11054
11197
|
function uiRender()
|
|
@@ -11472,10 +11615,15 @@ class UIObject
|
|
|
11472
11615
|
ASSERT(isVector2(pos), 'ui object pos must be a vec2');
|
|
11473
11616
|
ASSERT(isVector2(size), 'ui object size must be a vec2');
|
|
11474
11617
|
|
|
11475
|
-
/** @property {Vector2} -
|
|
11618
|
+
/** @property {Vector2} - Position you set: an offset from this object's
|
|
11619
|
+
* anchor point (the parent box, or the canvas for roots). This is the
|
|
11620
|
+
* input that controls placement — set this, not nativePos. */
|
|
11476
11621
|
this.localPos = pos.copy();
|
|
11477
|
-
/** @property {Vector2} -
|
|
11478
|
-
|
|
11622
|
+
/** @property {Vector2} - Resolved position in native UI space, recomputed
|
|
11623
|
+
* every frame from localPos + anchor (and nativeHeight, if set). This is a
|
|
11624
|
+
* derived output used for drawing and hit-testing; assigning to it has no
|
|
11625
|
+
* effect since it is overwritten each frame. Set localPos instead. */
|
|
11626
|
+
this.nativePos = pos.copy();
|
|
11479
11627
|
/** @property {Vector2} - Screen space size of the object */
|
|
11480
11628
|
this.size = size.copy();
|
|
11481
11629
|
/** @property {Color} - Color of the object */
|
|
@@ -11610,7 +11758,7 @@ class UIObject
|
|
|
11610
11758
|
const size = !isTouchDevice ? this.size :
|
|
11611
11759
|
this.size.add(vec2(this.extraTouchSize || 0));
|
|
11612
11760
|
const pos = uiSystem.screenToNative(mousePosScreen);
|
|
11613
|
-
return isOverlapping(this.
|
|
11761
|
+
return isOverlapping(this.nativePos, size, pos);
|
|
11614
11762
|
}
|
|
11615
11763
|
|
|
11616
11764
|
/** Update the object, called automatically by plugin once each frame */
|
|
@@ -11700,7 +11848,7 @@ class UIObject
|
|
|
11700
11848
|
this.color : this.color;
|
|
11701
11849
|
const lineWidth = this.lineWidth * (isNavigationObject ? 1.5 : 1);
|
|
11702
11850
|
|
|
11703
|
-
uiSystem.drawRect(this.
|
|
11851
|
+
uiSystem.drawRect(this.nativePos, this.size, color, lineWidth, lineColor, this.cornerRadius, this.gradientColor, this.shadowColor, this.shadowBlur, this.shadowOffset);
|
|
11704
11852
|
}
|
|
11705
11853
|
|
|
11706
11854
|
/** Get the size for text with overrides and scale
|
|
@@ -11737,8 +11885,8 @@ class UIObject
|
|
|
11737
11885
|
let text = 'type = ' + this.constructor.name;
|
|
11738
11886
|
if (this.text)
|
|
11739
11887
|
text += '\ntext = ' + this.text;
|
|
11740
|
-
if (this.
|
|
11741
|
-
text += '\
|
|
11888
|
+
if (this.nativePos.x || this.nativePos.y)
|
|
11889
|
+
text += '\nnativePos = ' + this.nativePos;
|
|
11742
11890
|
if (this.localPos.x || this.localPos.y)
|
|
11743
11891
|
text += '\nlocalPos = ' + this.localPos;
|
|
11744
11892
|
if (this.size.x || this.size.y)
|
|
@@ -11758,7 +11906,7 @@ class UIObject
|
|
|
11758
11906
|
this.isHoverObject() ? YELLOW :
|
|
11759
11907
|
this.disabled ? PURPLE :
|
|
11760
11908
|
this.interactive ? RED : BLUE;
|
|
11761
|
-
uiSystem.drawRect(this.
|
|
11909
|
+
uiSystem.drawRect(this.nativePos, this.size, CLEAR_BLACK, 4, color);
|
|
11762
11910
|
}
|
|
11763
11911
|
|
|
11764
11912
|
/** Internal function called when object is clicked
|
|
@@ -11841,7 +11989,7 @@ class UIText extends UIObject
|
|
|
11841
11989
|
|
|
11842
11990
|
// render the text
|
|
11843
11991
|
const textSize = this.getTextSize();
|
|
11844
|
-
uiSystem.drawText(this.text, this.
|
|
11992
|
+
uiSystem.drawText(this.text, this.nativePos, textSize, this.textColor, this.textLineWidth, this.textLineColor, this.align, this.font, this.fontStyle, true, this.textShadow, this.shadowColor, this.shadowBlur, this.shadowOffset);
|
|
11845
11993
|
}
|
|
11846
11994
|
}
|
|
11847
11995
|
|
|
@@ -11936,7 +12084,7 @@ class UITextInput extends UIObject
|
|
|
11936
12084
|
let text = this.text;
|
|
11937
12085
|
if (this.isKeyInputObject()) // add a cursor to end of text
|
|
11938
12086
|
text += timeReal%1 < .5 ? '█' : '░';
|
|
11939
|
-
uiSystem.drawText(text, this.
|
|
12087
|
+
uiSystem.drawText(text, this.nativePos, textSize,
|
|
11940
12088
|
this.textColor, this.textLineWidth, this.textLineColor, this.align, this.font, this.fontStyle, true, this.textShadow);
|
|
11941
12089
|
}
|
|
11942
12090
|
}
|
|
@@ -11979,7 +12127,7 @@ class UITile extends UIObject
|
|
|
11979
12127
|
}
|
|
11980
12128
|
render()
|
|
11981
12129
|
{
|
|
11982
|
-
uiSystem.drawTile(this.
|
|
12130
|
+
uiSystem.drawTile(this.nativePos, this.size, this.tileInfo, this.color, this.angle, this.mirror, this.shadowColor, this.shadowBlur, this.shadowOffset);
|
|
11983
12131
|
}
|
|
11984
12132
|
}
|
|
11985
12133
|
|
|
@@ -12018,7 +12166,7 @@ class UIButton extends UIObject
|
|
|
12018
12166
|
|
|
12019
12167
|
// draw the text scaled to fit
|
|
12020
12168
|
const textSize = this.getTextSize();
|
|
12021
|
-
uiSystem.drawText(this.text, this.
|
|
12169
|
+
uiSystem.drawText(this.text, this.nativePos.add(this.textOffset), textSize,
|
|
12022
12170
|
this.textColor, this.textLineWidth, this.textLineColor, this.align, this.font, this.fontStyle, true, this.textShadow);
|
|
12023
12171
|
}
|
|
12024
12172
|
}
|
|
@@ -12066,13 +12214,13 @@ class UICheckbox extends UIObject
|
|
|
12066
12214
|
const p = this.cornerRadius / min(this.size.x, this.size.y) * 2;
|
|
12067
12215
|
const length = lerp(1, 2**.5/2, p) / 2;
|
|
12068
12216
|
let s = this.size.scale(length);
|
|
12069
|
-
uiSystem.drawLine(this.
|
|
12070
|
-
uiSystem.drawLine(this.
|
|
12217
|
+
uiSystem.drawLine(this.nativePos.add(s.multiply(vec2(-1))), this.nativePos.add(s.multiply(vec2(1))), this.lineWidth, this.lineColor);
|
|
12218
|
+
uiSystem.drawLine(this.nativePos.add(s.multiply(vec2(-1,1))), this.nativePos.add(s.multiply(vec2(1,-1))), this.lineWidth, this.lineColor);
|
|
12071
12219
|
}
|
|
12072
12220
|
|
|
12073
12221
|
// draw the text next to the checkbox
|
|
12074
12222
|
const textSize = this.getTextSize();
|
|
12075
|
-
const pos = this.
|
|
12223
|
+
const pos = this.nativePos.add(vec2(this.size.x,0));
|
|
12076
12224
|
uiSystem.drawText(this.text, pos, textSize,
|
|
12077
12225
|
this.textColor, this.textLineWidth, this.textLineColor, 'left', this.font, this.fontStyle, false, this.textShadow);
|
|
12078
12226
|
}
|
|
@@ -12128,7 +12276,7 @@ class UISlider extends UIObject
|
|
|
12128
12276
|
const isHorizontal = this.size.x > this.size.y;
|
|
12129
12277
|
const handleSize = isHorizontal ? this.size.y : this.size.x;
|
|
12130
12278
|
const barSize = isHorizontal ? this.size.x : this.size.y;
|
|
12131
|
-
const centerPos = isHorizontal ? this.
|
|
12279
|
+
const centerPos = isHorizontal ? this.nativePos.x : this.nativePos.y;
|
|
12132
12280
|
|
|
12133
12281
|
// check if value changed
|
|
12134
12282
|
const handleWidth = barSize - handleSize;
|
|
@@ -12162,7 +12310,7 @@ class UISlider extends UIObject
|
|
|
12162
12310
|
const minWidth = min(handleWidth, this.cornerRadius * 2);
|
|
12163
12311
|
const progressWidth = lerp(minWidth, barWidth, this.value);
|
|
12164
12312
|
const p = (progressWidth - barWidth) * (isHorizontal ? .5 : -.5);
|
|
12165
|
-
const pos = this.
|
|
12313
|
+
const pos = this.nativePos.add(isHorizontal ? vec2(p, 0) : vec2(0, p));
|
|
12166
12314
|
const color = this.disabled ? this.disabledColor : this.handleColor;
|
|
12167
12315
|
const drawSize = isHorizontal ?
|
|
12168
12316
|
vec2(progressWidth, this.size.y) : vec2(this.size.x, progressWidth);
|
|
@@ -12173,7 +12321,7 @@ class UISlider extends UIObject
|
|
|
12173
12321
|
// draw the slider handle
|
|
12174
12322
|
const value = clamp(isHorizontal ? this.value : 1 - this.value);
|
|
12175
12323
|
const p = (barWidth - handleWidth) * (value - .5);
|
|
12176
|
-
const pos = this.
|
|
12324
|
+
const pos = this.nativePos.add(isHorizontal ? vec2(p, 0) : vec2(0, p));
|
|
12177
12325
|
const color = this.disabled ? this.disabledColor : this.handleColor;
|
|
12178
12326
|
const drawSize = vec2(handleWidth);
|
|
12179
12327
|
uiSystem.drawRect(pos, drawSize, color, this.lineWidth, this.lineColor, this.cornerRadius, this.gradientColor);
|
|
@@ -12181,7 +12329,7 @@ class UISlider extends UIObject
|
|
|
12181
12329
|
|
|
12182
12330
|
// draw the text scaled to fit on the slider
|
|
12183
12331
|
const textSize = this.getTextSize();
|
|
12184
|
-
uiSystem.drawText(this.text, this.
|
|
12332
|
+
uiSystem.drawText(this.text, this.nativePos, textSize,
|
|
12185
12333
|
this.textColor, this.textLineWidth, this.textLineColor, this.align, this.font, this.fontStyle, true, this.textShadow);
|
|
12186
12334
|
}
|
|
12187
12335
|
navigatePressed()
|
|
@@ -12320,7 +12468,7 @@ class UIVideo extends UIObject
|
|
|
12320
12468
|
const context = uiSystem.uiContext;
|
|
12321
12469
|
const s = this.size;
|
|
12322
12470
|
context.save();
|
|
12323
|
-
context.translate(this.
|
|
12471
|
+
context.translate(this.nativePos.x, this.nativePos.y);
|
|
12324
12472
|
context.drawImage(this.video, -s.x/2, -s.y/2, s.x, s.y);
|
|
12325
12473
|
context.restore();
|
|
12326
12474
|
}
|
|
@@ -14549,8 +14697,8 @@ async function box2dInit()
|
|
|
14549
14697
|
* This function can not apply color because it draws using the 2d context
|
|
14550
14698
|
* @param {Vector2} pos - Screen space position
|
|
14551
14699
|
* @param {Vector2} size - Screen space size
|
|
14552
|
-
* @param {TileInfo} startTile -
|
|
14553
|
-
* @param {number} [borderSize] -
|
|
14700
|
+
* @param {TileInfo} startTile - Top-left tile of the 3x3 block to sample (see drawNineSlice)
|
|
14701
|
+
* @param {number} [borderSize] - Rendered thickness of the border sections
|
|
14554
14702
|
* @param {number} [extraSpace] - Extra spacing adjustment
|
|
14555
14703
|
* @param {number} [angle] - Angle to rotate by
|
|
14556
14704
|
* @memberof DrawUtilities */
|
|
@@ -14561,11 +14709,16 @@ function drawNineSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2,
|
|
|
14561
14709
|
|
|
14562
14710
|
/** Draw a scalable nine-slice UI element in world space
|
|
14563
14711
|
* This function can apply color and additive color if WebGL is enabled
|
|
14712
|
+
* The nine-slice samples a 3x3 block of tiles from the tilesheet, it does not
|
|
14713
|
+
* subdivide a single tile. Pass the top-left tile of that block as startTile;
|
|
14714
|
+
* the other 8 tiles (edges, corners, and center) are taken automatically from
|
|
14715
|
+
* the 3x3 grid of tiles extending right and down from it. borderSize only sets
|
|
14716
|
+
* the rendered thickness of the edges and corners, not how the texture is cut.
|
|
14564
14717
|
* @param {Vector2} pos - World space position
|
|
14565
14718
|
* @param {Vector2} size - World space size
|
|
14566
|
-
* @param {TileInfo} startTile -
|
|
14719
|
+
* @param {TileInfo} startTile - Top-left tile of the 3x3 block to sample the nine-slice from
|
|
14567
14720
|
* @param {Color} [color] - Color to modulate with
|
|
14568
|
-
* @param {number} [borderSize] -
|
|
14721
|
+
* @param {number} [borderSize] - Rendered thickness of the border sections
|
|
14569
14722
|
* @param {Color} [additiveColor] - Additive color
|
|
14570
14723
|
* @param {number} [extraSpace] - Extra spacing adjustment
|
|
14571
14724
|
* @param {number} [angle] - Angle to rotate by
|
|
@@ -14575,7 +14728,8 @@ function drawNineSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2,
|
|
|
14575
14728
|
* @memberof DrawUtilities */
|
|
14576
14729
|
function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor, extraSpace=.05, angle=0, useWebGL=glEnable, screenSpace, context)
|
|
14577
14730
|
{
|
|
14578
|
-
// setup nine slice tiles
|
|
14731
|
+
// setup nine slice tiles - startTile is the top-left of a 3x3 tile block,
|
|
14732
|
+
// so the center tile is one tile down and right from it
|
|
14579
14733
|
const centerTile = startTile.offset(startTile.size);
|
|
14580
14734
|
const centerSize = size.add(vec2(extraSpace-borderSize*2));
|
|
14581
14735
|
const cornerSize = vec2(borderSize);
|
|
@@ -14609,8 +14763,8 @@ function drawNineSlice(pos, size, startTile, color, borderSize=1, additiveColor,
|
|
|
14609
14763
|
* This function can not apply color because it draws using the 2d context
|
|
14610
14764
|
* @param {Vector2} pos - Screen space position
|
|
14611
14765
|
* @param {Vector2} size - Screen space size
|
|
14612
|
-
* @param {TileInfo} startTile -
|
|
14613
|
-
* @param {number} [borderSize] -
|
|
14766
|
+
* @param {TileInfo} startTile - First of 3 consecutive tiles: corner, side, center (see drawThreeSlice)
|
|
14767
|
+
* @param {number} [borderSize] - Rendered thickness of the border sections
|
|
14614
14768
|
* @param {number} [extraSpace] - Extra spacing adjustment
|
|
14615
14769
|
* @param {number} [angle] - Angle to rotate by
|
|
14616
14770
|
* @memberof DrawUtilities */
|
|
@@ -14621,11 +14775,15 @@ function drawThreeSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2,
|
|
|
14621
14775
|
|
|
14622
14776
|
/** Draw a scalable three-slice UI element in world space
|
|
14623
14777
|
* This function can apply color and additive color if WebGL is enabled
|
|
14778
|
+
* The three-slice samples 3 consecutive tiles from the tilesheet, it does not
|
|
14779
|
+
* subdivide a single tile. Pass the first tile as startTile; the three tiles
|
|
14780
|
+
* are used in order as corner, side, and center, then rotated and mirrored to
|
|
14781
|
+
* build all four edges and corners. borderSize only sets the rendered thickness.
|
|
14624
14782
|
* @param {Vector2} pos - World space position
|
|
14625
14783
|
* @param {Vector2} size - World space size
|
|
14626
|
-
* @param {TileInfo} startTile -
|
|
14784
|
+
* @param {TileInfo} startTile - First of 3 consecutive tiles (corner, side, center) for the three-slice
|
|
14627
14785
|
* @param {Color} [color] - Color to modulate with
|
|
14628
|
-
* @param {number} [borderSize] -
|
|
14786
|
+
* @param {number} [borderSize] - Rendered thickness of the border sections
|
|
14629
14787
|
* @param {Color} [additiveColor] - Additive color
|
|
14630
14788
|
* @param {number} [extraSpace] - Extra spacing adjustment
|
|
14631
14789
|
* @param {number} [angle] - Angle to rotate by
|
|
@@ -14635,7 +14793,7 @@ function drawThreeSliceScreen(pos, size, startTile, borderSize=32, extraSpace=2,
|
|
|
14635
14793
|
* @memberof DrawUtilities */
|
|
14636
14794
|
function drawThreeSlice(pos, size, startTile, color, borderSize=1, additiveColor, extraSpace=.05, angle=0, useWebGL=glEnable, screenSpace, context)
|
|
14637
14795
|
{
|
|
14638
|
-
// setup three slice tiles
|
|
14796
|
+
// setup three slice tiles - 3 tiles in a row starting at startTile
|
|
14639
14797
|
const cornerTile = startTile.frame(0);
|
|
14640
14798
|
const sideTile = startTile.frame(1);
|
|
14641
14799
|
const centerTile = startTile.frame(2);
|
|
@@ -14683,8 +14841,9 @@ function drawThreeSlice(pos, size, startTile, color, borderSize=1, additiveColor
|
|
|
14683
14841
|
* @memberof DrawUtilities */
|
|
14684
14842
|
function drawCrescent(pos, size=1, percent=0, color=WHITE, angle=0, invert=false, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
14685
14843
|
{
|
|
14686
|
-
|
|
14687
|
-
|
|
14844
|
+
// build local-space points and let drawPoly apply pos/angle so screen space works
|
|
14845
|
+
const points = getCrescentPoints(vec2(), size, percent, 0, invert);
|
|
14846
|
+
drawPoly(points, color, lineWidth, lineColor, pos, angle, useWebGL, screenSpace, context);
|
|
14688
14847
|
}
|
|
14689
14848
|
|
|
14690
14849
|
/** Get the list of points that make up a crescent / moon-phase shape
|
|
@@ -16317,7 +16476,7 @@ export
|
|
|
16317
16476
|
drawCanvas2D,
|
|
16318
16477
|
drawText,
|
|
16319
16478
|
drawTextScreen,
|
|
16320
|
-
|
|
16479
|
+
setAdditiveBlendMode,
|
|
16321
16480
|
combineCanvases,
|
|
16322
16481
|
engineImageFont,
|
|
16323
16482
|
ImageFont,
|
|
@@ -16325,6 +16484,7 @@ export
|
|
|
16325
16484
|
toggleFullscreen,
|
|
16326
16485
|
setCursor,
|
|
16327
16486
|
getCameraSize,
|
|
16487
|
+
cameraFit,
|
|
16328
16488
|
isOnScreen,
|
|
16329
16489
|
|
|
16330
16490
|
// WebGL
|
|
@@ -16368,10 +16528,16 @@ export
|
|
|
16368
16528
|
mouseWheel,
|
|
16369
16529
|
mouseInWindow,
|
|
16370
16530
|
isUsingGamepad,
|
|
16531
|
+
lastInputDevice,
|
|
16532
|
+
inputMouseMoveThreshold,
|
|
16371
16533
|
inputPreventDefault,
|
|
16372
16534
|
gamepadPrimary,
|
|
16373
16535
|
isTouchDevice,
|
|
16374
16536
|
setInputPreventDefault,
|
|
16537
|
+
setInputMouseMoveThreshold,
|
|
16538
|
+
usingMouseInput,
|
|
16539
|
+
usingKeyboardInput,
|
|
16540
|
+
usingGamepadInput,
|
|
16375
16541
|
gamepadIsDown,
|
|
16376
16542
|
gamepadWasPressed,
|
|
16377
16543
|
gamepadWasReleased,
|