@utsp/input 0.9.0 → 0.10.0-nightly.20251213135145.115c488
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/core.cjs +1 -0
- package/dist/core.d.ts +200 -0
- package/dist/core.mjs +1 -0
- package/dist/desktop.cjs +1 -0
- package/dist/desktop.d.ts +540 -0
- package/dist/desktop.mjs +1 -0
- package/dist/gamepad.cjs +1 -0
- package/dist/gamepad.d.ts +563 -0
- package/dist/gamepad.mjs +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +369 -5
- package/dist/index.mjs +1 -1
- package/dist/keyboard.cjs +1 -0
- package/dist/keyboard.d.ts +341 -0
- package/dist/keyboard.mjs +1 -0
- package/dist/mouse.cjs +1 -0
- package/dist/mouse.d.ts +376 -0
- package/dist/mouse.mjs +1 -0
- package/dist/touch.cjs +1 -0
- package/dist/touch.d.ts +572 -0
- package/dist/touch.mjs +1 -0
- package/package.json +54 -2
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import { Vector2, Vector3, IGamepadVibrationProcessor, GamepadVibrationOptions } from '@utsp/types';
|
|
2
|
+
export { GamepadInput, InputDeviceType, Vector2 } from '@utsp/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base interface for all input management systems
|
|
6
|
+
* This provides a common API for keyboard, mouse, gamepad, and touch inputs
|
|
7
|
+
*/
|
|
8
|
+
interface IInputs {
|
|
9
|
+
/**
|
|
10
|
+
* Start listening to input events
|
|
11
|
+
*/
|
|
12
|
+
start(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Stop listening to input events and cleanup
|
|
15
|
+
*/
|
|
16
|
+
stop(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Reset all input states to default
|
|
19
|
+
*/
|
|
20
|
+
reset(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Check if the input system is currently active/listening
|
|
23
|
+
*/
|
|
24
|
+
isListening(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Cleanup and destroy the instance, removing all event listeners
|
|
27
|
+
*/
|
|
28
|
+
destroy(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Reset delta values (call at the end of each frame/update)
|
|
31
|
+
*/
|
|
32
|
+
resetDelta(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Set or update event callbacks
|
|
35
|
+
*/
|
|
36
|
+
setCallbacks(callbacks: unknown): void;
|
|
37
|
+
/**
|
|
38
|
+
* Remove all event callbacks
|
|
39
|
+
*/
|
|
40
|
+
clearCallbacks(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Check if a key is currently pressed (Keyboard)
|
|
43
|
+
*/
|
|
44
|
+
isKeyPressed?(key: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Check if left mouse button is pressed (Mouse)
|
|
47
|
+
*/
|
|
48
|
+
isLeftMousePressed?(): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Get mouse position (Mouse)
|
|
51
|
+
*/
|
|
52
|
+
getMousePosition?(): Vector2 | null;
|
|
53
|
+
/**
|
|
54
|
+
* Get mouse movement delta (Mouse)
|
|
55
|
+
*/
|
|
56
|
+
getMouseDelta?(): Vector2 | null;
|
|
57
|
+
/**
|
|
58
|
+
* Get wheel delta (Mouse)
|
|
59
|
+
*/
|
|
60
|
+
getWheelDelta?(): Vector2 | null;
|
|
61
|
+
/**
|
|
62
|
+
* Check if a button is pressed (Gamepad, TVRemote)
|
|
63
|
+
*/
|
|
64
|
+
isButtonPressed?(button: string | number): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Get axis value (Gamepad, TVRemote)
|
|
67
|
+
*/
|
|
68
|
+
getAxis?(axis: number): number | null;
|
|
69
|
+
/**
|
|
70
|
+
* Get motion/gyroscope data (TVRemote, Mobile)
|
|
71
|
+
*/
|
|
72
|
+
getMotionData?(): MotionData | null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Motion/Gyroscope data interface
|
|
76
|
+
*/
|
|
77
|
+
interface MotionData extends Vector3 {
|
|
78
|
+
timestamp?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Common button state interface
|
|
82
|
+
*/
|
|
83
|
+
interface ButtonState {
|
|
84
|
+
pressed: boolean;
|
|
85
|
+
justPressed: boolean;
|
|
86
|
+
justReleased: boolean;
|
|
87
|
+
timestamp?: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Input event types enumeration
|
|
91
|
+
*/
|
|
92
|
+
declare enum InputEventType {
|
|
93
|
+
KeyDown = "keydown",
|
|
94
|
+
KeyUp = "keyup",
|
|
95
|
+
MouseDown = "mousedown",
|
|
96
|
+
MouseUp = "mouseup",
|
|
97
|
+
MouseMove = "mousemove",
|
|
98
|
+
MouseWheel = "mousewheel",
|
|
99
|
+
TouchStart = "touchstart",
|
|
100
|
+
TouchEnd = "touchend",
|
|
101
|
+
TouchMove = "touchmove",
|
|
102
|
+
GamepadConnected = "gamepadconnected",
|
|
103
|
+
GamepadDisconnected = "gamepaddisconnected",
|
|
104
|
+
GamepadButton = "gamepadbutton",
|
|
105
|
+
GamepadAxis = "gamepadaxis"
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Configuration options for input systems
|
|
109
|
+
*/
|
|
110
|
+
interface InputConfig {
|
|
111
|
+
/**
|
|
112
|
+
* Target element to attach listeners to (default: window)
|
|
113
|
+
*/
|
|
114
|
+
targetElement?: HTMLElement | Window;
|
|
115
|
+
/**
|
|
116
|
+
* Enable/disable specific input types
|
|
117
|
+
*/
|
|
118
|
+
enabled?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Prevent default browser behaviors
|
|
121
|
+
*/
|
|
122
|
+
preventDefault?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Stop event propagation
|
|
125
|
+
*/
|
|
126
|
+
stopPropagation?: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Enable debug logging
|
|
129
|
+
*/
|
|
130
|
+
debug?: boolean;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Abstract base class for input systems
|
|
134
|
+
* All input classes should extend this to maintain consistency
|
|
135
|
+
*/
|
|
136
|
+
declare abstract class BaseInputs implements IInputs {
|
|
137
|
+
protected isActive: boolean;
|
|
138
|
+
protected targetElement: HTMLElement | Window;
|
|
139
|
+
protected config: InputConfig;
|
|
140
|
+
protected callbacks: Record<string, ((...args: any[]) => void)[]>;
|
|
141
|
+
constructor(targetElement?: HTMLElement | Window, config?: InputConfig);
|
|
142
|
+
abstract start(): void;
|
|
143
|
+
abstract stop(): void;
|
|
144
|
+
abstract reset(): void;
|
|
145
|
+
abstract resetDelta(): void;
|
|
146
|
+
isListening(): boolean;
|
|
147
|
+
destroy(): void;
|
|
148
|
+
setCallbacks(callbacks: unknown): void;
|
|
149
|
+
clearCallbacks(): void;
|
|
150
|
+
/**
|
|
151
|
+
* Get the target element
|
|
152
|
+
*/
|
|
153
|
+
getTargetElement(): HTMLElement | Window;
|
|
154
|
+
/**
|
|
155
|
+
* Enable the input system
|
|
156
|
+
*/
|
|
157
|
+
enable(): void;
|
|
158
|
+
/**
|
|
159
|
+
* Disable the input system
|
|
160
|
+
*/
|
|
161
|
+
disable(): void;
|
|
162
|
+
/**
|
|
163
|
+
* Check if the input system is enabled
|
|
164
|
+
*/
|
|
165
|
+
isEnabled(): boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Emit an event to all registered callbacks
|
|
168
|
+
*/
|
|
169
|
+
protected emit(eventType: string, ...args: unknown[]): void;
|
|
170
|
+
/**
|
|
171
|
+
* Log debug messages if debug mode is enabled
|
|
172
|
+
*/
|
|
173
|
+
protected log(...args: unknown[]): void;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* GamepadInputs - Gamepad/Controller input management
|
|
178
|
+
* Handles gamepad events with support for multiple controllers, buttons, axes, and vibration
|
|
179
|
+
*/
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Gamepad button state with press detection
|
|
183
|
+
*/
|
|
184
|
+
interface GamepadButtonState {
|
|
185
|
+
pressed: boolean;
|
|
186
|
+
justPressed: boolean;
|
|
187
|
+
justReleased: boolean;
|
|
188
|
+
value: number;
|
|
189
|
+
touched: boolean;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Complete state of a single gamepad
|
|
193
|
+
*/
|
|
194
|
+
interface GamepadState {
|
|
195
|
+
id: string;
|
|
196
|
+
index: number;
|
|
197
|
+
connected: boolean;
|
|
198
|
+
buttons: GamepadButtonState[];
|
|
199
|
+
axes: number[];
|
|
200
|
+
timestamp: number;
|
|
201
|
+
mapping: GamepadMappingType;
|
|
202
|
+
hapticActuators?: GamepadHapticActuator[];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Callbacks for gamepad events
|
|
206
|
+
*/
|
|
207
|
+
interface GamepadCallbacks {
|
|
208
|
+
onGamepadConnected?: (gamepad: GamepadState) => void;
|
|
209
|
+
onGamepadDisconnected?: (index: number) => void;
|
|
210
|
+
onButtonDown?: (gamepadIndex: number, buttonIndex: number, value: number) => void;
|
|
211
|
+
onButtonUp?: (gamepadIndex: number, buttonIndex: number) => void;
|
|
212
|
+
onAxisMove?: (gamepadIndex: number, axisIndex: number, value: number) => void;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Configuration for GamepadInputs
|
|
216
|
+
*/
|
|
217
|
+
interface GamepadInputsConfig extends InputConfig {
|
|
218
|
+
/** Maximum number of gamepads to support (default: 4) */
|
|
219
|
+
maxGamepads?: number;
|
|
220
|
+
/** Deadzone for analog axes (default: 0.1) */
|
|
221
|
+
axisDeadzone?: number;
|
|
222
|
+
/** Polling interval in milliseconds (default: 16 ~= 60fps) */
|
|
223
|
+
pollingInterval?: number;
|
|
224
|
+
/** Enable automatic polling (default: true) */
|
|
225
|
+
autoPolling?: boolean;
|
|
226
|
+
}
|
|
227
|
+
declare class GamepadInputs extends BaseInputs implements IGamepadVibrationProcessor {
|
|
228
|
+
private gamepads;
|
|
229
|
+
private previousGamepads;
|
|
230
|
+
private gamepadCallbacks;
|
|
231
|
+
private gamepadConfig;
|
|
232
|
+
private pollingIntervalId;
|
|
233
|
+
private rafId;
|
|
234
|
+
private boundHandlers;
|
|
235
|
+
constructor(config?: GamepadInputsConfig);
|
|
236
|
+
start(): void;
|
|
237
|
+
stop(): void;
|
|
238
|
+
reset(): void;
|
|
239
|
+
resetDelta(): void;
|
|
240
|
+
/**
|
|
241
|
+
* Start automatic polling of gamepad state
|
|
242
|
+
*/
|
|
243
|
+
startPolling(): void;
|
|
244
|
+
/**
|
|
245
|
+
* Stop automatic polling
|
|
246
|
+
*/
|
|
247
|
+
stopPolling(): void;
|
|
248
|
+
/**
|
|
249
|
+
* Manually poll gamepad state (call this in your game loop if autoPolling is false)
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```typescript
|
|
253
|
+
* const gamepad = new GamepadInputs({ autoPolling: false });
|
|
254
|
+
* gamepad.start();
|
|
255
|
+
*
|
|
256
|
+
* function gameLoop() {
|
|
257
|
+
* gamepad.poll(); // Manual polling
|
|
258
|
+
* // Check inputs...
|
|
259
|
+
* requestAnimationFrame(gameLoop);
|
|
260
|
+
* }
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
poll(): void;
|
|
264
|
+
/**
|
|
265
|
+
* Get the number of connected gamepads
|
|
266
|
+
*
|
|
267
|
+
* @returns Number of gamepads currently connected (0-4)
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* const count = gamepad.getConnectedGamepadCount();
|
|
272
|
+
* console.log(`${count} gamepads connected`);
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
getConnectedGamepadCount(): number;
|
|
276
|
+
/**
|
|
277
|
+
* Check if a gamepad is connected at the given index
|
|
278
|
+
*
|
|
279
|
+
* @param index - Gamepad index (0-3)
|
|
280
|
+
* @returns True if gamepad is connected, false otherwise
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```typescript
|
|
284
|
+
* if (gamepad.isGamepadConnected(0)) {
|
|
285
|
+
* console.log('Player 1 controller ready!');
|
|
286
|
+
* }
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
isGamepadConnected(index: number): boolean;
|
|
290
|
+
/**
|
|
291
|
+
* Get the state of a specific gamepad
|
|
292
|
+
*/
|
|
293
|
+
getGamepadState(index: number): GamepadState | null;
|
|
294
|
+
/**
|
|
295
|
+
* Get all connected gamepads
|
|
296
|
+
*/
|
|
297
|
+
getAllGamepads(): GamepadState[];
|
|
298
|
+
/**
|
|
299
|
+
* Check if a button is currently pressed
|
|
300
|
+
*
|
|
301
|
+
* @param gamepadIndex - Gamepad index (0-3)
|
|
302
|
+
* @param buttonIndex - Button index (0-16, standard mapping)
|
|
303
|
+
* @returns True if button is pressed, false otherwise
|
|
304
|
+
* @throws {Error} If gamepadIndex or buttonIndex is invalid
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```typescript
|
|
308
|
+
* // Check A button (Xbox) / Cross (PlayStation)
|
|
309
|
+
* if (gamepad.isButtonPressed(0, 0)) {
|
|
310
|
+
* console.log('Action button pressed!');
|
|
311
|
+
* }
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
isButtonPressed(gamepadIndex: number, buttonIndex: number): boolean;
|
|
315
|
+
/**
|
|
316
|
+
* Check if a button was just pressed this frame
|
|
317
|
+
*/
|
|
318
|
+
isButtonJustPressed(gamepadIndex: number, buttonIndex: number): boolean;
|
|
319
|
+
/**
|
|
320
|
+
* Check if a button was just released this frame
|
|
321
|
+
*/
|
|
322
|
+
isButtonJustReleased(gamepadIndex: number, buttonIndex: number): boolean;
|
|
323
|
+
/**
|
|
324
|
+
* Get the analog value of a button (0.0 to 1.0)
|
|
325
|
+
*/
|
|
326
|
+
getButtonValue(gamepadIndex: number, buttonIndex: number): number;
|
|
327
|
+
/**
|
|
328
|
+
* Get the value of an axis (-1.0 to 1.0, with deadzone applied)
|
|
329
|
+
*
|
|
330
|
+
* @param gamepadIndex - Gamepad index (0-3)
|
|
331
|
+
* @param axisIndex - Axis index (0: left X, 1: left Y, 2: right X, 3: right Y)
|
|
332
|
+
* @returns Axis value from -1.0 (left/up) to 1.0 (right/down)
|
|
333
|
+
* @throws {Error} If gamepadIndex or axisIndex is invalid
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* const leftStickX = gamepad.getAxis(0, 0);
|
|
338
|
+
* const leftStickY = gamepad.getAxis(0, 1);
|
|
339
|
+
*
|
|
340
|
+
* playerX += leftStickX * speed;
|
|
341
|
+
* playerY += leftStickY * speed;
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
getAxis(gamepadIndex: number, axisIndex: number): number;
|
|
345
|
+
/**
|
|
346
|
+
* Get the raw axis value without deadzone
|
|
347
|
+
*/
|
|
348
|
+
getAxisRaw(gamepadIndex: number, axisIndex: number): number;
|
|
349
|
+
/**
|
|
350
|
+
* Get left stick as a 2D vector (x: axis 0, y: axis 1)
|
|
351
|
+
*
|
|
352
|
+
* @param gamepadIndex - Gamepad index (0-3)
|
|
353
|
+
* @returns Object with x and y properties (-1.0 to 1.0)
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* ```typescript
|
|
357
|
+
* const leftStick = gamepad.getLeftStick(0);
|
|
358
|
+
*
|
|
359
|
+
* if (Math.abs(leftStick.x) > 0.5) {
|
|
360
|
+
* console.log('Strong horizontal input');
|
|
361
|
+
* }
|
|
362
|
+
*
|
|
363
|
+
* // Calculate magnitude for movement
|
|
364
|
+
* const magnitude = Math.sqrt(leftStick.x ** 2 + leftStick.y ** 2);
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
getLeftStick(gamepadIndex: number): {
|
|
368
|
+
x: number;
|
|
369
|
+
y: number;
|
|
370
|
+
};
|
|
371
|
+
/**
|
|
372
|
+
* Get right stick as a 2D vector (x: axis 2, y: axis 3)
|
|
373
|
+
*/
|
|
374
|
+
getRightStick(gamepadIndex: number): {
|
|
375
|
+
x: number;
|
|
376
|
+
y: number;
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* Check if a gamepad supports vibration
|
|
380
|
+
*
|
|
381
|
+
* @param gamepadIndex - Gamepad index (0-3)
|
|
382
|
+
* @returns True if gamepad supports vibration
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```typescript
|
|
386
|
+
* if (gamepad.supportsVibration(0)) {
|
|
387
|
+
* gamepad.vibrate(0, { duration: 200, strongMagnitude: 1.0 });
|
|
388
|
+
* }
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
supportsVibration(gamepadIndex: number): boolean;
|
|
392
|
+
/**
|
|
393
|
+
* Vibrate a gamepad with dual-motor control
|
|
394
|
+
*
|
|
395
|
+
* Modern gamepads have two motors:
|
|
396
|
+
* - Strong motor (low frequency) - heavy rumble
|
|
397
|
+
* - Weak motor (high frequency) - light vibration
|
|
398
|
+
*
|
|
399
|
+
* @param gamepadIndex - Gamepad index (0-3)
|
|
400
|
+
* @param options - Vibration options
|
|
401
|
+
* @returns Promise that resolves when vibration completes or fails
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```typescript
|
|
405
|
+
* // Strong rumble for 500ms
|
|
406
|
+
* await gamepad.vibrate(0, {
|
|
407
|
+
* duration: 500,
|
|
408
|
+
* strongMagnitude: 1.0,
|
|
409
|
+
* weakMagnitude: 0.0,
|
|
410
|
+
* });
|
|
411
|
+
*
|
|
412
|
+
* // Light vibration
|
|
413
|
+
* await gamepad.vibrate(0, {
|
|
414
|
+
* duration: 200,
|
|
415
|
+
* strongMagnitude: 0.0,
|
|
416
|
+
* weakMagnitude: 0.5,
|
|
417
|
+
* });
|
|
418
|
+
*
|
|
419
|
+
* // Balanced rumble
|
|
420
|
+
* await gamepad.vibrate(0, {
|
|
421
|
+
* duration: 300,
|
|
422
|
+
* strongMagnitude: 0.5,
|
|
423
|
+
* weakMagnitude: 0.5,
|
|
424
|
+
* });
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
427
|
+
vibrate(gamepadIndex: number, options: GamepadVibrationOptions): Promise<boolean>;
|
|
428
|
+
/**
|
|
429
|
+
* Stop vibration on a gamepad
|
|
430
|
+
*
|
|
431
|
+
* @param gamepadIndex - Gamepad index (0-3)
|
|
432
|
+
* @returns Promise that resolves when vibration is stopped
|
|
433
|
+
*/
|
|
434
|
+
stopVibration(gamepadIndex: number): Promise<boolean>;
|
|
435
|
+
/**
|
|
436
|
+
* Stop vibration on all connected gamepads
|
|
437
|
+
*/
|
|
438
|
+
stopAllVibrations(): Promise<void>;
|
|
439
|
+
/**
|
|
440
|
+
* Vibrate all connected gamepads
|
|
441
|
+
*
|
|
442
|
+
* @param options - Vibration options (same as vibrate())
|
|
443
|
+
*/
|
|
444
|
+
vibrateAll(options: {
|
|
445
|
+
duration: number;
|
|
446
|
+
strongMagnitude?: number;
|
|
447
|
+
weakMagnitude?: number;
|
|
448
|
+
startDelay?: number;
|
|
449
|
+
}): Promise<void>;
|
|
450
|
+
/**
|
|
451
|
+
* Preset vibration patterns for common use cases
|
|
452
|
+
*/
|
|
453
|
+
readonly vibrationPresets: {
|
|
454
|
+
/** Light tap feedback */
|
|
455
|
+
tap: {
|
|
456
|
+
duration: number;
|
|
457
|
+
strongMagnitude: number;
|
|
458
|
+
weakMagnitude: number;
|
|
459
|
+
};
|
|
460
|
+
/** Medium impact */
|
|
461
|
+
impact: {
|
|
462
|
+
duration: number;
|
|
463
|
+
strongMagnitude: number;
|
|
464
|
+
weakMagnitude: number;
|
|
465
|
+
};
|
|
466
|
+
/** Heavy rumble */
|
|
467
|
+
heavy: {
|
|
468
|
+
duration: number;
|
|
469
|
+
strongMagnitude: number;
|
|
470
|
+
weakMagnitude: number;
|
|
471
|
+
};
|
|
472
|
+
/** Success feedback */
|
|
473
|
+
success: {
|
|
474
|
+
duration: number;
|
|
475
|
+
strongMagnitude: number;
|
|
476
|
+
weakMagnitude: number;
|
|
477
|
+
};
|
|
478
|
+
/** Error/failure feedback */
|
|
479
|
+
error: {
|
|
480
|
+
duration: number;
|
|
481
|
+
strongMagnitude: number;
|
|
482
|
+
weakMagnitude: number;
|
|
483
|
+
};
|
|
484
|
+
/** Explosion effect */
|
|
485
|
+
explosion: {
|
|
486
|
+
duration: number;
|
|
487
|
+
strongMagnitude: number;
|
|
488
|
+
weakMagnitude: number;
|
|
489
|
+
};
|
|
490
|
+
/** Engine/continuous rumble */
|
|
491
|
+
engine: {
|
|
492
|
+
duration: number;
|
|
493
|
+
strongMagnitude: number;
|
|
494
|
+
weakMagnitude: number;
|
|
495
|
+
};
|
|
496
|
+
/** Heartbeat (use twice with delay) */
|
|
497
|
+
heartbeat: {
|
|
498
|
+
duration: number;
|
|
499
|
+
strongMagnitude: number;
|
|
500
|
+
weakMagnitude: number;
|
|
501
|
+
};
|
|
502
|
+
};
|
|
503
|
+
/**
|
|
504
|
+
* Play a preset vibration pattern
|
|
505
|
+
*
|
|
506
|
+
* @param gamepadIndex - Gamepad index (0-3)
|
|
507
|
+
* @param preset - Preset name
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```typescript
|
|
511
|
+
* // On hit
|
|
512
|
+
* gamepad.vibratePreset(0, 'impact');
|
|
513
|
+
*
|
|
514
|
+
* // On explosion
|
|
515
|
+
* gamepad.vibratePreset(0, 'explosion');
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
518
|
+
vibratePreset(gamepadIndex: number, preset: keyof typeof this.vibrationPresets): Promise<boolean>;
|
|
519
|
+
/**
|
|
520
|
+
* Get the native Gamepad object (for advanced usage)
|
|
521
|
+
*/
|
|
522
|
+
private getNativeGamepad;
|
|
523
|
+
/**
|
|
524
|
+
* Set gamepad-specific callbacks
|
|
525
|
+
*
|
|
526
|
+
* @param callbacks - Object containing callback functions
|
|
527
|
+
*
|
|
528
|
+
* @example
|
|
529
|
+
* ```typescript
|
|
530
|
+
* gamepad.setGamepadCallbacks({
|
|
531
|
+
* onGamepadConnected: (state) => {
|
|
532
|
+
* console.log(`Gamepad connected: ${state.id}`);
|
|
533
|
+
* },
|
|
534
|
+
* onButtonDown: (gamepadIndex, buttonIndex, value) => {
|
|
535
|
+
* console.log(`Button ${buttonIndex} pressed with value ${value}`);
|
|
536
|
+
* },
|
|
537
|
+
* onAxisMove: (gamepadIndex, axisIndex, value) => {
|
|
538
|
+
* console.log(`Axis ${axisIndex} moved to ${value}`);
|
|
539
|
+
* },
|
|
540
|
+
* });
|
|
541
|
+
* ```
|
|
542
|
+
*/
|
|
543
|
+
setGamepadCallbacks(callbacks: GamepadCallbacks): void;
|
|
544
|
+
/**
|
|
545
|
+
* Clear all gamepad callbacks
|
|
546
|
+
*/
|
|
547
|
+
clearGamepadCallbacks(): void;
|
|
548
|
+
setCallbacks(callbacks: GamepadCallbacks | unknown): void;
|
|
549
|
+
private isGamepadCallbacks;
|
|
550
|
+
private updateGamepadState;
|
|
551
|
+
private triggerCallbacks;
|
|
552
|
+
private handleGamepadConnected;
|
|
553
|
+
private handleGamepadDisconnected;
|
|
554
|
+
private applyDeadzone;
|
|
555
|
+
private cloneGamepadState;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* React hook for gamepad inputs
|
|
559
|
+
*/
|
|
560
|
+
declare function useGamepadInputs(callbacks?: GamepadCallbacks, config?: GamepadInputsConfig): GamepadInputs;
|
|
561
|
+
|
|
562
|
+
export { BaseInputs, GamepadInputs, InputEventType, useGamepadInputs };
|
|
563
|
+
export type { ButtonState, GamepadButtonState, GamepadCallbacks, GamepadInputsConfig, GamepadState, IInputs, InputConfig };
|
package/dist/gamepad.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var G=Object.defineProperty;var M=(o,n,e)=>n in o?G(o,n,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[n]=e;var m=(o,n)=>G(o,"name",{value:n,configurable:!0});var s=(o,n,e)=>(M(o,typeof n!="symbol"?n+"":n,e),e);import{Vector2 as V}from"@utsp/types";import{InputDeviceType as B,GamepadInput as N}from"@utsp/types";var w=(r=>(r.KeyDown="keydown",r.KeyUp="keyup",r.MouseDown="mousedown",r.MouseUp="mouseup",r.MouseMove="mousemove",r.MouseWheel="mousewheel",r.TouchStart="touchstart",r.TouchEnd="touchend",r.TouchMove="touchmove",r.GamepadConnected="gamepadconnected",r.GamepadDisconnected="gamepaddisconnected",r.GamepadButton="gamepadbutton",r.GamepadAxis="gamepadaxis",r))(w||{}),f=class f{constructor(n=window,e={}){s(this,"isActive",!1);s(this,"targetElement");s(this,"config");s(this,"callbacks",{});this.targetElement=n,this.config={enabled:!0,preventDefault:!1,stopPropagation:!1,debug:!1,...e}}isListening(){return this.isActive}destroy(){this.stop(),this.clearCallbacks()}setCallbacks(n){typeof n=="object"&&n!==null&&Object.assign(this.callbacks,n)}clearCallbacks(){this.callbacks={}}getTargetElement(){return this.targetElement}enable(){this.config.enabled=!0,this.isActive||this.start()}disable(){this.config.enabled=!1,this.isActive&&this.stop()}isEnabled(){return this.config.enabled??!0}emit(n,...e){this.callbacks[n]&&this.callbacks[n].forEach(t=>{try{t(...e)}catch(a){this.config.debug&&console.error(`Error in ${n} callback:`,a)}})}log(...n){this.config.debug&&console.warn("[InputSystem]",...n)}};m(f,"BaseInputs");var b=f;var k=.01,v=class v extends b{constructor(e={}){super(window,e);s(this,"gamepads",new Map);s(this,"previousGamepads",new Map);s(this,"gamepadCallbacks",{});s(this,"gamepadConfig");s(this,"pollingIntervalId",null);s(this,"rafId",null);s(this,"boundHandlers");s(this,"vibrationPresets",{tap:{duration:50,strongMagnitude:.3,weakMagnitude:.5},impact:{duration:100,strongMagnitude:.7,weakMagnitude:.3},heavy:{duration:200,strongMagnitude:1,weakMagnitude:.2},success:{duration:150,strongMagnitude:.4,weakMagnitude:.6},error:{duration:300,strongMagnitude:.8,weakMagnitude:.4},explosion:{duration:400,strongMagnitude:1,weakMagnitude:.8},engine:{duration:1e3,strongMagnitude:.5,weakMagnitude:.2},heartbeat:{duration:100,strongMagnitude:.6,weakMagnitude:.1}});this.gamepadConfig={targetElement:window,maxGamepads:e.maxGamepads??4,axisDeadzone:e.axisDeadzone??.1,pollingInterval:e.pollingInterval??16,autoPolling:e.autoPolling??!0,enabled:e.enabled??!0,debug:e.debug??!1,preventDefault:e.preventDefault??!1,stopPropagation:e.stopPropagation??!1},this.boundHandlers={connected:this.handleGamepadConnected.bind(this),disconnected:this.handleGamepadDisconnected.bind(this)}}start(){this.isActive||(window.addEventListener("gamepadconnected",this.boundHandlers.connected),window.addEventListener("gamepaddisconnected",this.boundHandlers.disconnected),this.gamepadConfig.autoPolling&&this.startPolling(),this.isActive=!0,this.log("GamepadInputs started"))}stop(){this.isActive&&(window.removeEventListener("gamepadconnected",this.boundHandlers.connected),window.removeEventListener("gamepaddisconnected",this.boundHandlers.disconnected),this.stopPolling(),this.isActive=!1,this.log("GamepadInputs stopped"))}reset(){this.gamepads.clear(),this.previousGamepads.clear()}resetDelta(){this.previousGamepads.clear(),this.gamepads.forEach((e,t)=>{this.previousGamepads.set(t,this.cloneGamepadState(e))})}startPolling(){if(this.pollingIntervalId!==null||this.rafId!==null)return;let e=m(()=>{this.poll(),this.rafId=requestAnimationFrame(e)},"poll");this.rafId=requestAnimationFrame(e),this.log("Polling started")}stopPolling(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.pollingIntervalId!==null&&(clearInterval(this.pollingIntervalId),this.pollingIntervalId=null),this.log("Polling stopped")}poll(){let e=navigator.getGamepads?navigator.getGamepads():[];for(let t=0;t<e.length&&t<this.gamepadConfig.maxGamepads;t++){let a=e[t];a&&this.updateGamepadState(a)}}getConnectedGamepadCount(){return this.gamepads.size}isGamepadConnected(e){return this.gamepads.has(e)&&this.gamepads.get(e).connected}getGamepadState(e){return this.gamepads.get(e)||null}getAllGamepads(){return Array.from(this.gamepads.values()).filter(e=>e.connected)}isButtonPressed(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid buttonIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.buttons.length?!1:a.buttons[t].pressed}isButtonJustPressed(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid buttonIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.buttons.length?!1:a.buttons[t].justPressed}isButtonJustReleased(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid buttonIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.buttons.length?!1:a.buttons[t].justReleased}getButtonValue(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid buttonIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.buttons.length?0:a.buttons[t].value}getAxis(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid axisIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);if(!a||!a.connected||t>=a.axes.length)return 0;let i=a.axes[t];return this.applyDeadzone(i)}getAxisRaw(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`Invalid gamepadIndex: ${e}. Must be a non-negative integer.`);if(!Number.isInteger(t)||t<0)throw new Error(`Invalid axisIndex: ${t}. Must be a non-negative integer.`);let a=this.gamepads.get(e);return!a||!a.connected||t>=a.axes.length?0:a.axes[t]}getLeftStick(e){return{x:this.getAxis(e,0),y:this.getAxis(e,1)}}getRightStick(e){return{x:this.getAxis(e,2),y:this.getAxis(e,3)}}supportsVibration(e){let t=this.getNativeGamepad(e);if(!t)return!1;let a=t.vibrationActuator;if(a&&typeof a.playEffect=="function")return!0;let i=t.hapticActuators;return!!(i&&i.length>0)}async vibrate(e,t){let a=this.getNativeGamepad(e);if(!a)return this.log(`Vibrate failed: gamepad ${e} not found`),!1;let{duration:i,strongMagnitude:u=1,weakMagnitude:d=.5,startDelay:g=0}=t;try{let l=a.vibrationActuator;if(l&&typeof l.playEffect=="function"){let c=await l.playEffect("dual-rumble",{startDelay:g,duration:i,strongMagnitude:Math.min(1,Math.max(0,u)),weakMagnitude:Math.min(1,Math.max(0,d))});return this.log(`Vibration effect result: ${c}`),c==="complete"}let p=a.hapticActuators;if(p&&p.length>0){let c=Math.max(u,d);return await p[0].pulse(c,i),!0}return this.log(`Vibrate failed: gamepad ${e} does not support vibration`),!1}catch(l){return this.log("Vibrate error:",l),!1}}async stopVibration(e){let t=this.getNativeGamepad(e);if(!t)return!1;try{let a=t.vibrationActuator;return a&&typeof a.reset=="function"?(await a.reset(),!0):a&&typeof a.playEffect=="function"?(await a.playEffect("dual-rumble",{duration:0,strongMagnitude:0,weakMagnitude:0}),!0):!1}catch(a){return this.log("Stop vibration error:",a),!1}}async stopAllVibrations(){let e=[];for(let[t]of this.gamepads)e.push(this.stopVibration(t));await Promise.all(e)}async vibrateAll(e){let t=[];for(let[a]of this.gamepads)t.push(this.vibrate(a,e));await Promise.all(t)}async vibratePreset(e,t){let a=this.vibrationPresets[t];return a?this.vibrate(e,a):(this.log(`Unknown vibration preset: ${t}`),!1)}getNativeGamepad(e){return(navigator.getGamepads?navigator.getGamepads():[])[e]||null}setGamepadCallbacks(e){this.gamepadCallbacks={...this.gamepadCallbacks,...e}}clearGamepadCallbacks(){this.gamepadCallbacks={}}setCallbacks(e){this.isGamepadCallbacks(e)&&this.setGamepadCallbacks(e)}isGamepadCallbacks(e){return typeof e=="object"&&e!==null&&("onGamepadConnected"in e||"onGamepadDisconnected"in e||"onButtonDown"in e||"onButtonUp"in e||"onAxisMove"in e)}updateGamepadState(e){let t=this.gamepads.get(e.index),a=Array.from(e.buttons).map((d,g)=>{let l=t?.buttons[g],p=d.pressed,c=l?.pressed??!1;return{pressed:p,justPressed:p&&!c,justReleased:!p&&c,value:d.value,touched:d.touched??!1}}),i=Array.from(e.axes),u={id:e.id,index:e.index,connected:e.connected,buttons:a,axes:i,timestamp:e.timestamp,mapping:e.mapping};this.gamepads.set(e.index,u),this.triggerCallbacks(u,t)}triggerCallbacks(e,t){try{e.buttons.forEach((a,i)=>{a.justPressed&&this.gamepadCallbacks.onButtonDown?.(e.index,i,a.value),a.justReleased&&this.gamepadCallbacks.onButtonUp?.(e.index,i)}),e.axes.forEach((a,i)=>{let u=t?.axes[i]??0,d=this.applyDeadzone(a),g=this.applyDeadzone(u);Math.abs(d-g)>k&&this.gamepadCallbacks.onAxisMove?.(e.index,i,d)})}catch(a){this.log("Error in gamepad callbacks:",a)}}handleGamepadConnected(e){try{this.log(`Gamepad connected: ${e.gamepad.id} at index ${e.gamepad.index}`);let t={id:e.gamepad.id,index:e.gamepad.index,connected:!0,buttons:Array.from(e.gamepad.buttons).map(a=>({pressed:a.pressed,justPressed:!1,justReleased:!1,value:a.value,touched:a.touched??!1})),axes:Array.from(e.gamepad.axes),timestamp:e.gamepad.timestamp,mapping:e.gamepad.mapping};this.gamepads.set(e.gamepad.index,t),this.gamepadCallbacks.onGamepadConnected?.(t)}catch(t){this.log("Error handling gamepad connected:",t)}}handleGamepadDisconnected(e){try{this.log(`Gamepad disconnected at index ${e.gamepad.index}`),this.gamepads.delete(e.gamepad.index),this.previousGamepads.delete(e.gamepad.index),this.gamepadCallbacks.onGamepadDisconnected?.(e.gamepad.index)}catch(t){this.log("Error handling gamepad disconnected:",t)}}applyDeadzone(e){let t=this.gamepadConfig.axisDeadzone;return Math.abs(e)<t?0:(e>0?1:-1)*((Math.abs(e)-t)/(1-t))}cloneGamepadState(e){return{...e,buttons:e.buttons.map(t=>({...t})),axes:[...e.axes]}}};m(v,"GamepadInputs");var h=v;function y(o,n){let e=new h(n);return o&&e.setGamepadCallbacks(o),e.start(),e}m(y,"useGamepadInputs");export{b as BaseInputs,N as GamepadInput,h as GamepadInputs,B as InputDeviceType,w as InputEventType,V as Vector2,y as useGamepadInputs};
|