Update controller inputs with useRef

pull/41/head
Spencer Vaughn 2024-03-04 18:23:19 -06:00
parent ba5be77ae5
commit 22f2461cc7
1 changed files with 107 additions and 33 deletions

View File

@ -1,42 +1,115 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';
const GamepadButtons = {
A: 0,
B: 1,
X: 2,
Y: 3,
LB: 4,
RB: 5,
LT: 6,
RT: 7,
start: 8,
select: 9,
L3: 10,
R3: 11,
up: 12,
down: 13,
left: 14,
right: 15,
count: 16
}
const GamepadAxes = {
leftjoy_x: 0, // Steer left/right
leftjoy_y: 1,
rightjoy_x: 2,
rightjoy_y: 3
}
const defaultGamepadInfo = {
connected: false,
buttonA: false,
buttonB: false,
buttonX: false,
buttonY: false,
joystick: [0, 0],
joystickRight: [0, 0],
RB: false,
LB: false,
RT: false,
LT: false,
start: false,
select: false,
up: false,
down: false,
left: false,
right: false
};
const isButtonActive = (activeButtons, button) => Boolean(activeButtons & (1 << button));
export const useGamepad = () => {
const [gamepadInfo, setGamepadInfo] = useState({ connected: false, buttonA: false, buttonB :false, buttonX: false, buttonY:false, joystick: [0, 0], joystickRight : [0,0], RB: false, LB: false, RT: false, LT: false, start: false, select: false, up: false, down: false, left: false, right: false});
const gamepadInfo = useRef(defaultGamepadInfo);
const [, forceUpdate] = useState();
const activeButtonsRef = useRef(0);
// Function to update gamepad state
const updateGamepadState = () => {
const gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
const gamepad = gamepads[0]; // Assuming the first gamepad
const [gamepad] = navigator.getGamepads ? navigator.getGamepads() : []; // Get the first gamepad
// newly read active button bitmask
let newActiveButtons = 0;
if (gamepad) {
const newGamepadInfo = {
connected: true,
buttonA: gamepad.buttons[0].pressed,
buttonB: gamepad.buttons[1].pressed,
buttonX: gamepad.buttons[2].pressed,
buttonY: gamepad.buttons[3].pressed,
joystickRight: [gamepad.axes[2], gamepad.axes[3]],
LT: gamepad.buttons[6].pressed,
RT: gamepad.buttons[7].pressed,
LB: gamepad.buttons[4].pressed,
RB: gamepad.buttons[5].pressed,
// Buttons
for (let i = GamepadButtons.A; i < gamepad.buttons.length; i++) {
// Set the bit for the button if it's pressed
newActiveButtons |= (gamepad.buttons[i].pressed << i);
}
start: gamepad.buttons[9].pressed,
select: gamepad.buttons[8].pressed,
up: gamepad.buttons[12].pressed,
down: gamepad.buttons[13].pressed,
left: gamepad.buttons[14].pressed,
right: gamepad.buttons[15].pressed,
joystick: [gamepad.axes[0], gamepad.axes[1]]
};
const hasJoysticksMoved =
gamepad.axes[GamepadAxes.leftjoy_x] !== gamepadInfo.current.joystick[0] ||
gamepad.axes[GamepadAxes.leftjoy_y] !== gamepadInfo.current.joystick[1] ||
gamepad.axes[GamepadAxes.rightjoy_x] !== gamepadInfo.current.joystickRight[0] ||
gamepad.axes[GamepadAxes.rightjoy_y] !== gamepadInfo.current.joystickRight[1];
// Update state only if there's a change
if (JSON.stringify(newGamepadInfo) !== JSON.stringify(gamepadInfo)) {
setGamepadInfo(newGamepadInfo);
// Update state only if there's a button change or joystick change
if (newActiveButtons !== activeButtonsRef.current || hasJoysticksMoved) {
// Update gamepad info for the current gamepad
gamepadInfo.current = {
connected: true,
buttonA: isButtonActive(newActiveButtons, GamepadButtons.A),
buttonB: isButtonActive(newActiveButtons, GamepadButtons.B),
buttonX: isButtonActive(newActiveButtons, GamepadButtons.X),
buttonY: isButtonActive(newActiveButtons, GamepadButtons.Y),
LB: isButtonActive(newActiveButtons, GamepadButtons.LB),
RB: isButtonActive(newActiveButtons, GamepadButtons.RB),
LT: isButtonActive(newActiveButtons, GamepadButtons.LT),
RT: isButtonActive(newActiveButtons, GamepadButtons.RT),
L3: isButtonActive(newActiveButtons, GamepadButtons.L3),
R3: isButtonActive(newActiveButtons, GamepadButtons.R3),
start: isButtonActive(newActiveButtons, GamepadButtons.start),
select: isButtonActive(newActiveButtons, GamepadButtons.select),
up: isButtonActive(newActiveButtons, GamepadButtons.up),
down: isButtonActive(newActiveButtons, GamepadButtons.down),
left: isButtonActive(newActiveButtons, GamepadButtons.left),
right: isButtonActive(newActiveButtons, GamepadButtons.right),
joystick: [gamepad.axes[0], gamepad.axes[1]],
joystickRight: [gamepad.axes[2], gamepad.axes[3]]
};
// Update the active buttons ref for the next iteration
activeButtonsRef.current = newActiveButtons;
// useGamepad will only cause re-render when a value is changed
forceUpdate({});
}
} else {
// If the gamepad has disconnected, reset the gamepad info
if (gamepadInfo.connected) {
setGamepadInfo({ connected: false, buttonA: false, buttonB :false, buttonX: false, buttonY:false, joystick: [0, 0], joystickRight : [0,0], RB: false, LB: false, RT: false, LT: false, start: false, select: false, up: false, down: false, left: false, right: false});
gamepadInfo.current = defaultGamepadInfo;
forceUpdate({});
}
}
};
@ -46,23 +119,24 @@ export const useGamepad = () => {
console.log('Gamepad connected!');
updateGamepadState();
};
const gamepadDisconnected = () => {
console.log('Gamepad disconnected!');
setGamepadInfo({ connected : false, buttonA: false, buttonB :false, buttonX: false, buttonY:false, joystick: [0, 0], joystickRight : [0,0], RB: false, LB: false, RT: false, LT: false, start: false, select: false, up: false, down: false, left: false, right: false});
gamepadInfo.current = defaultGamepadInfo;
};
window.addEventListener('gamepadconnected', gamepadConnected);
window.addEventListener('gamepaddisconnected', gamepadDisconnected);
const interval = setInterval(updateGamepadState, 100);
// Read gamepad state every frame
const interval = setInterval(updateGamepadState, 1000 / 60);
return () => {
window.removeEventListener('gamepadconnected', gamepadConnected);
window.removeEventListener('gamepaddisconnected', gamepadDisconnected);
clearInterval(interval);
};
}, [gamepadInfo]);
}, []);
return gamepadInfo;
};
return gamepadInfo.current;
};