Skip to content

Instantly share code, notes, and snippets.

@jakearchibald
Last active September 6, 2024 21:56
Show Gist options
  • Save jakearchibald/e4a2a63a5b9adf2580d0667c70dcaf82 to your computer and use it in GitHub Desktop.
Save jakearchibald/e4a2a63a5b9adf2580d0667c70dcaf82 to your computer and use it in GitHub Desktop.
export declare function gamepadButtonPress(listeners: {
[button: number]: () => void;
}): Promise<void>;
export async function gamepadButtonPress(listeners) {
const lastStates = [];
while (true) {
const gamepads = [...navigator.getGamepads()].filter(Boolean);
if (gamepads.length === 0) {
await new Promise((resolve) => {
addEventListener('gamepadconnected', () => resolve(), { once: true });
});
continue;
}
await new Promise((r) => requestAnimationFrame(r));
for (const gamepad of gamepads) {
const state = gamepad.buttons.map((button) => button.pressed);
const lastState = lastStates[gamepad.index] || state.map(() => false);
for (const [buttonIndex, callback] of Object.entries(listeners)) {
const wasPressed = lastState[buttonIndex];
const pressed = state[buttonIndex];
if (pressed && !wasPressed) callback();
}
lastStates[gamepad.index] = state;
}
}
}
// Example usage:
gamepadButtonPress({
0: () => presentation.next(),
3: () => presentation.previous(),
});
@Teiem
Copy link

Teiem commented Nov 10, 2021

I would iterate over the listeners since they should be less than the number of buttons.

for (const [index, callback] of Object.entries(listeners)) {
    const wasPressed = lastState[index];
    const pressed = state[index];
    if (pressed && !wasPressed) callback();
}

@jakearchibald
Copy link
Author

jakearchibald commented Nov 10, 2021

@Teiem good point! I guess I was thinking too much about making a generic "buttondown", and forgot that I have access to the listeners.

I updated the code. I guess you could optimise the state storage in a similar way.

@jonnytest1
Copy link

in the example usage you cna also pass the function by reference (though i can understand writing it this way for clarification you are passing functions ^^)

@jakearchibald
Copy link
Author

@jonnytest1 that depends on how the function is written, and whether it depends on a particular this value. https://web.dev/javascript-this/

@luminarious
Copy link

If there was a way to detect button release as well, it would cover 90% of gamedev needs that use d-pad for movement :)

@jakearchibald
Copy link
Author

@luminarious instead of:

if (pressed && !wasPressed) callback();

It would be:

if (wasPressed && !pressed) callback();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment