diff --git a/main.js b/main.js index fc273ca..1ea815b 100644 --- a/main.js +++ b/main.js @@ -12,7 +12,7 @@ function bound(low, x, high) { return Math.max(low, Math.min(x, high)); } -class Input { +class SnesInput { constructor() { this.up = false; this.down = false; @@ -26,72 +26,21 @@ class Input { this.r = false; this.select = false; this.start = false; - - this.keysPressed = {}; - - window.addEventListener('gamepadconnected', this.gamepadConnected); - window.addEventListener('gamepaddisconnected', this.gamepadDisconnected); - document.addEventListener('keydown', (e) => this.keyDown(e)); - document.addEventListener('keyup', (e) => this.keyUp(e)); - } - - keyDown(e) { - this.keysPressed[e.key] = true; - } - - keyUp(e) { - this.keysPressed[e.key] = false; - } - - update() { - // Default ZSNES keybindings. See: - // http://zsnes-docs.sourceforge.net/html/readme.htm#default_keys - this.up = this.keysPressed['ArrowUp']; - this.down = this.keysPressed['ArrowDown']; - this.left = this.keysPressed['ArrowLeft']; - this.right = this.keysPressed['ArrowRight']; - this.start = this.keysPressed['Enter']; - this.select = this.keysPressed['Shift']; - this.a = this.keysPressed['x']; - this.b = this.keysPressed['z']; - this.x = this.keysPressed['s']; - this.y = this.keysPressed['a']; - this.l = this.keysPressed['d']; - this.r = this.keysPressed['c']; - - const gamepad = navigator.getGamepads()[0]; - if (gamepad == null || !gamepad.connected || gamepad.axes.length < 2 || - gamepad.buttons.length < 12) { - return; - } - // TODO: have a config screen instead of hard-coding the 8Bitdo SNES30 pad. - // TODO: handle connects / disconnects more correctly. - this.up |= gamepad.axes[1] < 0; - this.down |= gamepad.axes[1] > 0; - this.left |= gamepad.axes[0] < 0; - this.right |= gamepad.axes[0] > 0; - this.a |= gamepad.buttons[0].pressed; - this.b |= gamepad.buttons[1].pressed; - this.x |= gamepad.buttons[3].pressed; - this.y |= gamepad.buttons[4].pressed; - this.l |= gamepad.buttons[6].pressed; - this.r |= gamepad.buttons[7].pressed; - this.select |= gamepad.buttons[10].pressed; - this.start |= gamepad.buttons[11].pressed; - debug(this.toString()); - } - - gamepadConnected(e) { - debug('gamepad connected! :)'); - console.log('gamepad connected @ index %d: %d buttons, %d axes\n[%s]', - e.gamepad.index, e.gamepad.buttons.length, e.gamepad.axes.length, - e.gamepad.id); } - gamepadDisconnected(e) { - debug('gamepad disconnected :('); - console.log('gamepad disconnected @ index %d:\n[%s]', e.gamepad.index, - e.gamepad.id); + copyFrom(other) { + this.up = other.up; + this.down = other.down; + this.left = other.left; + this.right = other.right; + this.a = other.a; + this.b = other.b; + this.x = other.x; + this.y = other.y; + this.l = other.l; + this.r = other.r; + this.select = other.select; + this.start = other.start; } toString() { @@ -143,6 +92,89 @@ class Input { } } +class InputHandler { + constructor() { + this.up = false; + this.down = false; + this.left = false; + this.right = false; + this.a = false; + this.b = false; + this.x = false; + this.y = false; + this.l = false; + this.r = false; + this.select = false; + this.start = false; + + this.keysPressed = {}; + + window.addEventListener('gamepadconnected', this.gamepadConnected); + window.addEventListener('gamepaddisconnected', this.gamepadDisconnected); + document.addEventListener('keydown', (e) => this.keyDown(e)); + document.addEventListener('keyup', (e) => this.keyUp(e)); + } + + keyDown(e) { + this.keysPressed[e.key] = true; + } + + keyUp(e) { + this.keysPressed[e.key] = false; + } + + update(input) { + // Default ZSNES keybindings. See: + // http://zsnes-docs.sourceforge.net/html/readme.htm#default_keys + input.up = this.keysPressed['ArrowUp']; + input.down = this.keysPressed['ArrowDown']; + input.left = this.keysPressed['ArrowLeft']; + input.right = this.keysPressed['ArrowRight']; + input.start = this.keysPressed['Enter']; + input.select = this.keysPressed['Shift']; + input.a = this.keysPressed['x']; + input.b = this.keysPressed['z']; + input.x = this.keysPressed['s']; + input.y = this.keysPressed['a']; + input.l = this.keysPressed['d']; + input.r = this.keysPressed['c']; + + const gamepad = navigator.getGamepads()[0]; + if (gamepad == null || !gamepad.connected || gamepad.axes.length < 2 || + gamepad.buttons.length < 12) { + return; + } + // TODO: have a config screen instead of hard-coding the 8Bitdo SNES30 pad. + // TODO: handle connects / disconnects more correctly. + input.up |= gamepad.axes[1] < 0; + input.down |= gamepad.axes[1] > 0; + input.left |= gamepad.axes[0] < 0; + input.right |= gamepad.axes[0] > 0; + input.a |= gamepad.buttons[0].pressed; + input.b |= gamepad.buttons[1].pressed; + input.x |= gamepad.buttons[3].pressed; + input.y |= gamepad.buttons[4].pressed; + input.l |= gamepad.buttons[6].pressed; + input.r |= gamepad.buttons[7].pressed; + input.select |= gamepad.buttons[10].pressed; + input.start |= gamepad.buttons[11].pressed; + debug(input.toString()); + } + + gamepadConnected(e) { + debug('gamepad connected! :)'); + console.log('gamepad connected @ index %d: %d buttons, %d axes\n[%s]', + e.gamepad.index, e.gamepad.buttons.length, e.gamepad.axes.length, + e.gamepad.id); + } + + gamepadDisconnected(e) { + debug('gamepad disconnected :('); + console.log('gamepad disconnected @ index %d:\n[%s]', e.gamepad.index, + e.gamepad.id); + } +} + class Graphics { constructor(canvas) { this.canvas_ = canvas; @@ -223,7 +255,9 @@ class World { constructor() { this.state_ = null; this.fpsCounter_ = new FpsCounter(); - this.input_ = new Input(); + this.inputHandler_ = new InputHandler(); + this.input_ = new SnesInput(); + this.lastInput_ = new SnesInput(); this.player_ = new Player(); // TODO: move rendering stuff to a separate object. @@ -235,9 +269,13 @@ class World { update(timestampMs) { this.fpsCounter_.update(timestampMs); - const wasRPressed = this.input_.r; - this.input_.update(); - if (!wasRPressed && this.input_.r) { + + // We copy values to avoid allocating new SnesInput objects every frame. + // TODO: is this actually worth it? + this.lastInput_.copyFrom(this.input_); + this.inputHandler_.update(this.input_); + + if (!this.lastInput_.r && this.input_.r) { this.player_.cycleSprite(); } if (this.input_.left) {