three-gamepad-controls 0.10.4 → 0.10.5

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.
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
2
+ import { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue } from "./utils.js";
2
3
  import { GamepadControls } from "./gamepad-controls.js";
3
4
  import { Vector2, Vector3 } from "three";
4
5
  //#region src/gamepad-arcball-controls.ts
@@ -76,12 +77,12 @@ var GamepadArcballControls = class extends GamepadControls {
76
77
  this.#endInteraction();
77
78
  return;
78
79
  }
79
- const rotateX = controls.enableRotate ? this.#applyDeadzone(gamepad.axes[axisRotateX] ?? 0, deadzone) : 0;
80
- const rotateY = controls.enableRotate ? this.#applyDeadzone(gamepad.axes[axisRotateY] ?? 0, deadzone) : 0;
81
- const panX = controls.enablePan ? this.#applyDeadzone(gamepad.axes[axisPanX] ?? 0, deadzone) : 0;
82
- const panY = controls.enablePan ? this.#applyDeadzone(gamepad.axes[axisPanY] ?? 0, deadzone) : 0;
83
- const zoom = controls.enableZoom ? this.#getButtonValue(gamepad, buttonZoomIn) - this.#getButtonValue(gamepad, buttonZoomOut) : 0;
84
- const zRotation = controls.enableRotate ? this.#getButtonValue(gamepad, buttonZRotateLeft) - this.#getButtonValue(gamepad, buttonZRotateRight) : 0;
80
+ const rotateX = controls.enableRotate ? applyGamepadDeadzone(gamepad.axes[axisRotateX] ?? 0, deadzone) : 0;
81
+ const rotateY = controls.enableRotate ? applyGamepadDeadzone(gamepad.axes[axisRotateY] ?? 0, deadzone) : 0;
82
+ const panX = controls.enablePan ? applyGamepadDeadzone(gamepad.axes[axisPanX] ?? 0, deadzone) : 0;
83
+ const panY = controls.enablePan ? applyGamepadDeadzone(gamepad.axes[axisPanY] ?? 0, deadzone) : 0;
84
+ const zoom = controls.enableZoom ? getGamepadButtonValue(gamepad, buttonZoomIn) - getGamepadButtonValue(gamepad, buttonZoomOut) : 0;
85
+ const zRotation = controls.enableRotate ? getGamepadButtonValue(gamepad, buttonZRotateLeft) - getGamepadButtonValue(gamepad, buttonZRotateRight) : 0;
85
86
  const activeInput = rotateX !== 0 || rotateY !== 0 || panX !== 0 || panY !== 0 || Math.abs(zoom) > deadzone || Math.abs(zRotation) > deadzone;
86
87
  if (!activeInput && focusPoint === null) {
87
88
  this.#endInteraction();
@@ -171,7 +172,7 @@ var GamepadArcballControls = class extends GamepadControls {
171
172
  }
172
173
  #consumeFocusPoint(gamepad, buttonFocus) {
173
174
  const controls = this.#controls;
174
- const focusPressed = this.#getButtonPressed(gamepad, buttonFocus);
175
+ const focusPressed = getGamepadButtonPressed(gamepad, buttonFocus);
175
176
  const shouldFocus = focusPressed && !this.#focusButtonPressed;
176
177
  this.#focusButtonPressed = focusPressed;
177
178
  if (!shouldFocus || !controls.enabled || !controls.enablePan || !controls.enableFocus || controls.scene === null) return null;
@@ -182,24 +183,6 @@ var GamepadArcballControls = class extends GamepadControls {
182
183
  this.#controls.dispatchEvent({ type: "end" });
183
184
  this.#wasInteracting = false;
184
185
  }
185
- #getButtonValue(gamepad, button) {
186
- const gamepadButton = gamepad.buttons[button];
187
- if (gamepadButton === void 0) return 0;
188
- if (gamepadButton.value !== 0) return gamepadButton.value;
189
- return gamepadButton.pressed ? 1 : 0;
190
- }
191
- #getButtonPressed(gamepad, button) {
192
- return gamepad.buttons[button]?.pressed ?? false;
193
- }
194
- /**
195
- * Returns `value` unchanged, or `0` if below the dead zone `threshold`.
196
- *
197
- * @param value - Raw axis or trigger value, typically in `[-1, 1]`.
198
- * @param threshold - Dead zone size; values below this magnitude are zeroed.
199
- */
200
- #applyDeadzone(value, threshold) {
201
- return Math.abs(value) < threshold ? 0 : value;
202
- }
203
186
  };
204
187
  //#endregion
205
188
  export { GamepadArcballControls };
@@ -1,3 +1,4 @@
1
+ import { getFirstConnectedGamepad, getGamepadByIndex } from "./utils.js";
1
2
  import { EventDispatcher } from "three";
2
3
  //#region src/gamepad-controls.ts
3
4
  /**
@@ -36,9 +37,17 @@ var GamepadControls = class extends EventDispatcher {
36
37
  */
37
38
  update(deltaTime) {
38
39
  if (!this.enabled) return;
39
- if (this.gamepad !== null) {
40
- const gamepads = navigator.getGamepads();
41
- this.gamepad = gamepads[this.gamepad.index] ?? null;
40
+ if (this.gamepad === null) {
41
+ const connectedGamepad = getFirstConnectedGamepad();
42
+ if (connectedGamepad !== null) this.onGamepadConnected(connectedGamepad);
43
+ } else {
44
+ const previousGamepad = this.gamepad;
45
+ const nextGamepad = getGamepadByIndex(previousGamepad.index);
46
+ if (nextGamepad === null) {
47
+ this.onGamepadDisconnected(previousGamepad);
48
+ return;
49
+ }
50
+ this.gamepad = nextGamepad;
42
51
  }
43
52
  if (this.gamepad === null) return;
44
53
  this.onUpdate(deltaTime, this.gamepad);
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
2
+ import { applyGamepadDeadzone, getGamepadButtonPressed } from "./utils.js";
2
3
  import { GamepadControls } from "./gamepad-controls.js";
3
4
  import { Matrix4, Vector2, Vector3 } from "three";
4
5
  //#region src/gamepad-drag-controls.ts
@@ -71,7 +72,7 @@ var GamepadDragControls = class extends GamepadControls {
71
72
  */
72
73
  onUpdate(deltaTime, gamepad) {
73
74
  const controls = this.#controls;
74
- const selectPressed = this.#getButtonPressed(gamepad, this.#options.buttonSelect);
75
+ const selectPressed = getGamepadButtonPressed(gamepad, this.#options.buttonSelect);
75
76
  const selectStarted = selectPressed && !this.#selectButtonPressed;
76
77
  this.#selectButtonPressed = selectPressed;
77
78
  if (!controls.enabled) {
@@ -114,10 +115,10 @@ var GamepadDragControls = class extends GamepadControls {
114
115
  const selected = this.#selected;
115
116
  if (selected === null) return;
116
117
  const { dragSpeed, rotateSpeed, deadzone, axisDragX, axisDragY, axisRotateX, axisRotateY } = this.#options;
117
- const dragX = this.#applyDeadzone(gamepad.axes[axisDragX] ?? 0, deadzone);
118
- const dragY = this.#applyDeadzone(gamepad.axes[axisDragY] ?? 0, deadzone);
119
- const rotateX = this.#applyDeadzone(gamepad.axes[axisRotateX] ?? 0, deadzone);
120
- const rotateY = this.#applyDeadzone(gamepad.axes[axisRotateY] ?? 0, deadzone);
118
+ const dragX = applyGamepadDeadzone(gamepad.axes[axisDragX] ?? 0, deadzone);
119
+ const dragY = applyGamepadDeadzone(gamepad.axes[axisDragY] ?? 0, deadzone);
120
+ const rotateX = applyGamepadDeadzone(gamepad.axes[axisRotateX] ?? 0, deadzone);
121
+ const rotateY = applyGamepadDeadzone(gamepad.axes[axisRotateY] ?? 0, deadzone);
121
122
  const dragged = this.#applyDrag(deltaTime, dragX, dragY, dragSpeed);
122
123
  const rotated = this.#applyRotation(deltaTime, rotateX, rotateY, rotateSpeed);
123
124
  if (dragged || rotated) this.#controls.dispatchEvent({
@@ -243,18 +244,6 @@ var GamepadDragControls = class extends GamepadControls {
243
244
  #isOrthographicCamera(camera) {
244
245
  return camera.isOrthographicCamera === true;
245
246
  }
246
- #getButtonPressed(gamepad, button) {
247
- return gamepad.buttons[button]?.pressed ?? false;
248
- }
249
- /**
250
- * Returns `value` unchanged, or `0` if below the dead zone `threshold`.
251
- *
252
- * @param value - Raw axis value, typically in `[-1, 1]`.
253
- * @param threshold - Dead zone size; values below this magnitude are zeroed.
254
- */
255
- #applyDeadzone(value, threshold) {
256
- return Math.abs(value) < threshold ? 0 : value;
257
- }
258
247
  };
259
248
  //#endregion
260
249
  export { GamepadDragControls };
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
2
+ import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
2
3
  import { GamepadControls } from "./gamepad-controls.js";
3
4
  import { MathUtils, Spherical, Vector3 } from "three";
4
5
  //#region src/gamepad-first-person-controls.ts
@@ -60,7 +61,7 @@ var GamepadFirstPersonControls = class extends GamepadControls {
60
61
  #applyMovement(deltaTime, gamepad, moveSpeed, deadzone, axisMoveForward, axisMoveRight, buttonMoveUp, buttonMoveDown) {
61
62
  const controls = this.#controls;
62
63
  const moveMult = deltaTime * controls.movementSpeed * moveSpeed;
63
- const forward = this.#applyDeadzone(gamepad.axes[axisMoveForward] ?? 0, deadzone);
64
+ const forward = applyGamepadDeadzone(gamepad.axes[axisMoveForward] ?? 0, deadzone);
64
65
  if (forward !== 0) {
65
66
  let distance = forward * moveMult;
66
67
  if (forward < 0 && controls.heightSpeed) {
@@ -69,16 +70,16 @@ var GamepadFirstPersonControls = class extends GamepadControls {
69
70
  }
70
71
  controls.object.translateZ(distance);
71
72
  }
72
- const strafe = this.#applyDeadzone(gamepad.axes[axisMoveRight] ?? 0, deadzone);
73
+ const strafe = applyGamepadDeadzone(gamepad.axes[axisMoveRight] ?? 0, deadzone);
73
74
  if (strafe !== 0) controls.object.translateX(strafe * moveMult);
74
- const up = gamepad.buttons[buttonMoveUp]?.value ?? 0;
75
- const down = gamepad.buttons[buttonMoveDown]?.value ?? 0;
75
+ const up = getGamepadButtonValue(gamepad, buttonMoveUp);
76
+ const down = getGamepadButtonValue(gamepad, buttonMoveDown);
76
77
  if (up > deadzone) controls.object.translateY(up * moveMult);
77
78
  if (down > deadzone) controls.object.translateY(-down * moveMult);
78
79
  }
79
80
  #applyLook(deltaTime, gamepad, lookSpeed, deadzone, axisLookX, axisLookY) {
80
- const lookX = this.#applyDeadzone(gamepad.axes[axisLookX] ?? 0, deadzone);
81
- const lookY = this.#applyDeadzone(gamepad.axes[axisLookY] ?? 0, deadzone);
81
+ const lookX = applyGamepadDeadzone(gamepad.axes[axisLookX] ?? 0, deadzone);
82
+ const lookY = applyGamepadDeadzone(gamepad.axes[axisLookY] ?? 0, deadzone);
82
83
  if (lookX === 0 && lookY === 0) return;
83
84
  const controls = this.#controls;
84
85
  const actualLookSpeed = controls.lookSpeed * lookSpeed * deltaTime * LOOK_SPEED_SCALE;
@@ -110,15 +111,6 @@ var GamepadFirstPersonControls = class extends GamepadControls {
110
111
  lon: MathUtils.radToDeg(this.#spherical.theta)
111
112
  };
112
113
  }
113
- /**
114
- * Returns `value` unchanged, or `0` if below the dead zone `threshold`.
115
- *
116
- * @param value - Raw axis or trigger value, typically in `[-1, 1]`.
117
- * @param threshold - Dead zone size; values below this magnitude are zeroed.
118
- */
119
- #applyDeadzone(value, threshold) {
120
- return Math.abs(value) < threshold ? 0 : value;
121
- }
122
114
  };
123
115
  //#endregion
124
116
  export { GamepadFirstPersonControls };
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
2
+ import { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue } from "./utils.js";
2
3
  import { GamepadControls } from "./gamepad-controls.js";
3
4
  import { Quaternion } from "three";
4
5
  //#region src/gamepad-fly-controls.ts
@@ -52,32 +53,23 @@ var GamepadFlyControls = class extends GamepadControls {
52
53
  onUpdate(deltaTime, gamepad) {
53
54
  const { moveSpeed, rotateSpeed, deadzone, axisMoveForward, axisMoveRight, axisLookX, axisLookY, buttonRollLeft, buttonRollRight, buttonMoveUp, buttonMoveDown } = this.#options;
54
55
  const moveMult = deltaTime * this.#controls.movementSpeed * moveSpeed;
55
- const fwd = this.#applyDeadzone(gamepad.axes[axisMoveForward] ?? 0, deadzone);
56
+ const fwd = applyGamepadDeadzone(gamepad.axes[axisMoveForward] ?? 0, deadzone);
56
57
  if (fwd !== 0) this.#controls.object.translateZ(fwd * moveMult);
57
- const strafe = this.#applyDeadzone(gamepad.axes[axisMoveRight] ?? 0, deadzone);
58
+ const strafe = applyGamepadDeadzone(gamepad.axes[axisMoveRight] ?? 0, deadzone);
58
59
  if (strafe !== 0) this.#controls.object.translateX(strafe * moveMult);
59
- const up = gamepad.buttons[buttonMoveUp]?.value ?? 0;
60
- const down = gamepad.buttons[buttonMoveDown]?.value ?? 0;
60
+ const up = getGamepadButtonValue(gamepad, buttonMoveUp);
61
+ const down = getGamepadButtonValue(gamepad, buttonMoveDown);
61
62
  if (up > deadzone) this.#controls.object.translateY(up * moveMult);
62
63
  if (down > deadzone) this.#controls.object.translateY(-down * moveMult);
63
64
  const rotMult = deltaTime * this.#controls.rollSpeed * rotateSpeed;
64
- const pitch = -this.#applyDeadzone(gamepad.axes[axisLookY] ?? 0, deadzone);
65
- const yaw = -this.#applyDeadzone(gamepad.axes[axisLookX] ?? 0, deadzone);
66
- const roll = (gamepad.buttons[buttonRollLeft]?.pressed ? 1 : 0) - (gamepad.buttons[buttonRollRight]?.pressed ? 1 : 0);
65
+ const pitch = -applyGamepadDeadzone(gamepad.axes[axisLookY] ?? 0, deadzone);
66
+ const yaw = -applyGamepadDeadzone(gamepad.axes[axisLookX] ?? 0, deadzone);
67
+ const roll = (getGamepadButtonPressed(gamepad, buttonRollLeft) ? 1 : 0) - (getGamepadButtonPressed(gamepad, buttonRollRight) ? 1 : 0);
67
68
  if (pitch !== 0 || yaw !== 0 || roll !== 0) {
68
69
  this.#tmpQuaternion.set(pitch * rotMult, yaw * rotMult, roll * rotMult, 1).normalize();
69
70
  this.#controls.object.quaternion.multiply(this.#tmpQuaternion);
70
71
  }
71
72
  }
72
- /**
73
- * Returns `value` unchanged, or `0` if below the dead zone `threshold`.
74
- *
75
- * @param value - Raw axis or trigger value, typically in `[-1, 1]`.
76
- * @param threshold - Dead zone size; values below this magnitude are zeroed.
77
- */
78
- #applyDeadzone(value, threshold) {
79
- return Math.abs(value) < threshold ? 0 : value;
80
- }
81
73
  };
82
74
  //#endregion
83
75
  export { GamepadFlyControls };
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
2
+ import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
2
3
  import { GamepadControls } from "./gamepad-controls.js";
3
4
  //#region src/gamepad-orbit-controls.ts
4
5
  /**
@@ -46,27 +47,18 @@ var GamepadOrbitControls = class extends GamepadControls {
46
47
  */
47
48
  onUpdate(deltaTime, gamepad) {
48
49
  const { rotateSpeed, panSpeed, zoomSpeed, deadzone, axisRotateX, axisRotateY, axisPanX, axisPanY, buttonDollyIn, buttonDollyOut } = this.#options;
49
- const rotX = this.#applyDeadzone(gamepad.axes[axisRotateX] ?? 0, deadzone);
50
- const rotY = this.#applyDeadzone(gamepad.axes[axisRotateY] ?? 0, deadzone);
50
+ const rotX = applyGamepadDeadzone(gamepad.axes[axisRotateX] ?? 0, deadzone);
51
+ const rotY = applyGamepadDeadzone(gamepad.axes[axisRotateY] ?? 0, deadzone);
51
52
  if (rotX !== 0) this.#controls.rotateLeft(rotX * rotateSpeed * deltaTime * Math.PI);
52
53
  if (rotY !== 0) this.#controls.rotateUp(rotY * rotateSpeed * deltaTime * Math.PI);
53
- const panX = this.#applyDeadzone(gamepad.axes[axisPanX] ?? 0, deadzone);
54
- const panY = this.#applyDeadzone(gamepad.axes[axisPanY] ?? 0, deadzone);
54
+ const panX = applyGamepadDeadzone(gamepad.axes[axisPanX] ?? 0, deadzone);
55
+ const panY = applyGamepadDeadzone(gamepad.axes[axisPanY] ?? 0, deadzone);
55
56
  if (panX !== 0 || panY !== 0) this.#controls.pan(panX * panSpeed * deltaTime * 500, panY * panSpeed * deltaTime * 500);
56
- const triggerIn = gamepad.buttons[buttonDollyIn]?.value ?? 0;
57
- const triggerOut = gamepad.buttons[buttonDollyOut]?.value ?? 0;
57
+ const triggerIn = getGamepadButtonValue(gamepad, buttonDollyIn);
58
+ const triggerOut = getGamepadButtonValue(gamepad, buttonDollyOut);
58
59
  if (triggerIn > deadzone) this.#controls.dollyIn(1 / (1 + zoomSpeed * triggerIn * deltaTime));
59
60
  if (triggerOut > deadzone) this.#controls.dollyOut(1 / (1 + zoomSpeed * triggerOut * deltaTime));
60
61
  }
61
- /**
62
- * Returns `value` unchanged, or `0` if below the dead zone `threshold`.
63
- *
64
- * @param value - Raw axis or trigger value, typically in `[-1, 1]`.
65
- * @param threshold - Dead zone size; values below this magnitude are zeroed.
66
- */
67
- #applyDeadzone(value, threshold) {
68
- return Math.abs(value) < threshold ? 0 : value;
69
- }
70
62
  };
71
63
  //#endregion
72
64
  export { GamepadOrbitControls };
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS } from "./core.js";
2
+ import { applyGamepadDeadzone } from "./utils.js";
2
3
  import { GamepadControls } from "./gamepad-controls.js";
3
4
  import { Euler } from "three";
4
5
  //#region src/gamepad-pointer-lock-controls.ts
@@ -47,12 +48,12 @@ var GamepadPointerLockControls = class extends GamepadControls {
47
48
  */
48
49
  onUpdate(deltaTime, gamepad) {
49
50
  const { moveSpeed, lookSpeed, deadzone, axisMoveForward, axisMoveRight, axisLookX, axisLookY } = this.#options;
50
- const fwd = this.#applyDeadzone(gamepad.axes[axisMoveForward] ?? 0, deadzone);
51
- const strafe = this.#applyDeadzone(gamepad.axes[axisMoveRight] ?? 0, deadzone);
51
+ const fwd = applyGamepadDeadzone(gamepad.axes[axisMoveForward] ?? 0, deadzone);
52
+ const strafe = applyGamepadDeadzone(gamepad.axes[axisMoveRight] ?? 0, deadzone);
52
53
  if (fwd !== 0) this.#controls.moveForward(-fwd * moveSpeed * deltaTime);
53
54
  if (strafe !== 0) this.#controls.moveRight(strafe * moveSpeed * deltaTime);
54
- const lookX = this.#applyDeadzone(gamepad.axes[axisLookX] ?? 0, deadzone);
55
- const lookY = this.#applyDeadzone(gamepad.axes[axisLookY] ?? 0, deadzone);
55
+ const lookX = applyGamepadDeadzone(gamepad.axes[axisLookX] ?? 0, deadzone);
56
+ const lookY = applyGamepadDeadzone(gamepad.axes[axisLookY] ?? 0, deadzone);
56
57
  if (lookX !== 0 || lookY !== 0) {
57
58
  const camera = this.#controls.object;
58
59
  const scale = lookSpeed * this.#controls.pointerSpeed * deltaTime * Math.PI;
@@ -63,15 +64,6 @@ var GamepadPointerLockControls = class extends GamepadControls {
63
64
  camera.quaternion.setFromEuler(this.#euler);
64
65
  }
65
66
  }
66
- /**
67
- * Returns `value` unchanged, or `0` if below the dead zone `threshold`.
68
- *
69
- * @param value - Raw axis value, typically in `[-1, 1]`.
70
- * @param threshold - Dead zone size; values below this magnitude are zeroed.
71
- */
72
- #applyDeadzone(value, threshold) {
73
- return Math.abs(value) < threshold ? 0 : value;
74
- }
75
67
  };
76
68
  //#endregion
77
69
  export { GamepadPointerLockControls };
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
2
+ import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
2
3
  import { GamepadControls } from "./gamepad-controls.js";
3
4
  //#region src/gamepad-trackball-controls.ts
4
5
  /**
@@ -57,8 +58,8 @@ var GamepadTrackballControls = class extends GamepadControls {
57
58
  controls._lastAngle = 0;
58
59
  return;
59
60
  }
60
- const rotX = this.#applyDeadzone(gamepad.axes[axisRotateX] ?? 0, deadzone);
61
- const rotY = this.#applyDeadzone(gamepad.axes[axisRotateY] ?? 0, deadzone);
61
+ const rotX = applyGamepadDeadzone(gamepad.axes[axisRotateX] ?? 0, deadzone);
62
+ const rotY = applyGamepadDeadzone(gamepad.axes[axisRotateY] ?? 0, deadzone);
62
63
  if (rotX === 0 && rotY === 0) return;
63
64
  const scale = rotateSpeed * deltaTime * Math.PI;
64
65
  controls._moveCurr.x += rotX * scale;
@@ -70,8 +71,8 @@ var GamepadTrackballControls = class extends GamepadControls {
70
71
  controls._panStart.copy(controls._panEnd);
71
72
  return;
72
73
  }
73
- const panX = this.#applyDeadzone(gamepad.axes[axisPanX] ?? 0, deadzone);
74
- const panY = this.#applyDeadzone(gamepad.axes[axisPanY] ?? 0, deadzone);
74
+ const panX = applyGamepadDeadzone(gamepad.axes[axisPanX] ?? 0, deadzone);
75
+ const panY = applyGamepadDeadzone(gamepad.axes[axisPanY] ?? 0, deadzone);
75
76
  if (panX === 0 && panY === 0) return;
76
77
  const scale = panSpeed * deltaTime * this.#getInputDampingFactor();
77
78
  controls._panEnd.x += panX * scale;
@@ -83,8 +84,8 @@ var GamepadTrackballControls = class extends GamepadControls {
83
84
  controls._zoomStart.copy(controls._zoomEnd);
84
85
  return;
85
86
  }
86
- const triggerIn = gamepad.buttons[buttonZoomIn]?.value ?? 0;
87
- const triggerOut = gamepad.buttons[buttonZoomOut]?.value ?? 0;
87
+ const triggerIn = getGamepadButtonValue(gamepad, buttonZoomIn);
88
+ const triggerOut = getGamepadButtonValue(gamepad, buttonZoomOut);
88
89
  if (triggerIn <= deadzone && triggerOut <= deadzone) return;
89
90
  controls._zoomEnd.y += (triggerOut - triggerIn) * zoomSpeed * deltaTime * this.#getInputDampingFactor();
90
91
  }
@@ -96,15 +97,6 @@ var GamepadTrackballControls = class extends GamepadControls {
96
97
  const controls = this.#controls;
97
98
  return controls.staticMoving ? 1 : controls.dynamicDampingFactor;
98
99
  }
99
- /**
100
- * Returns `value` unchanged, or `0` if below the dead zone `threshold`.
101
- *
102
- * @param value - Raw axis or trigger value, typically in `[-1, 1]`.
103
- * @param threshold - Dead zone size; values below this magnitude are zeroed.
104
- */
105
- #applyDeadzone(value, threshold) {
106
- return Math.abs(value) < threshold ? 0 : value;
107
- }
108
100
  };
109
101
  //#endregion
110
102
  export { GamepadTrackballControls };
@@ -1,4 +1,5 @@
1
1
  import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
2
+ import { applyGamepadDeadzone, getGamepadButtonPressed } from "./utils.js";
2
3
  import { GamepadControls } from "./gamepad-controls.js";
3
4
  import { Matrix4, Quaternion, Vector2, Vector3 } from "three";
4
5
  //#region src/gamepad-transform-controls.ts
@@ -172,8 +173,8 @@ var GamepadTransformControls = class extends GamepadControls {
172
173
  this.#endTransform(true);
173
174
  return;
174
175
  }
175
- const transformX = this.#applyDeadzone(gamepad.axes[this.#options.axisTransformX] ?? 0, this.#options.deadzone);
176
- const transformY = this.#applyDeadzone(gamepad.axes[this.#options.axisTransformY] ?? 0, this.#options.deadzone);
176
+ const transformX = applyGamepadDeadzone(gamepad.axes[this.#options.axisTransformX] ?? 0, this.#options.deadzone);
177
+ const transformY = applyGamepadDeadzone(gamepad.axes[this.#options.axisTransformY] ?? 0, this.#options.deadzone);
177
178
  if (transformX === 0 && transformY === 0) {
178
179
  this.#endTransform(false);
179
180
  return;
@@ -605,7 +606,7 @@ var GamepadTransformControls = class extends GamepadControls {
605
606
  #getStartedButtons(gamepad) {
606
607
  const startedButtons = /* @__PURE__ */ new Set();
607
608
  for (let index = 0; index < gamepad.buttons.length; index += 1) {
608
- const pressed = gamepad.buttons[index]?.pressed ?? false;
609
+ const pressed = getGamepadButtonPressed(gamepad, index);
609
610
  const wasPressed = this.#pressedButtons.has(index);
610
611
  if (pressed) {
611
612
  this.#pressedButtons.add(index);
@@ -620,15 +621,6 @@ var GamepadTransformControls = class extends GamepadControls {
620
621
  #isOrthographicCamera(camera) {
621
622
  return camera.isOrthographicCamera === true;
622
623
  }
623
- /**
624
- * Returns `value` unchanged, or `0` if below the dead zone `threshold`.
625
- *
626
- * @param value - Raw axis value, typically in `[-1, 1]`.
627
- * @param threshold - Dead zone size; values below this magnitude are zeroed.
628
- */
629
- #applyDeadzone(value, threshold) {
630
- return Math.abs(value) < threshold ? 0 : value;
631
- }
632
624
  };
633
625
  //#endregion
634
626
  export { GamepadTransformControls };
package/dist/utils.js ADDED
@@ -0,0 +1,60 @@
1
+ //#region src/utils.ts
2
+ /**
3
+ * Returns `value` unchanged, or `0` if below the dead zone `threshold`.
4
+ *
5
+ * @param value - Raw axis or trigger value, typically in `[-1, 1]`.
6
+ * @param threshold - Dead zone size; values below this magnitude are zeroed.
7
+ * @returns The original value when outside the dead zone, otherwise `0`.
8
+ */
9
+ const applyGamepadDeadzone = (value, threshold) => {
10
+ return Math.abs(value) < threshold ? 0 : value;
11
+ };
12
+ /**
13
+ * Returns whether a gamepad button is currently pressed.
14
+ *
15
+ * Missing buttons are treated as not pressed.
16
+ *
17
+ * @param gamepad - Gamepad snapshot to read from.
18
+ * @param button - Button index to inspect.
19
+ * @returns `true` when the button exists and is pressed, otherwise `false`.
20
+ */
21
+ const getGamepadButtonPressed = (gamepad, button) => {
22
+ return gamepad.buttons[button]?.pressed ?? false;
23
+ };
24
+ /**
25
+ * Returns the analog value for a gamepad button.
26
+ *
27
+ * Some digital buttons may report `pressed` without a meaningful non-zero
28
+ * `value`, so pressed buttons fall back to `1`.
29
+ *
30
+ * @param gamepad - Gamepad snapshot to read from.
31
+ * @param button - Button index to inspect.
32
+ * @returns The button value, `1` for pressed digital buttons, or `0` when unavailable.
33
+ */
34
+ const getGamepadButtonValue = (gamepad, button) => {
35
+ const gamepadButton = gamepad.buttons[button];
36
+ if (gamepadButton === void 0) return 0;
37
+ if (gamepadButton.value !== 0) return gamepadButton.value;
38
+ return gamepadButton.pressed ? 1 : 0;
39
+ };
40
+ /**
41
+ * Returns a fresh connected gamepad snapshot by index.
42
+ *
43
+ * @param index - Gamepad index to look up.
44
+ * @returns The connected gamepad snapshot at the index, or `null` when unavailable.
45
+ */
46
+ const getGamepadByIndex = (index) => {
47
+ const gamepad = navigator.getGamepads()[index] ?? null;
48
+ return gamepad?.connected === true ? gamepad : null;
49
+ };
50
+ /**
51
+ * Returns the first currently connected gamepad snapshot.
52
+ *
53
+ * @returns The first connected gamepad snapshot, or `null` when none are connected.
54
+ */
55
+ const getFirstConnectedGamepad = () => {
56
+ for (const gamepad of navigator.getGamepads()) if (gamepad?.connected === true) return gamepad;
57
+ return null;
58
+ };
59
+ //#endregion
60
+ export { applyGamepadDeadzone, getFirstConnectedGamepad, getGamepadButtonPressed, getGamepadButtonValue, getGamepadByIndex };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "three-gamepad-controls",
3
3
  "description": "Gamepad support for Three.js controls.",
4
- "version": "0.10.4",
4
+ "version": "0.10.5",
5
5
  "homepage": "https://github.com/luckasnix/three-gamepad-controls#readme",
6
6
  "author": {
7
7
  "name": "Kasnix",
@@ -46,11 +46,11 @@
46
46
  }
47
47
  },
48
48
  "devDependencies": {
49
- "@biomejs/biome": "2.4.16",
49
+ "@biomejs/biome": "2.5.0",
50
50
  "@commitlint/cli": "21.0.2",
51
51
  "@commitlint/config-conventional": "21.0.2",
52
52
  "@commitlint/types": "21.0.1",
53
- "@types/node": "24.12.4",
53
+ "@types/node": "24.13.2",
54
54
  "@types/three": "0.184.1",
55
55
  "husky": "9.1.7",
56
56
  "three": "0.184.0",