2023-06-25 03:18:12 +00:00
|
|
|
class GamepadHandler {
|
|
|
|
gamepads;
|
|
|
|
timeout;
|
|
|
|
listeners;
|
|
|
|
constructor() {
|
2023-09-02 10:34:24 +00:00
|
|
|
this.buttonLabels = {
|
|
|
|
0: 'BUTTON_1',
|
|
|
|
1: 'BUTTON_2',
|
|
|
|
2: 'BUTTON_3',
|
|
|
|
3: 'BUTTON_4',
|
2023-09-02 10:42:00 +00:00
|
|
|
4: 'LEFT_TOP_SHOULDER',
|
|
|
|
5: 'RIGHT_TOP_SHOULDER',
|
|
|
|
6: 'LEFT_BOTTOM_SHOULDER',
|
|
|
|
7: 'RIGHT_BOTTOM_SHOULDER',
|
2023-09-02 10:34:24 +00:00
|
|
|
8: 'SELECT',
|
|
|
|
9: 'START',
|
|
|
|
10: 'LEFT_STICK',
|
|
|
|
11: 'RIGHT_STICK',
|
|
|
|
12: 'DPAD_UP',
|
|
|
|
13: 'DPAD_DOWN',
|
|
|
|
14: 'DPAD_LEFT',
|
|
|
|
15: 'DPAD_RIGHT',
|
|
|
|
};
|
2023-06-25 03:18:12 +00:00
|
|
|
this.gamepads = [];
|
|
|
|
this.listeners = {};
|
|
|
|
this.timeout = null;
|
|
|
|
this.loop();
|
|
|
|
}
|
|
|
|
terminate() {
|
|
|
|
window.clearTimeout(this.timeout);
|
|
|
|
}
|
|
|
|
getGamepads() {
|
|
|
|
return navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
|
|
|
|
}
|
|
|
|
loop() {
|
|
|
|
this.updateGamepadState();
|
|
|
|
this.timeout = setTimeout(this.loop.bind(this), 10);
|
|
|
|
}
|
|
|
|
updateGamepadState() {
|
|
|
|
const gamepads = this.getGamepads();
|
|
|
|
gamepads.forEach((gamepad, index) => {
|
|
|
|
if (!gamepad) return;
|
|
|
|
let hasGamepad = false;
|
|
|
|
this.gamepads.forEach((oldGamepad, oldIndex) => {
|
|
|
|
if (oldGamepad.index !== gamepad.index) return;
|
2023-07-11 15:48:18 +00:00
|
|
|
const gamepadToSave = {
|
|
|
|
axes: [],
|
|
|
|
buttons: {},
|
2023-08-17 00:21:05 +00:00
|
|
|
index: oldGamepad.index,
|
|
|
|
id: oldGamepad.id
|
2023-07-11 15:48:18 +00:00
|
|
|
}
|
2023-06-25 03:18:12 +00:00
|
|
|
hasGamepad = true;
|
|
|
|
|
|
|
|
oldGamepad.axes.forEach((axis, axisIndex) => {
|
2023-07-28 14:16:50 +00:00
|
|
|
const val = (axis < 0.01 && axis > -0.01) ? 0 : axis;
|
|
|
|
const newVal = (gamepad.axes[axisIndex] < 0.01 && gamepad.axes[axisIndex] > -0.01) ? 0 : gamepad.axes[axisIndex];
|
|
|
|
if (newVal !== val) {
|
2023-06-25 03:18:12 +00:00
|
|
|
const axis = ['LEFT_STICK_X', 'LEFT_STICK_Y', 'RIGHT_STICK_X', 'RIGHT_STICK_Y'][axisIndex];
|
|
|
|
if (!axis) return;
|
2023-09-02 10:34:24 +00:00
|
|
|
this.dispatchEvent('axischanged', {
|
|
|
|
axis: axis,
|
|
|
|
value: newVal,
|
|
|
|
index: gamepad.index,
|
|
|
|
label: this.getAxisLabel(axis, newVal),
|
|
|
|
gamepadIndex: gamepad.index,
|
|
|
|
});
|
2023-06-25 03:18:12 +00:00
|
|
|
}
|
2023-07-28 14:16:50 +00:00
|
|
|
gamepadToSave.axes[axisIndex] = newVal;
|
2023-06-25 03:18:12 +00:00
|
|
|
})
|
2023-07-28 14:16:50 +00:00
|
|
|
|
2023-06-25 03:18:12 +00:00
|
|
|
gamepad.buttons.forEach((button, buttonIndex) => {
|
|
|
|
let pressed = oldGamepad.buttons[buttonIndex] === 1.0;
|
|
|
|
if (typeof oldGamepad.buttons[buttonIndex] === "object") {
|
|
|
|
pressed = oldGamepad.buttons[buttonIndex].pressed;
|
|
|
|
}
|
|
|
|
let pressed2 = button === 1.0;
|
|
|
|
if (typeof button === "object") {
|
|
|
|
pressed2 = button.pressed;
|
|
|
|
}
|
2023-07-11 15:48:18 +00:00
|
|
|
gamepadToSave.buttons[buttonIndex] = {pressed:pressed2};
|
2023-06-25 03:18:12 +00:00
|
|
|
if (pressed !== pressed2) {
|
|
|
|
if (pressed2) {
|
2023-09-02 10:34:24 +00:00
|
|
|
this.dispatchEvent('buttondown', {index: buttonIndex, label: this.getButtonLabel(buttonIndex), gamepadIndex: gamepad.index});
|
2023-06-25 03:18:12 +00:00
|
|
|
} else {
|
2023-09-02 10:34:24 +00:00
|
|
|
this.dispatchEvent('buttonup', {index: buttonIndex, label:this.getButtonLabel(buttonIndex), gamepadIndex: gamepad.index});
|
2023-06-25 03:18:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
2023-07-11 15:48:18 +00:00
|
|
|
this.gamepads[oldIndex] = gamepadToSave;
|
2023-06-25 03:18:12 +00:00
|
|
|
})
|
|
|
|
if (!hasGamepad) {
|
|
|
|
this.gamepads.push(gamepads[index]);
|
|
|
|
this.dispatchEvent('connected', {gamepadIndex: gamepad.index});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
for (let j=0; j<this.gamepads.length; j++) {
|
|
|
|
if (!this.gamepads[j]) continue;
|
|
|
|
let has = false;
|
|
|
|
for (let i=0; i<gamepads.length; i++) {
|
|
|
|
if (!gamepads[i]) continue;
|
|
|
|
if (this.gamepads[j].index === gamepads[i].index) {
|
|
|
|
has = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!has) {
|
|
|
|
this.dispatchEvent('disconnected', {gamepadIndex: this.gamepads[j].index});
|
|
|
|
this.gamepads.splice(j, 1);
|
|
|
|
j--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dispatchEvent(name, arg) {
|
|
|
|
if (typeof this.listeners[name] !== 'function') return;
|
|
|
|
if (!arg) arg={};
|
|
|
|
arg.type = name;
|
|
|
|
this.listeners[name](arg);
|
|
|
|
}
|
|
|
|
on(name, cb) {
|
|
|
|
this.listeners[name.toLowerCase()] = cb;
|
|
|
|
}
|
2023-09-02 10:34:24 +00:00
|
|
|
|
|
|
|
getButtonLabel(index) {
|
|
|
|
if (index === null || index === undefined) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (this.buttonLabels[index] === undefined) {
|
|
|
|
return `GAMEPAD_${index}`;
|
|
|
|
}
|
|
|
|
return this.buttonLabels[index];
|
|
|
|
}
|
|
|
|
getAxisLabel(axis, value) {
|
|
|
|
let valueLabel = null;
|
|
|
|
if (value > 0.5 || value < -0.5) {
|
|
|
|
if (value > 0) {
|
|
|
|
valueLabel = '+1';
|
|
|
|
} else {
|
|
|
|
valueLabel = '-1';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!axis || !valueLabel) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return `${axis}:${valueLabel}`;
|
|
|
|
}
|
2023-06-25 03:18:12 +00:00
|
|
|
}
|
|
|
|
|
2023-07-11 15:48:18 +00:00
|
|
|
window.GamepadHandler = GamepadHandler;
|