three-gamepad-controls 0.11.0 → 0.12.1
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/README.md +25 -6
- package/dist/gamepad-arcball-controls.d.ts +1 -2
- package/dist/gamepad-arcball-controls.js +11 -17
- package/dist/gamepad-controls.d.ts +17 -13
- package/dist/gamepad-controls.js +42 -56
- package/dist/gamepad-drag-controls.d.ts +1 -2
- package/dist/gamepad-drag-controls.js +9 -14
- package/dist/gamepad-first-person-controls.d.ts +1 -2
- package/dist/gamepad-first-person-controls.js +13 -15
- package/dist/gamepad-fly-controls.d.ts +1 -2
- package/dist/gamepad-fly-controls.js +9 -10
- package/dist/gamepad-input.js +41 -1
- package/dist/gamepad-orbit-controls.d.ts +1 -2
- package/dist/gamepad-orbit-controls.js +8 -9
- package/dist/gamepad-pointer-lock-controls.d.ts +1 -2
- package/dist/gamepad-pointer-lock-controls.js +6 -7
- package/dist/gamepad-trackball-controls.d.ts +1 -2
- package/dist/gamepad-trackball-controls.js +16 -18
- package/dist/gamepad-transform-controls.d.ts +1 -2
- package/dist/gamepad-transform-controls.js +21 -21
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/utils.js +0 -41
package/README.md
CHANGED
|
@@ -2,42 +2,61 @@
|
|
|
2
2
|
|
|
3
3
|
Gamepad support for [Three.js](https://threejs.org) controls, built on top of [Web Gamepad API](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API).
|
|
4
4
|
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="./assets/architecture-diagram.webp" width="800" alt="Architecture diagram" />
|
|
9
|
+
</p>
|
|
10
|
+
|
|
5
11
|
## 📦 Installation
|
|
6
12
|
|
|
7
13
|
npm:
|
|
8
14
|
|
|
9
15
|
```bash
|
|
10
|
-
npm i three-gamepad-controls
|
|
16
|
+
npm i three three-gamepad-controls
|
|
17
|
+
npm i -D @types/three # optional: for TypeScript projects
|
|
11
18
|
```
|
|
12
19
|
|
|
13
20
|
pnpm:
|
|
14
21
|
|
|
15
22
|
```bash
|
|
16
|
-
pnpm add three-gamepad-controls
|
|
23
|
+
pnpm add three three-gamepad-controls
|
|
24
|
+
pnpm add -D @types/three # optional: for TypeScript projects
|
|
17
25
|
```
|
|
18
26
|
|
|
19
27
|
Yarn:
|
|
20
28
|
|
|
21
29
|
```bash
|
|
22
|
-
yarn add three-gamepad-controls
|
|
30
|
+
yarn add three three-gamepad-controls
|
|
31
|
+
yarn add -D @types/three # optional: for TypeScript projects
|
|
23
32
|
```
|
|
24
33
|
|
|
25
34
|
Deno:
|
|
26
35
|
|
|
36
|
+
When using `deno.json`, Deno stores dependencies in `imports` and does not separate `devDependencies`; `-D` only applies when writing to `package.json`.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# deno.json
|
|
40
|
+
deno add three @types/three three-gamepad-controls
|
|
41
|
+
```
|
|
42
|
+
|
|
27
43
|
```bash
|
|
28
|
-
|
|
44
|
+
# package.json
|
|
45
|
+
deno add --package-json three three-gamepad-controls
|
|
46
|
+
deno add --package-json -D @types/three # optional: for TypeScript projects
|
|
29
47
|
```
|
|
30
48
|
|
|
31
49
|
Bun:
|
|
32
50
|
|
|
33
51
|
```bash
|
|
34
|
-
bun add three-gamepad-controls
|
|
52
|
+
bun add three three-gamepad-controls
|
|
53
|
+
bun add -d @types/three # optional: for TypeScript projects
|
|
35
54
|
```
|
|
36
55
|
|
|
37
56
|
## 📖 Documentation
|
|
38
57
|
|
|
39
58
|
- [Core](./docs/core.md) — The fundamental building blocks.
|
|
40
|
-
- [GamepadInput](./docs/gamepad-input.md) -
|
|
59
|
+
- [GamepadInput](./docs/gamepad-input.md) - Low-level reader for gamepad buttons, axes, sticks, and transitions.
|
|
41
60
|
- [GamepadControls](./docs/gamepad-controls.md) — Abstract base class for custom gamepad controls.
|
|
42
61
|
- [GamepadArcballControls](./docs/gamepad-arcball-controls.md) - Gamepad support for `ArcballControls`.
|
|
43
62
|
- [GamepadDragControls](./docs/gamepad-drag-controls.md) - Gamepad support for `DragControls`.
|
|
@@ -99,9 +99,8 @@ declare class GamepadArcballControls extends GamepadControls {
|
|
|
99
99
|
* z-rotation, and center focus.
|
|
100
100
|
*
|
|
101
101
|
* @param deltaTime - Seconds since the last frame.
|
|
102
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
103
102
|
*/
|
|
104
|
-
protected onUpdate(deltaTime: number
|
|
103
|
+
protected onUpdate(deltaTime: number): void;
|
|
105
104
|
}
|
|
106
105
|
//#endregion
|
|
107
106
|
export { GamepadArcballControls, GamepadArcballControlsOptions };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue } from "./utils.js";
|
|
4
3
|
import { Vector2, Vector3 } from "three";
|
|
5
4
|
//#region src/gamepad-arcball-controls.ts
|
|
6
5
|
/**
|
|
@@ -40,7 +39,6 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
40
39
|
#cameraForward;
|
|
41
40
|
#cameraRight;
|
|
42
41
|
#previousUp;
|
|
43
|
-
#focusButtonPressed = false;
|
|
44
42
|
#wasInteracting = false;
|
|
45
43
|
/**
|
|
46
44
|
* @param controls - A Three.js `ArcballControls` instance.
|
|
@@ -67,22 +65,22 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
67
65
|
* z-rotation, and center focus.
|
|
68
66
|
*
|
|
69
67
|
* @param deltaTime - Seconds since the last frame.
|
|
70
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
71
68
|
*/
|
|
72
|
-
onUpdate(deltaTime
|
|
69
|
+
onUpdate(deltaTime) {
|
|
73
70
|
const controls = this.#controls;
|
|
74
71
|
const { rotateSpeed, panSpeed, zoomSpeed, zRotateSpeed, deadzone, axisRotateX, axisRotateY, axisPanX, axisPanY, buttonZoomIn, buttonZoomOut, buttonZRotateLeft, buttonZRotateRight, buttonFocus } = this.#options;
|
|
75
|
-
const
|
|
72
|
+
const input = this.gamepadInput;
|
|
73
|
+
const focusPoint = this.#consumeFocusPoint(buttonFocus);
|
|
76
74
|
if (!controls.enabled) {
|
|
77
75
|
this.#endInteraction();
|
|
78
76
|
return;
|
|
79
77
|
}
|
|
80
|
-
const rotateX = controls.enableRotate ?
|
|
81
|
-
const rotateY = controls.enableRotate ?
|
|
82
|
-
const panX = controls.enablePan ?
|
|
83
|
-
const panY = controls.enablePan ?
|
|
84
|
-
const zoom = controls.enableZoom ?
|
|
85
|
-
const zRotation = controls.enableRotate ?
|
|
78
|
+
const rotateX = controls.enableRotate ? input.axis(axisRotateX, { deadzone }) : 0;
|
|
79
|
+
const rotateY = controls.enableRotate ? input.axis(axisRotateY, { deadzone }) : 0;
|
|
80
|
+
const panX = controls.enablePan ? input.axis(axisPanX, { deadzone }) : 0;
|
|
81
|
+
const panY = controls.enablePan ? input.axis(axisPanY, { deadzone }) : 0;
|
|
82
|
+
const zoom = controls.enableZoom ? input.buttonValue(buttonZoomIn) - input.buttonValue(buttonZoomOut) : 0;
|
|
83
|
+
const zRotation = controls.enableRotate ? input.buttonValue(buttonZRotateLeft) - input.buttonValue(buttonZRotateRight) : 0;
|
|
86
84
|
const activeInput = rotateX !== 0 || rotateY !== 0 || panX !== 0 || panY !== 0 || Math.abs(zoom) > deadzone || Math.abs(zRotation) > deadzone;
|
|
87
85
|
if (!activeInput && focusPoint === null) {
|
|
88
86
|
this.#endInteraction();
|
|
@@ -228,16 +226,12 @@ var GamepadArcballControls = class extends GamepadControls {
|
|
|
228
226
|
/**
|
|
229
227
|
* Consumes a focus-button press and resolves the viewport center hit point.
|
|
230
228
|
*
|
|
231
|
-
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
232
229
|
* @param buttonFocus - Button index configured for focus.
|
|
233
230
|
* @returns The center hit point, or `null` when focus should not run.
|
|
234
231
|
*/
|
|
235
|
-
#consumeFocusPoint(
|
|
232
|
+
#consumeFocusPoint(buttonFocus) {
|
|
236
233
|
const controls = this.#controls;
|
|
237
|
-
|
|
238
|
-
const shouldFocus = focusPressed && !this.#focusButtonPressed;
|
|
239
|
-
this.#focusButtonPressed = focusPressed;
|
|
240
|
-
if (!shouldFocus || !controls.enabled || !controls.enablePan || !controls.enableFocus || controls.scene === null) return null;
|
|
234
|
+
if (!this.gamepadInput.wasPressed(buttonFocus) || !controls.enabled || !controls.enablePan || !controls.enableFocus || controls.scene === null) return null;
|
|
241
235
|
return controls.unprojectOnObj(this.#centerNdc, controls.object);
|
|
242
236
|
}
|
|
243
237
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GamepadInput } from "./gamepad-input.js";
|
|
1
2
|
import { EventDispatcher } from "three";
|
|
2
3
|
|
|
3
4
|
//#region src/gamepad-controls.d.ts
|
|
@@ -30,8 +31,8 @@ type GamepadControlsEventMap = {
|
|
|
30
31
|
/**
|
|
31
32
|
* Abstract base class for Three.js gamepad controls.
|
|
32
33
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
34
|
+
* Delegates gamepad connection lifecycle and input polling to {@link GamepadInput}
|
|
35
|
+
* so subclasses can focus on mapping input to the wrapped Three.js control.
|
|
35
36
|
*/
|
|
36
37
|
declare abstract class GamepadControls extends EventDispatcher<GamepadControlsEventMap> {
|
|
37
38
|
#private;
|
|
@@ -45,9 +46,15 @@ declare abstract class GamepadControls extends EventDispatcher<GamepadControlsEv
|
|
|
45
46
|
*/
|
|
46
47
|
gamepad: Gamepad | null;
|
|
47
48
|
/**
|
|
48
|
-
* Creates the base
|
|
49
|
+
* Creates the base input reader and attaches lifecycle listeners.
|
|
49
50
|
*/
|
|
50
51
|
constructor();
|
|
52
|
+
/**
|
|
53
|
+
* Low-level gamepad input reader used by subclasses.
|
|
54
|
+
*
|
|
55
|
+
* @returns The shared input reader for the active gamepad.
|
|
56
|
+
*/
|
|
57
|
+
protected get gamepadInput(): GamepadInput;
|
|
51
58
|
/**
|
|
52
59
|
* Advances the controller by one frame. Call this inside your render loop.
|
|
53
60
|
*
|
|
@@ -62,25 +69,22 @@ declare abstract class GamepadControls extends EventDispatcher<GamepadControlsEv
|
|
|
62
69
|
* Called every frame when a gamepad is available and `enabled` is `true`.
|
|
63
70
|
*
|
|
64
71
|
* @param deltaTime - Seconds since the last frame.
|
|
65
|
-
* @param gamepad - A fresh snapshot of the currently active gamepad.
|
|
66
72
|
*/
|
|
67
|
-
protected abstract onUpdate(deltaTime: number
|
|
73
|
+
protected abstract onUpdate(deltaTime: number): void;
|
|
68
74
|
/**
|
|
69
|
-
* Called when
|
|
75
|
+
* Called when a gamepad becomes active through the shared input reader.
|
|
70
76
|
*
|
|
71
|
-
* The default
|
|
72
|
-
* Override to customize selection behavior.
|
|
77
|
+
* The default dispatches a `connected` event.
|
|
73
78
|
*
|
|
74
|
-
* @param gamepad - The gamepad that
|
|
79
|
+
* @param gamepad - The gamepad that became active.
|
|
75
80
|
*/
|
|
76
81
|
protected onGamepadConnected(gamepad: Gamepad): void;
|
|
77
82
|
/**
|
|
78
|
-
* Called when
|
|
83
|
+
* Called when the active gamepad disconnects through the shared input reader.
|
|
79
84
|
*
|
|
80
|
-
* The default
|
|
81
|
-
* disconnecting gamepad was the active one. Override to add custom cleanup.
|
|
85
|
+
* The default dispatches a `disconnected` event.
|
|
82
86
|
*
|
|
83
|
-
* @param gamepad - The gamepad that
|
|
87
|
+
* @param gamepad - The gamepad that was active before disconnection.
|
|
84
88
|
*/
|
|
85
89
|
protected onGamepadDisconnected(gamepad: Gamepad): void;
|
|
86
90
|
}
|
package/dist/gamepad-controls.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GamepadInput } from "./gamepad-input.js";
|
|
2
2
|
import { EventDispatcher } from "three";
|
|
3
3
|
//#region src/gamepad-controls.ts
|
|
4
4
|
/**
|
|
5
5
|
* Abstract base class for Three.js gamepad controls.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Delegates gamepad connection lifecycle and input polling to {@link GamepadInput}
|
|
8
|
+
* so subclasses can focus on mapping input to the wrapped Three.js control.
|
|
9
9
|
*/
|
|
10
10
|
var GamepadControls = class extends EventDispatcher {
|
|
11
11
|
/**
|
|
@@ -17,40 +17,50 @@ var GamepadControls = class extends EventDispatcher {
|
|
|
17
17
|
* The currently active gamepad, or `null` if no gamepad is connected.
|
|
18
18
|
*/
|
|
19
19
|
gamepad = null;
|
|
20
|
-
#
|
|
20
|
+
#gamepadInput;
|
|
21
21
|
/**
|
|
22
|
-
* Bound
|
|
22
|
+
* Bound input connection listener kept so it can be removed in {@link dispose}.
|
|
23
23
|
*/
|
|
24
24
|
#onGamepadConnected;
|
|
25
25
|
/**
|
|
26
|
-
* Bound
|
|
26
|
+
* Bound input disconnection listener kept so it can be removed in {@link dispose}.
|
|
27
27
|
*/
|
|
28
28
|
#onGamepadDisconnected;
|
|
29
29
|
/**
|
|
30
|
-
* Creates the base
|
|
30
|
+
* Creates the base input reader and attaches lifecycle listeners.
|
|
31
31
|
*/
|
|
32
32
|
constructor() {
|
|
33
33
|
super();
|
|
34
|
-
this.#
|
|
35
|
-
this.#onGamepadConnected = this.#
|
|
36
|
-
this.#onGamepadDisconnected = this.#
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
this.#gamepadInput = new GamepadInput();
|
|
35
|
+
this.#onGamepadConnected = this.#handleGamepadConnected.bind(this);
|
|
36
|
+
this.#onGamepadDisconnected = this.#handleGamepadDisconnected.bind(this);
|
|
37
|
+
this.#gamepadInput.addEventListener("connected", this.#onGamepadConnected);
|
|
38
|
+
this.#gamepadInput.addEventListener("disconnected", this.#onGamepadDisconnected);
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* Low-level gamepad input reader used by subclasses.
|
|
42
42
|
*
|
|
43
|
-
* @
|
|
43
|
+
* @returns The shared input reader for the active gamepad.
|
|
44
44
|
*/
|
|
45
|
-
|
|
45
|
+
get gamepadInput() {
|
|
46
|
+
return this.#gamepadInput;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Forwards an input connection event to the overridable lifecycle hook.
|
|
50
|
+
*
|
|
51
|
+
* @param event - Input event containing the connected gamepad snapshot.
|
|
52
|
+
*/
|
|
53
|
+
#handleGamepadConnected(event) {
|
|
54
|
+
this.gamepad = this.#gamepadInput.gamepad;
|
|
46
55
|
this.onGamepadConnected(event.gamepad);
|
|
47
56
|
}
|
|
48
57
|
/**
|
|
49
|
-
* Forwards
|
|
58
|
+
* Forwards an input disconnection event to the overridable lifecycle hook.
|
|
50
59
|
*
|
|
51
|
-
* @param event -
|
|
60
|
+
* @param event - Input event containing the disconnected gamepad snapshot.
|
|
52
61
|
*/
|
|
53
|
-
#
|
|
62
|
+
#handleGamepadDisconnected(event) {
|
|
63
|
+
this.gamepad = this.#gamepadInput.gamepad;
|
|
54
64
|
this.onGamepadDisconnected(event.gamepad);
|
|
55
65
|
}
|
|
56
66
|
/**
|
|
@@ -60,69 +70,45 @@ var GamepadControls = class extends EventDispatcher {
|
|
|
60
70
|
*/
|
|
61
71
|
update(deltaTime) {
|
|
62
72
|
if (!this.enabled) return;
|
|
63
|
-
this.#
|
|
64
|
-
|
|
65
|
-
if (connected !== null) {
|
|
66
|
-
this.#manager.activeGamepad = this.gamepad;
|
|
67
|
-
this.onGamepadConnected(connected);
|
|
68
|
-
this.#manager.activeGamepad = this.gamepad;
|
|
69
|
-
} else if (disconnected !== null) {
|
|
70
|
-
this.#manager.activeGamepad = this.gamepad;
|
|
71
|
-
this.onGamepadDisconnected(disconnected);
|
|
72
|
-
this.#manager.activeGamepad = this.gamepad;
|
|
73
|
-
return;
|
|
74
|
-
} else {
|
|
75
|
-
this.gamepad = gamepad;
|
|
76
|
-
this.#manager.activeGamepad = this.gamepad;
|
|
77
|
-
}
|
|
73
|
+
this.#gamepadInput.update();
|
|
74
|
+
this.gamepad = this.#gamepadInput.gamepad;
|
|
78
75
|
if (this.gamepad === null) return;
|
|
79
|
-
this.onUpdate(deltaTime
|
|
76
|
+
this.onUpdate(deltaTime);
|
|
80
77
|
}
|
|
81
78
|
/**
|
|
82
79
|
* Removes all event listeners attached by this controller. Call when no longer needed.
|
|
83
80
|
*/
|
|
84
81
|
dispose() {
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
this.#gamepadInput.removeEventListener("connected", this.#onGamepadConnected);
|
|
83
|
+
this.#gamepadInput.removeEventListener("disconnected", this.#onGamepadDisconnected);
|
|
84
|
+
this.#gamepadInput.dispose();
|
|
87
85
|
this.gamepad = null;
|
|
88
|
-
this.#manager.activeGamepad = null;
|
|
89
86
|
this.enabled = false;
|
|
90
87
|
}
|
|
91
88
|
/**
|
|
92
|
-
* Called when
|
|
89
|
+
* Called when a gamepad becomes active through the shared input reader.
|
|
93
90
|
*
|
|
94
|
-
* The default
|
|
95
|
-
* Override to customize selection behavior.
|
|
91
|
+
* The default dispatches a `connected` event.
|
|
96
92
|
*
|
|
97
|
-
* @param gamepad - The gamepad that
|
|
93
|
+
* @param gamepad - The gamepad that became active.
|
|
98
94
|
*/
|
|
99
95
|
onGamepadConnected(gamepad) {
|
|
100
|
-
this.#manager.activeGamepad = this.gamepad;
|
|
101
|
-
if (!this.#manager.connect(gamepad)) return;
|
|
102
|
-
const connectedGamepad = this.#manager.activeGamepad;
|
|
103
|
-
if (connectedGamepad === null) return;
|
|
104
|
-
this.gamepad = connectedGamepad;
|
|
105
96
|
this.dispatchEvent({
|
|
106
97
|
type: "connected",
|
|
107
|
-
gamepad
|
|
98
|
+
gamepad
|
|
108
99
|
});
|
|
109
100
|
}
|
|
110
101
|
/**
|
|
111
|
-
* Called when
|
|
102
|
+
* Called when the active gamepad disconnects through the shared input reader.
|
|
112
103
|
*
|
|
113
|
-
* The default
|
|
114
|
-
* disconnecting gamepad was the active one. Override to add custom cleanup.
|
|
104
|
+
* The default dispatches a `disconnected` event.
|
|
115
105
|
*
|
|
116
|
-
* @param gamepad - The gamepad that
|
|
106
|
+
* @param gamepad - The gamepad that was active before disconnection.
|
|
117
107
|
*/
|
|
118
108
|
onGamepadDisconnected(gamepad) {
|
|
119
|
-
this.#manager.activeGamepad = this.gamepad;
|
|
120
|
-
const disconnectedGamepad = this.#manager.disconnect(gamepad);
|
|
121
|
-
if (disconnectedGamepad === null) return;
|
|
122
|
-
this.gamepad = this.#manager.activeGamepad;
|
|
123
109
|
this.dispatchEvent({
|
|
124
110
|
type: "disconnected",
|
|
125
|
-
gamepad
|
|
111
|
+
gamepad
|
|
126
112
|
});
|
|
127
113
|
}
|
|
128
114
|
};
|
|
@@ -68,9 +68,8 @@ declare class GamepadDragControls extends GamepadControls {
|
|
|
68
68
|
* and rotate behavior.
|
|
69
69
|
*
|
|
70
70
|
* @param deltaTime - Seconds since the last frame.
|
|
71
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
72
71
|
*/
|
|
73
|
-
protected onUpdate(deltaTime: number
|
|
72
|
+
protected onUpdate(deltaTime: number): void;
|
|
74
73
|
/**
|
|
75
74
|
* Releases any selected object and removes hover state before disposing the
|
|
76
75
|
* gamepad lifecycle listeners.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone, getGamepadButtonPressed } from "./utils.js";
|
|
4
3
|
import { Matrix4, Vector2, Vector3 } from "three";
|
|
5
4
|
//#region src/gamepad-drag-controls.ts
|
|
6
5
|
/**
|
|
@@ -38,7 +37,6 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
38
37
|
#viewSize;
|
|
39
38
|
#hovered = null;
|
|
40
39
|
#selected = null;
|
|
41
|
-
#selectButtonPressed = false;
|
|
42
40
|
/**
|
|
43
41
|
* @param controls - A Three.js `DragControls` instance.
|
|
44
42
|
* @param options - Optional overrides for the default behavior.
|
|
@@ -68,13 +66,10 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
68
66
|
* and rotate behavior.
|
|
69
67
|
*
|
|
70
68
|
* @param deltaTime - Seconds since the last frame.
|
|
71
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
72
69
|
*/
|
|
73
|
-
onUpdate(deltaTime
|
|
70
|
+
onUpdate(deltaTime) {
|
|
74
71
|
const controls = this.#controls;
|
|
75
|
-
const
|
|
76
|
-
const selectStarted = selectPressed && !this.#selectButtonPressed;
|
|
77
|
-
this.#selectButtonPressed = selectPressed;
|
|
72
|
+
const selectStarted = this.gamepadInput.wasPressed(this.#options.buttonSelect);
|
|
78
73
|
if (!controls.enabled) {
|
|
79
74
|
this.#releaseSelected();
|
|
80
75
|
this.#clearHover();
|
|
@@ -85,7 +80,7 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
85
80
|
this.#releaseSelected();
|
|
86
81
|
return;
|
|
87
82
|
}
|
|
88
|
-
this.#updateSelected(deltaTime
|
|
83
|
+
this.#updateSelected(deltaTime);
|
|
89
84
|
return;
|
|
90
85
|
}
|
|
91
86
|
const hit = this.#intersectCenter();
|
|
@@ -115,16 +110,16 @@ var GamepadDragControls = class extends GamepadControls {
|
|
|
115
110
|
* Updates the selected object from gamepad drag and rotation input.
|
|
116
111
|
*
|
|
117
112
|
* @param deltaTime - Seconds since the last frame.
|
|
118
|
-
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
119
113
|
*/
|
|
120
|
-
#updateSelected(deltaTime
|
|
114
|
+
#updateSelected(deltaTime) {
|
|
121
115
|
const selected = this.#selected;
|
|
122
116
|
if (selected === null) return;
|
|
123
117
|
const { dragSpeed, rotateSpeed, deadzone, axisDragX, axisDragY, axisRotateX, axisRotateY } = this.#options;
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
const
|
|
118
|
+
const input = this.gamepadInput;
|
|
119
|
+
const dragX = input.axis(axisDragX, { deadzone });
|
|
120
|
+
const dragY = input.axis(axisDragY, { deadzone });
|
|
121
|
+
const rotateX = input.axis(axisRotateX, { deadzone });
|
|
122
|
+
const rotateY = input.axis(axisRotateY, { deadzone });
|
|
128
123
|
const dragged = this.#applyDrag(deltaTime, dragX, dragY, dragSpeed);
|
|
129
124
|
const rotated = this.#applyRotation(deltaTime, rotateX, rotateY, rotateSpeed);
|
|
130
125
|
if (dragged || rotated) this.#controls.dispatchEvent({
|
|
@@ -73,9 +73,8 @@ declare class GamepadFirstPersonControls extends GamepadControls {
|
|
|
73
73
|
* Maps the current gamepad state to `FirstPersonControls` translation and look.
|
|
74
74
|
*
|
|
75
75
|
* @param deltaTime - Seconds since the last frame.
|
|
76
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
77
76
|
*/
|
|
78
|
-
protected onUpdate(deltaTime: number
|
|
77
|
+
protected onUpdate(deltaTime: number): void;
|
|
79
78
|
}
|
|
80
79
|
//#endregion
|
|
81
80
|
export { GamepadFirstPersonControls, GamepadFirstPersonControlsOptions };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
|
|
4
3
|
import { MathUtils, Spherical, Vector3 } from "three";
|
|
5
4
|
//#region src/gamepad-first-person-controls.ts
|
|
6
5
|
/**
|
|
@@ -51,18 +50,16 @@ var GamepadFirstPersonControls = class extends GamepadControls {
|
|
|
51
50
|
* Maps the current gamepad state to `FirstPersonControls` translation and look.
|
|
52
51
|
*
|
|
53
52
|
* @param deltaTime - Seconds since the last frame.
|
|
54
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
55
53
|
*/
|
|
56
|
-
onUpdate(deltaTime
|
|
54
|
+
onUpdate(deltaTime) {
|
|
57
55
|
const { moveSpeed, lookSpeed, deadzone, axisMoveForward, axisMoveRight, axisLookX, axisLookY, buttonMoveUp, buttonMoveDown } = this.#options;
|
|
58
|
-
this.#applyMovement(deltaTime,
|
|
59
|
-
this.#applyLook(deltaTime,
|
|
56
|
+
this.#applyMovement(deltaTime, moveSpeed, deadzone, axisMoveForward, axisMoveRight, buttonMoveUp, buttonMoveDown);
|
|
57
|
+
this.#applyLook(deltaTime, lookSpeed, deadzone, axisLookX, axisLookY);
|
|
60
58
|
}
|
|
61
59
|
/**
|
|
62
60
|
* Applies local translation input to FirstPersonControls' object.
|
|
63
61
|
*
|
|
64
62
|
* @param deltaTime - Seconds since the last frame.
|
|
65
|
-
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
66
63
|
* @param moveSpeed - User-configured movement speed multiplier.
|
|
67
64
|
* @param deadzone - Axis and trigger dead zone threshold.
|
|
68
65
|
* @param axisMoveForward - Axis index for forward and backward movement.
|
|
@@ -70,10 +67,11 @@ var GamepadFirstPersonControls = class extends GamepadControls {
|
|
|
70
67
|
* @param buttonMoveUp - Button index for upward movement.
|
|
71
68
|
* @param buttonMoveDown - Button index for downward movement.
|
|
72
69
|
*/
|
|
73
|
-
#applyMovement(deltaTime,
|
|
70
|
+
#applyMovement(deltaTime, moveSpeed, deadzone, axisMoveForward, axisMoveRight, buttonMoveUp, buttonMoveDown) {
|
|
74
71
|
const controls = this.#controls;
|
|
72
|
+
const input = this.gamepadInput;
|
|
75
73
|
const moveMult = deltaTime * controls.movementSpeed * moveSpeed;
|
|
76
|
-
const forward =
|
|
74
|
+
const forward = input.axis(axisMoveForward, { deadzone });
|
|
77
75
|
if (forward !== 0) {
|
|
78
76
|
let distance = forward * moveMult;
|
|
79
77
|
if (forward < 0 && controls.heightSpeed) {
|
|
@@ -82,10 +80,10 @@ var GamepadFirstPersonControls = class extends GamepadControls {
|
|
|
82
80
|
}
|
|
83
81
|
controls.object.translateZ(distance);
|
|
84
82
|
}
|
|
85
|
-
const strafe =
|
|
83
|
+
const strafe = input.axis(axisMoveRight, { deadzone });
|
|
86
84
|
if (strafe !== 0) controls.object.translateX(strafe * moveMult);
|
|
87
|
-
const up =
|
|
88
|
-
const down =
|
|
85
|
+
const up = input.buttonValue(buttonMoveUp);
|
|
86
|
+
const down = input.buttonValue(buttonMoveDown);
|
|
89
87
|
if (up > deadzone) controls.object.translateY(up * moveMult);
|
|
90
88
|
if (down > deadzone) controls.object.translateY(-down * moveMult);
|
|
91
89
|
}
|
|
@@ -93,15 +91,15 @@ var GamepadFirstPersonControls = class extends GamepadControls {
|
|
|
93
91
|
* Applies camera look input while keeping FirstPersonControls state in sync.
|
|
94
92
|
*
|
|
95
93
|
* @param deltaTime - Seconds since the last frame.
|
|
96
|
-
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
97
94
|
* @param lookSpeed - User-configured look speed multiplier.
|
|
98
95
|
* @param deadzone - Axis dead zone threshold.
|
|
99
96
|
* @param axisLookX - Axis index for yaw input.
|
|
100
97
|
* @param axisLookY - Axis index for pitch input.
|
|
101
98
|
*/
|
|
102
|
-
#applyLook(deltaTime,
|
|
103
|
-
const
|
|
104
|
-
const
|
|
99
|
+
#applyLook(deltaTime, lookSpeed, deadzone, axisLookX, axisLookY) {
|
|
100
|
+
const input = this.gamepadInput;
|
|
101
|
+
const lookX = input.axis(axisLookX, { deadzone });
|
|
102
|
+
const lookY = input.axis(axisLookY, { deadzone });
|
|
105
103
|
if (lookX === 0 && lookY === 0) return;
|
|
106
104
|
const controls = this.#controls;
|
|
107
105
|
const actualLookSpeed = controls.lookSpeed * lookSpeed * deltaTime * LOOK_SPEED_SCALE;
|
|
@@ -83,9 +83,8 @@ declare class GamepadFlyControls extends GamepadControls {
|
|
|
83
83
|
* Maps the current gamepad state to `FlyControls` translation and rotation.
|
|
84
84
|
*
|
|
85
85
|
* @param deltaTime - Seconds since the last frame.
|
|
86
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
87
86
|
*/
|
|
88
|
-
protected onUpdate(deltaTime: number
|
|
87
|
+
protected onUpdate(deltaTime: number): void;
|
|
89
88
|
}
|
|
90
89
|
//#endregion
|
|
91
90
|
export { GamepadFlyControls, GamepadFlyControlsOptions };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue } from "./utils.js";
|
|
4
3
|
import { Quaternion } from "three";
|
|
5
4
|
//#region src/gamepad-fly-controls.ts
|
|
6
5
|
/**
|
|
@@ -48,23 +47,23 @@ var GamepadFlyControls = class extends GamepadControls {
|
|
|
48
47
|
* Maps the current gamepad state to `FlyControls` translation and rotation.
|
|
49
48
|
*
|
|
50
49
|
* @param deltaTime - Seconds since the last frame.
|
|
51
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
52
50
|
*/
|
|
53
|
-
onUpdate(deltaTime
|
|
51
|
+
onUpdate(deltaTime) {
|
|
54
52
|
const { moveSpeed, rotateSpeed, deadzone, axisMoveForward, axisMoveRight, axisLookX, axisLookY, buttonRollLeft, buttonRollRight, buttonMoveUp, buttonMoveDown } = this.#options;
|
|
53
|
+
const input = this.gamepadInput;
|
|
55
54
|
const moveMult = deltaTime * this.#controls.movementSpeed * moveSpeed;
|
|
56
|
-
const fwd =
|
|
55
|
+
const fwd = input.axis(axisMoveForward, { deadzone });
|
|
57
56
|
if (fwd !== 0) this.#controls.object.translateZ(fwd * moveMult);
|
|
58
|
-
const strafe =
|
|
57
|
+
const strafe = input.axis(axisMoveRight, { deadzone });
|
|
59
58
|
if (strafe !== 0) this.#controls.object.translateX(strafe * moveMult);
|
|
60
|
-
const up =
|
|
61
|
-
const down =
|
|
59
|
+
const up = input.buttonValue(buttonMoveUp);
|
|
60
|
+
const down = input.buttonValue(buttonMoveDown);
|
|
62
61
|
if (up > deadzone) this.#controls.object.translateY(up * moveMult);
|
|
63
62
|
if (down > deadzone) this.#controls.object.translateY(-down * moveMult);
|
|
64
63
|
const rotMult = deltaTime * this.#controls.rollSpeed * rotateSpeed;
|
|
65
|
-
const pitch = -
|
|
66
|
-
const yaw = -
|
|
67
|
-
const roll = (
|
|
64
|
+
const pitch = -input.axis(axisLookY, { deadzone });
|
|
65
|
+
const yaw = -input.axis(axisLookX, { deadzone });
|
|
66
|
+
const roll = (input.isPressed(buttonRollLeft) ? 1 : 0) - (input.isPressed(buttonRollRight) ? 1 : 0);
|
|
68
67
|
if (pitch !== 0 || yaw !== 0 || roll !== 0) {
|
|
69
68
|
this.#tmpQuaternion.set(pitch * rotMult, yaw * rotMult, roll * rotMult, 1).normalize();
|
|
70
69
|
this.#controls.object.quaternion.multiply(this.#tmpQuaternion);
|
package/dist/gamepad-input.js
CHANGED
|
@@ -1,9 +1,49 @@
|
|
|
1
1
|
import { GamepadManager } from "./gamepad-manager.js";
|
|
2
|
-
import { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue } from "./utils.js";
|
|
3
2
|
import { EventDispatcher } from "three";
|
|
4
3
|
//#region src/gamepad-input.ts
|
|
5
4
|
const DEFAULT_GAMEPAD_INPUT_OPTIONS = { deadzone: .1 };
|
|
6
5
|
/**
|
|
6
|
+
* Returns `value` unchanged, or `0` if below the dead zone `threshold`.
|
|
7
|
+
*
|
|
8
|
+
* Kept private to this module because `GamepadInput` is the only public API
|
|
9
|
+
* that currently exposes processed axis values.
|
|
10
|
+
*
|
|
11
|
+
* @param value - Raw axis or trigger value, typically in `[-1, 1]`.
|
|
12
|
+
* @param threshold - Dead zone size; values below this magnitude are zeroed.
|
|
13
|
+
* @returns The original value when outside the dead zone, otherwise `0`.
|
|
14
|
+
*/
|
|
15
|
+
const applyGamepadDeadzone = (value, threshold) => {
|
|
16
|
+
return Math.abs(value) < threshold ? 0 : value;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Returns whether a gamepad button is currently pressed.
|
|
20
|
+
*
|
|
21
|
+
* Missing buttons are treated as not pressed.
|
|
22
|
+
*
|
|
23
|
+
* @param gamepad - Gamepad snapshot to read from.
|
|
24
|
+
* @param button - Button index to inspect.
|
|
25
|
+
* @returns `true` when the button exists and is pressed, otherwise `false`.
|
|
26
|
+
*/
|
|
27
|
+
const getGamepadButtonPressed = (gamepad, button) => {
|
|
28
|
+
return gamepad.buttons[button]?.pressed ?? false;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Returns the analog value for a gamepad button.
|
|
32
|
+
*
|
|
33
|
+
* Some digital buttons may report `pressed` without a meaningful non-zero
|
|
34
|
+
* `value`, so pressed buttons fall back to `1`.
|
|
35
|
+
*
|
|
36
|
+
* @param gamepad - Gamepad snapshot to read from.
|
|
37
|
+
* @param button - Button index to inspect.
|
|
38
|
+
* @returns The button value, `1` for pressed digital buttons, or `0` when unavailable.
|
|
39
|
+
*/
|
|
40
|
+
const getGamepadButtonValue = (gamepad, button) => {
|
|
41
|
+
const gamepadButton = gamepad.buttons[button];
|
|
42
|
+
if (gamepadButton === void 0) return 0;
|
|
43
|
+
if (gamepadButton.value !== 0) return gamepadButton.value;
|
|
44
|
+
return gamepadButton.pressed ? 1 : 0;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
7
47
|
* Gamepad input state reader for gameplay, menus, and custom actions.
|
|
8
48
|
*
|
|
9
49
|
* Call {@link update} once per frame before reading button transitions or axes.
|
|
@@ -77,9 +77,8 @@ declare class GamepadOrbitControls extends GamepadControls {
|
|
|
77
77
|
* Maps the current gamepad state to `OrbitControls` rotation, pan, and dolly.
|
|
78
78
|
*
|
|
79
79
|
* @param deltaTime - Seconds since the last frame.
|
|
80
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
81
80
|
*/
|
|
82
|
-
protected onUpdate(deltaTime: number
|
|
81
|
+
protected onUpdate(deltaTime: number): void;
|
|
83
82
|
}
|
|
84
83
|
//#endregion
|
|
85
84
|
export { GamepadOrbitControls, GamepadOrbitControlsOptions };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
|
|
4
3
|
//#region src/gamepad-orbit-controls.ts
|
|
5
4
|
/**
|
|
6
5
|
* Default options merged in the constructor when no explicit configuration is provided.
|
|
@@ -43,19 +42,19 @@ var GamepadOrbitControls = class extends GamepadControls {
|
|
|
43
42
|
* Maps the current gamepad state to `OrbitControls` rotation, pan, and dolly.
|
|
44
43
|
*
|
|
45
44
|
* @param deltaTime - Seconds since the last frame.
|
|
46
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
47
45
|
*/
|
|
48
|
-
onUpdate(deltaTime
|
|
46
|
+
onUpdate(deltaTime) {
|
|
49
47
|
const { rotateSpeed, panSpeed, zoomSpeed, deadzone, axisRotateX, axisRotateY, axisPanX, axisPanY, buttonDollyIn, buttonDollyOut } = this.#options;
|
|
50
|
-
const
|
|
51
|
-
const
|
|
48
|
+
const input = this.gamepadInput;
|
|
49
|
+
const rotX = input.axis(axisRotateX, { deadzone });
|
|
50
|
+
const rotY = input.axis(axisRotateY, { deadzone });
|
|
52
51
|
if (rotX !== 0) this.#controls.rotateLeft(rotX * rotateSpeed * deltaTime * Math.PI);
|
|
53
52
|
if (rotY !== 0) this.#controls.rotateUp(rotY * rotateSpeed * deltaTime * Math.PI);
|
|
54
|
-
const panX =
|
|
55
|
-
const panY =
|
|
53
|
+
const panX = input.axis(axisPanX, { deadzone });
|
|
54
|
+
const panY = input.axis(axisPanY, { deadzone });
|
|
56
55
|
if (panX !== 0 || panY !== 0) this.#controls.pan(panX * panSpeed * deltaTime * 500, panY * panSpeed * deltaTime * 500);
|
|
57
|
-
const triggerIn =
|
|
58
|
-
const triggerOut =
|
|
56
|
+
const triggerIn = input.buttonValue(buttonDollyIn);
|
|
57
|
+
const triggerOut = input.buttonValue(buttonDollyOut);
|
|
59
58
|
if (triggerIn > deadzone) this.#controls.dollyIn(1 / (1 + zoomSpeed * triggerIn * deltaTime));
|
|
60
59
|
if (triggerOut > deadzone) this.#controls.dollyOut(1 / (1 + zoomSpeed * triggerOut * deltaTime));
|
|
61
60
|
}
|
|
@@ -63,9 +63,8 @@ declare class GamepadPointerLockControls extends GamepadControls {
|
|
|
63
63
|
* Maps the current gamepad state to `PointerLockControls` movement and look.
|
|
64
64
|
*
|
|
65
65
|
* @param deltaTime - Seconds since the last frame.
|
|
66
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
67
66
|
*/
|
|
68
|
-
protected onUpdate(deltaTime: number
|
|
67
|
+
protected onUpdate(deltaTime: number): void;
|
|
69
68
|
}
|
|
70
69
|
//#endregion
|
|
71
70
|
export { GamepadPointerLockControls, GamepadPointerLockControlsOptions };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone } from "./utils.js";
|
|
4
3
|
import { Euler } from "three";
|
|
5
4
|
//#region src/gamepad-pointer-lock-controls.ts
|
|
6
5
|
/**
|
|
@@ -44,16 +43,16 @@ var GamepadPointerLockControls = class extends GamepadControls {
|
|
|
44
43
|
* Maps the current gamepad state to `PointerLockControls` movement and look.
|
|
45
44
|
*
|
|
46
45
|
* @param deltaTime - Seconds since the last frame.
|
|
47
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
48
46
|
*/
|
|
49
|
-
onUpdate(deltaTime
|
|
47
|
+
onUpdate(deltaTime) {
|
|
50
48
|
const { moveSpeed, lookSpeed, deadzone, axisMoveForward, axisMoveRight, axisLookX, axisLookY } = this.#options;
|
|
51
|
-
const
|
|
52
|
-
const
|
|
49
|
+
const input = this.gamepadInput;
|
|
50
|
+
const fwd = input.axis(axisMoveForward, { deadzone });
|
|
51
|
+
const strafe = input.axis(axisMoveRight, { deadzone });
|
|
53
52
|
if (fwd !== 0) this.#controls.moveForward(-fwd * moveSpeed * deltaTime);
|
|
54
53
|
if (strafe !== 0) this.#controls.moveRight(strafe * moveSpeed * deltaTime);
|
|
55
|
-
const lookX =
|
|
56
|
-
const lookY =
|
|
54
|
+
const lookX = input.axis(axisLookX, { deadzone });
|
|
55
|
+
const lookY = input.axis(axisLookY, { deadzone });
|
|
57
56
|
if (lookX !== 0 || lookY !== 0) {
|
|
58
57
|
const camera = this.#controls.object;
|
|
59
58
|
const scale = lookSpeed * this.#controls.pointerSpeed * deltaTime * Math.PI;
|
|
@@ -77,9 +77,8 @@ declare class GamepadTrackballControls extends GamepadControls {
|
|
|
77
77
|
* Maps the current gamepad state to `TrackballControls` rotation, pan, and zoom.
|
|
78
78
|
*
|
|
79
79
|
* @param deltaTime - Seconds since the last frame.
|
|
80
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
81
80
|
*/
|
|
82
|
-
protected onUpdate(deltaTime: number
|
|
81
|
+
protected onUpdate(deltaTime: number): void;
|
|
83
82
|
}
|
|
84
83
|
//#endregion
|
|
85
84
|
export { GamepadTrackballControls, GamepadTrackballControlsOptions };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone, getGamepadButtonValue } from "./utils.js";
|
|
4
3
|
//#region src/gamepad-trackball-controls.ts
|
|
5
4
|
/**
|
|
6
5
|
* Default options merged in the constructor when no explicit configuration is provided.
|
|
@@ -43,33 +42,32 @@ var GamepadTrackballControls = class extends GamepadControls {
|
|
|
43
42
|
* Maps the current gamepad state to `TrackballControls` rotation, pan, and zoom.
|
|
44
43
|
*
|
|
45
44
|
* @param deltaTime - Seconds since the last frame.
|
|
46
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
47
45
|
*/
|
|
48
|
-
onUpdate(deltaTime
|
|
46
|
+
onUpdate(deltaTime) {
|
|
49
47
|
const { rotateSpeed, panSpeed, zoomSpeed, deadzone, axisRotateX, axisRotateY, axisPanX, axisPanY, buttonZoomIn, buttonZoomOut } = this.#options;
|
|
50
|
-
this.#queueRotation(deltaTime,
|
|
51
|
-
this.#queuePan(deltaTime,
|
|
52
|
-
this.#queueZoom(deltaTime,
|
|
48
|
+
this.#queueRotation(deltaTime, rotateSpeed, deadzone, axisRotateX, axisRotateY);
|
|
49
|
+
this.#queuePan(deltaTime, panSpeed, deadzone, axisPanX, axisPanY);
|
|
50
|
+
this.#queueZoom(deltaTime, zoomSpeed, deadzone, buttonZoomIn, buttonZoomOut);
|
|
53
51
|
}
|
|
54
52
|
/**
|
|
55
53
|
* Queues rotation input into TrackballControls' normalized move state.
|
|
56
54
|
*
|
|
57
55
|
* @param deltaTime - Seconds since the last frame.
|
|
58
|
-
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
59
56
|
* @param rotateSpeed - User-configured rotation speed multiplier.
|
|
60
57
|
* @param deadzone - Axis dead zone threshold.
|
|
61
58
|
* @param axisRotateX - Axis index for horizontal rotation.
|
|
62
59
|
* @param axisRotateY - Axis index for vertical rotation.
|
|
63
60
|
*/
|
|
64
|
-
#queueRotation(deltaTime,
|
|
61
|
+
#queueRotation(deltaTime, rotateSpeed, deadzone, axisRotateX, axisRotateY) {
|
|
65
62
|
const controls = this.#controls;
|
|
66
63
|
if (controls.noRotate) {
|
|
67
64
|
controls._movePrev.copy(controls._moveCurr);
|
|
68
65
|
controls._lastAngle = 0;
|
|
69
66
|
return;
|
|
70
67
|
}
|
|
71
|
-
const
|
|
72
|
-
const
|
|
68
|
+
const input = this.gamepadInput;
|
|
69
|
+
const rotX = input.axis(axisRotateX, { deadzone });
|
|
70
|
+
const rotY = input.axis(axisRotateY, { deadzone });
|
|
73
71
|
if (rotX === 0 && rotY === 0) return;
|
|
74
72
|
const scale = rotateSpeed * deltaTime * Math.PI;
|
|
75
73
|
controls._moveCurr.x += rotX * scale;
|
|
@@ -79,20 +77,20 @@ var GamepadTrackballControls = class extends GamepadControls {
|
|
|
79
77
|
* Queues pan input into TrackballControls' normalized pan state.
|
|
80
78
|
*
|
|
81
79
|
* @param deltaTime - Seconds since the last frame.
|
|
82
|
-
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
83
80
|
* @param panSpeed - User-configured pan speed multiplier.
|
|
84
81
|
* @param deadzone - Axis dead zone threshold.
|
|
85
82
|
* @param axisPanX - Axis index for horizontal panning.
|
|
86
83
|
* @param axisPanY - Axis index for vertical panning.
|
|
87
84
|
*/
|
|
88
|
-
#queuePan(deltaTime,
|
|
85
|
+
#queuePan(deltaTime, panSpeed, deadzone, axisPanX, axisPanY) {
|
|
89
86
|
const controls = this.#controls;
|
|
90
87
|
if (controls.noPan) {
|
|
91
88
|
controls._panStart.copy(controls._panEnd);
|
|
92
89
|
return;
|
|
93
90
|
}
|
|
94
|
-
const
|
|
95
|
-
const
|
|
91
|
+
const input = this.gamepadInput;
|
|
92
|
+
const panX = input.axis(axisPanX, { deadzone });
|
|
93
|
+
const panY = input.axis(axisPanY, { deadzone });
|
|
96
94
|
if (panX === 0 && panY === 0) return;
|
|
97
95
|
const scale = panSpeed * deltaTime * this.#getInputDampingFactor();
|
|
98
96
|
controls._panEnd.x += panX * scale;
|
|
@@ -102,20 +100,20 @@ var GamepadTrackballControls = class extends GamepadControls {
|
|
|
102
100
|
* Queues trigger zoom input into TrackballControls' normalized zoom state.
|
|
103
101
|
*
|
|
104
102
|
* @param deltaTime - Seconds since the last frame.
|
|
105
|
-
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
106
103
|
* @param zoomSpeed - User-configured zoom speed multiplier.
|
|
107
104
|
* @param deadzone - Trigger dead zone threshold.
|
|
108
105
|
* @param buttonZoomIn - Button index for zooming in.
|
|
109
106
|
* @param buttonZoomOut - Button index for zooming out.
|
|
110
107
|
*/
|
|
111
|
-
#queueZoom(deltaTime,
|
|
108
|
+
#queueZoom(deltaTime, zoomSpeed, deadzone, buttonZoomIn, buttonZoomOut) {
|
|
112
109
|
const controls = this.#controls;
|
|
113
110
|
if (controls.noZoom) {
|
|
114
111
|
controls._zoomStart.copy(controls._zoomEnd);
|
|
115
112
|
return;
|
|
116
113
|
}
|
|
117
|
-
const
|
|
118
|
-
const
|
|
114
|
+
const input = this.gamepadInput;
|
|
115
|
+
const triggerIn = input.buttonValue(buttonZoomIn);
|
|
116
|
+
const triggerOut = input.buttonValue(buttonZoomOut);
|
|
119
117
|
if (triggerIn <= deadzone && triggerOut <= deadzone) return;
|
|
120
118
|
controls._zoomEnd.y += (triggerOut - triggerIn) * zoomSpeed * deltaTime * this.#getInputDampingFactor();
|
|
121
119
|
}
|
|
@@ -114,9 +114,8 @@ declare class GamepadTransformControls extends GamepadControls {
|
|
|
114
114
|
* translate, rotate, scale, and reset behavior.
|
|
115
115
|
*
|
|
116
116
|
* @param deltaTime - Seconds since the last frame.
|
|
117
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
118
117
|
*/
|
|
119
|
-
protected onUpdate(deltaTime: number
|
|
118
|
+
protected onUpdate(deltaTime: number): void;
|
|
120
119
|
/**
|
|
121
120
|
* Ends any active transform before disposing the gamepad lifecycle listeners.
|
|
122
121
|
*/
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
2
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
|
-
import { applyGamepadDeadzone, getGamepadButtonPressed } from "./utils.js";
|
|
4
3
|
import { Matrix4, Quaternion, Vector2, Vector3 } from "three";
|
|
5
4
|
//#region src/gamepad-transform-controls.ts
|
|
6
5
|
/**
|
|
@@ -70,7 +69,6 @@ const PROJECTED_AXIS_EPSILON = .001;
|
|
|
70
69
|
var GamepadTransformControls = class extends GamepadControls {
|
|
71
70
|
#controls;
|
|
72
71
|
#options;
|
|
73
|
-
#pressedButtons;
|
|
74
72
|
#activeAxisByMode;
|
|
75
73
|
#viewSize;
|
|
76
74
|
#parentInverse;
|
|
@@ -117,7 +115,6 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
117
115
|
...DEFAULT_TRANSFORM_OPTIONS,
|
|
118
116
|
...options
|
|
119
117
|
};
|
|
120
|
-
this.#pressedButtons = /* @__PURE__ */ new Set();
|
|
121
118
|
this.#activeAxisByMode = {
|
|
122
119
|
translate: "X",
|
|
123
120
|
rotate: "X",
|
|
@@ -158,10 +155,9 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
158
155
|
* translate, rotate, scale, and reset behavior.
|
|
159
156
|
*
|
|
160
157
|
* @param deltaTime - Seconds since the last frame.
|
|
161
|
-
* @param gamepad - Fresh gamepad snapshot provided by the base class.
|
|
162
158
|
*/
|
|
163
|
-
onUpdate(deltaTime
|
|
164
|
-
const startedButtons = this.#getStartedButtons(
|
|
159
|
+
onUpdate(deltaTime) {
|
|
160
|
+
const startedButtons = this.#getStartedButtons();
|
|
165
161
|
this.#handleModeAndAxisButtons(startedButtons);
|
|
166
162
|
if (startedButtons.has(this.#options.buttonReset)) this.#resetActiveTransform();
|
|
167
163
|
const controls = this.#controls;
|
|
@@ -173,8 +169,8 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
173
169
|
this.#endTransform(true);
|
|
174
170
|
return;
|
|
175
171
|
}
|
|
176
|
-
const transformX =
|
|
177
|
-
const transformY =
|
|
172
|
+
const transformX = this.gamepadInput.axis(this.#options.axisTransformX, { deadzone: this.#options.deadzone });
|
|
173
|
+
const transformY = this.gamepadInput.axis(this.#options.axisTransformY, { deadzone: this.#options.deadzone });
|
|
178
174
|
if (transformX === 0 && transformY === 0) {
|
|
179
175
|
this.#endTransform(false);
|
|
180
176
|
return;
|
|
@@ -190,7 +186,6 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
190
186
|
*/
|
|
191
187
|
dispose() {
|
|
192
188
|
this.#endTransform(true);
|
|
193
|
-
this.#pressedButtons.clear();
|
|
194
189
|
super.dispose();
|
|
195
190
|
}
|
|
196
191
|
/**
|
|
@@ -200,7 +195,6 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
200
195
|
*/
|
|
201
196
|
onGamepadDisconnected(gamepad) {
|
|
202
197
|
this.#endTransform(true);
|
|
203
|
-
this.#pressedButtons.clear();
|
|
204
198
|
super.onGamepadDisconnected(gamepad);
|
|
205
199
|
}
|
|
206
200
|
/**
|
|
@@ -882,21 +876,27 @@ var GamepadTransformControls = class extends GamepadControls {
|
|
|
882
876
|
return Math.abs(inputX) >= Math.abs(inputY) ? inputX : inputY;
|
|
883
877
|
}
|
|
884
878
|
/**
|
|
885
|
-
*
|
|
879
|
+
* Returns configured button indices that were newly pressed this frame.
|
|
886
880
|
*
|
|
887
|
-
* @param gamepad - Fresh gamepad snapshot to read from.
|
|
888
881
|
* @returns Button indices that transitioned to pressed.
|
|
889
882
|
*/
|
|
890
|
-
#getStartedButtons(
|
|
883
|
+
#getStartedButtons() {
|
|
891
884
|
const startedButtons = /* @__PURE__ */ new Set();
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
885
|
+
const input = this.gamepadInput;
|
|
886
|
+
const buttons = [
|
|
887
|
+
this.#options.buttonTranslate,
|
|
888
|
+
this.#options.buttonRotate,
|
|
889
|
+
this.#options.buttonScale,
|
|
890
|
+
this.#options.buttonToggleSpace,
|
|
891
|
+
this.#options.buttonAxisX,
|
|
892
|
+
this.#options.buttonAxisY,
|
|
893
|
+
this.#options.buttonAxisZ,
|
|
894
|
+
this.#options.buttonAxisComposite,
|
|
895
|
+
this.#options.buttonAxisPrevious,
|
|
896
|
+
this.#options.buttonAxisNext,
|
|
897
|
+
this.#options.buttonReset
|
|
898
|
+
];
|
|
899
|
+
for (const button of buttons) if (input.wasPressed(button)) startedButtons.add(button);
|
|
900
900
|
return startedButtons;
|
|
901
901
|
}
|
|
902
902
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON, GamepadAxisKey, GamepadAxisValue, GamepadButtonKey, GamepadButtonValue } from "./core.js";
|
|
2
|
+
import { GamepadAxisOptions, GamepadInput, GamepadInputEventMap, GamepadInputOptions, GamepadStick } from "./gamepad-input.js";
|
|
2
3
|
import { GamepadControls, GamepadControlsEventMap } from "./gamepad-controls.js";
|
|
3
4
|
import { GamepadArcballControls, GamepadArcballControlsOptions } from "./gamepad-arcball-controls.js";
|
|
4
5
|
import { GamepadDragControls, GamepadDragControlsOptions } from "./gamepad-drag-controls.js";
|
|
5
6
|
import { GamepadFirstPersonControls, GamepadFirstPersonControlsOptions } from "./gamepad-first-person-controls.js";
|
|
6
7
|
import { GamepadFlyControls, GamepadFlyControlsOptions } from "./gamepad-fly-controls.js";
|
|
7
|
-
import { GamepadAxisOptions, GamepadInput, GamepadInputEventMap, GamepadInputOptions, GamepadStick } from "./gamepad-input.js";
|
|
8
8
|
import { GamepadOrbitControls, GamepadOrbitControlsOptions } from "./gamepad-orbit-controls.js";
|
|
9
9
|
import { GamepadMapControls } from "./gamepad-map-controls.js";
|
|
10
10
|
import { GamepadPointerLockControls, GamepadPointerLockControlsOptions } from "./gamepad-pointer-lock-controls.js";
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { GAMEPAD_AXIS, GAMEPAD_BUTTON } from "./core.js";
|
|
2
|
+
import { GamepadInput } from "./gamepad-input.js";
|
|
2
3
|
import { GamepadControls } from "./gamepad-controls.js";
|
|
3
4
|
import { GamepadArcballControls } from "./gamepad-arcball-controls.js";
|
|
4
5
|
import { GamepadDragControls } from "./gamepad-drag-controls.js";
|
|
5
6
|
import { GamepadFirstPersonControls } from "./gamepad-first-person-controls.js";
|
|
6
7
|
import { GamepadFlyControls } from "./gamepad-fly-controls.js";
|
|
7
|
-
import { GamepadInput } from "./gamepad-input.js";
|
|
8
8
|
import { GamepadOrbitControls } from "./gamepad-orbit-controls.js";
|
|
9
9
|
import { GamepadMapControls } from "./gamepad-map-controls.js";
|
|
10
10
|
import { GamepadPointerLockControls } from "./gamepad-pointer-lock-controls.js";
|
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.
|
|
4
|
+
"version": "0.12.1",
|
|
5
5
|
"homepage": "https://github.com/luckasnix/three-gamepad-controls#readme",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Kasnix",
|
|
@@ -51,14 +51,14 @@
|
|
|
51
51
|
"@commitlint/config-conventional": "21.0.2",
|
|
52
52
|
"@commitlint/types": "21.0.1",
|
|
53
53
|
"@types/node": "24.13.2",
|
|
54
|
-
"@types/three": "0.184.
|
|
54
|
+
"@types/three": "0.184.0",
|
|
55
55
|
"husky": "9.1.7",
|
|
56
56
|
"three": "0.184.0",
|
|
57
57
|
"tsdown": "0.22.2",
|
|
58
58
|
"typescript": "6.0.3"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@types/three": ">=0.184.
|
|
61
|
+
"@types/three": ">=0.184.0",
|
|
62
62
|
"three": ">=0.184.0"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
package/dist/utils.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
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
|
-
//#endregion
|
|
41
|
-
export { applyGamepadDeadzone, getGamepadButtonPressed, getGamepadButtonValue };
|