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