|
1 | | -export interface PlayerInput { |
2 | | - left: string; |
3 | | - right: string; |
4 | | - up: string; |
5 | | - down: string; |
6 | | - flip: string; |
7 | | - hold: string; |
| 1 | +import { difference } from "lodash-es"; |
| 2 | + |
| 3 | +export type Input = string; |
| 4 | +type Callback = (input: Input) => void; |
| 5 | + |
| 6 | +// Singleton that unifies keyboard and gamepad inputs |
| 7 | +class InputManager { |
| 8 | + keydownCallbacks: Callback[] = []; |
| 9 | + keyupCallbacks: Callback[] = []; |
| 10 | + |
| 11 | + // All currently pressed keys on gamepad |
| 12 | + gamepadPressed: Input[] = []; |
| 13 | + |
| 14 | + constructor() { |
| 15 | + document.addEventListener("keydown", (e) => { |
| 16 | + for (let cb of this.keydownCallbacks) { |
| 17 | + cb(keyInputs[e.key]); |
| 18 | + } |
| 19 | + }); |
| 20 | + document.addEventListener("keyup", (e) => { |
| 21 | + for (let cb of this.keyupCallbacks) { |
| 22 | + cb(keyInputs[e.key]); |
| 23 | + } |
| 24 | + }); |
| 25 | + } |
| 26 | + |
| 27 | + tick() { |
| 28 | + // On each tick, fire `keydown` events for each newly pressed gamepad button |
| 29 | + // and fire `keyup` events for each released button |
| 30 | + const gamepads = navigator.getGamepads(); |
| 31 | + let newInputs: Input[] = []; |
| 32 | + if (gamepads[0]) { |
| 33 | + newInputs = newInputs.concat( |
| 34 | + this.getGamepadButtons("player1", gamepads[0]) |
| 35 | + ); |
| 36 | + } |
| 37 | + if (gamepads[1]) { |
| 38 | + newInputs = newInputs.concat( |
| 39 | + this.getGamepadButtons("player2", gamepads[1]) |
| 40 | + ); |
| 41 | + } |
| 42 | + for (let input of difference(this.gamepadPressed, newInputs)) { |
| 43 | + for (let cb of this.keyupCallbacks) { |
| 44 | + cb(input); |
| 45 | + } |
| 46 | + } |
| 47 | + for (let input of difference(newInputs, this.gamepadPressed)) { |
| 48 | + for (let cb of this.keydownCallbacks) { |
| 49 | + cb(input); |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + this.gamepadPressed = newInputs; |
| 54 | + } |
| 55 | + |
| 56 | + getGamepadButtons(playerIndex: string, gp: Gamepad) { |
| 57 | + const buttons = []; |
| 58 | + if (gp.axes[0] < -0.5 || gp.buttons[14].pressed) { |
| 59 | + buttons.push(`${playerIndex}.left`); |
| 60 | + } |
| 61 | + if (gp.axes[0] > 0.5 || gp.buttons[15].pressed) { |
| 62 | + buttons.push(`${playerIndex}.right`); |
| 63 | + } |
| 64 | + if (gp.axes[1] < -0.5 || gp.buttons[12].pressed) { |
| 65 | + buttons.push(`${playerIndex}.up`); |
| 66 | + } |
| 67 | + if (gp.axes[1] > 0.5 || gp.buttons[13].pressed) { |
| 68 | + buttons.push(`${playerIndex}.down`); |
| 69 | + } |
| 70 | + if (gp.buttons[0].pressed) { |
| 71 | + buttons.push(`${playerIndex}.flip`); |
| 72 | + } |
| 73 | + if (gp.buttons[1].pressed) { |
| 74 | + buttons.push(`${playerIndex}.hold`); |
| 75 | + } |
| 76 | + return buttons; |
| 77 | + } |
| 78 | + |
| 79 | + addKeydownListener(cb: Callback) { |
| 80 | + this.keydownCallbacks.push(cb); |
| 81 | + } |
| 82 | + |
| 83 | + removeKeydownListener(cb: Callback) { |
| 84 | + const index = this.keydownCallbacks.indexOf(cb); |
| 85 | + if (index >= 0) { |
| 86 | + this.keydownCallbacks.splice(index, 1); |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + addKeyupListener(cb: Callback) { |
| 91 | + this.keyupCallbacks.push(cb); |
| 92 | + } |
| 93 | + |
| 94 | + removeKeyupListener(cb: Callback) { |
| 95 | + const index = this.keydownCallbacks.indexOf(cb); |
| 96 | + if (index >= 0) { |
| 97 | + this.keyupCallbacks.splice(index, 1); |
| 98 | + } |
| 99 | + } |
8 | 100 | } |
9 | | -export const inputs = { |
10 | | - refresh: "r", |
11 | | - pause: "p", |
12 | | - translate: "t", |
13 | | - player1: { |
14 | | - left: "a", |
15 | | - right: "d", |
16 | | - up: "w", |
17 | | - down: "s", |
18 | | - flip: "e", |
19 | | - hold: "q", |
20 | | - }, |
21 | | - player2: { |
22 | | - left: "j", |
23 | | - right: "l", |
24 | | - up: "i", |
25 | | - down: "k", |
26 | | - flip: "o", |
27 | | - hold: "u", |
28 | | - }, |
| 101 | + |
| 102 | +export const inputManager = new InputManager(); |
| 103 | + |
| 104 | +const keyInputs: Record<string, string> = { |
| 105 | + r: "refresh", |
| 106 | + p: "pause", |
| 107 | + t: "translate", |
| 108 | + a: "player1.left", |
| 109 | + d: "player1.right", |
| 110 | + w: "player1.up", |
| 111 | + s: "player1.down", |
| 112 | + e: "player1.flip", |
| 113 | + q: "player1.hold", |
| 114 | + j: "player2.left", |
| 115 | + l: "player2.right", |
| 116 | + i: "player2.up", |
| 117 | + k: "player2.down", |
| 118 | + o: "player2.flip", |
| 119 | + u: "player2.hold", |
29 | 120 | }; |
0 commit comments