Spaces:
Running
Running
Download src/input.js from jedisct1/edge-runner: direct link, hf CLI and curl.
- Browser
- Download file 3.24 kB
-
https://huggingface.co/spaces/jedisct1/edge-runner/resolve/main/src/input.js
- Command line
-
hf download hf://spaces/jedisct1/edge-runner/src/input.js
-
curl -L -o input.js https://huggingface.co/spaces/jedisct1/edge-runner/resolve/main/src/input.js
3.24 kB
| // Keyboard and touch input, translated into game intents. | |
| const KEY_MAP = { | |
| ArrowLeft: 'left', | |
| KeyA: 'left', | |
| ArrowRight: 'right', | |
| KeyD: 'right', | |
| ArrowUp: 'jump', | |
| KeyW: 'jump', | |
| Space: 'jump', | |
| ShiftLeft: 'boost', | |
| ShiftRight: 'boost', | |
| KeyE: 'edge', | |
| Enter: 'confirm', | |
| NumpadEnter: 'confirm', | |
| KeyP: 'pause', | |
| Escape: 'pause', | |
| KeyM: 'mute', | |
| }; | |
| export class Input { | |
| constructor(target = window) { | |
| this.target = target; | |
| this.pressed = new Set(); | |
| this.held = new Set(); | |
| this.muted = false; | |
| this.onFirstGesture = null; | |
| this.keydown = (e) => { | |
| const action = KEY_MAP[e.code]; | |
| if (!action) return; | |
| // Stop the page scrolling under the game. | |
| if (e.code === 'Space' || e.code.startsWith('Arrow')) e.preventDefault(); | |
| if (!this.held.has(action)) this.pressed.add(action); | |
| this.held.add(action); | |
| this.#gesture(); | |
| }; | |
| this.keyup = (e) => { | |
| const action = KEY_MAP[e.code]; | |
| if (action) this.held.delete(action); | |
| }; | |
| this.blur = () => this.held.clear(); | |
| this.touchstart = (e) => { | |
| const t = e.changedTouches[0]; | |
| this.touchOrigin = { x: t.clientX, y: t.clientY, time: performance.now() }; | |
| this.#gesture(); | |
| }; | |
| this.touchend = (e) => { | |
| if (!this.touchOrigin) return; | |
| const t = e.changedTouches[0]; | |
| const dx = t.clientX - this.touchOrigin.x; | |
| const dy = t.clientY - this.touchOrigin.y; | |
| const dt = performance.now() - this.touchOrigin.time; | |
| const THRESHOLD = 34; | |
| if (Math.abs(dx) < THRESHOLD && Math.abs(dy) < THRESHOLD) { | |
| if (dt < 400) this.pressed.add('jump'); | |
| } else if (Math.abs(dx) > Math.abs(dy)) { | |
| this.pressed.add(dx > 0 ? 'right' : 'left'); | |
| } else if (dy < 0) { | |
| this.pressed.add('jump'); | |
| } else { | |
| this.pressed.add('edge'); | |
| } | |
| this.touchOrigin = null; | |
| }; | |
| this.pointerdown = () => this.#gesture(); | |
| target.addEventListener('keydown', this.keydown); | |
| target.addEventListener('keyup', this.keyup); | |
| target.addEventListener('blur', this.blur); | |
| target.addEventListener('touchstart', this.touchstart, { passive: true }); | |
| target.addEventListener('touchend', this.touchend, { passive: true }); | |
| target.addEventListener('pointerdown', this.pointerdown); | |
| } | |
| #gesture() { | |
| if (!this.gestured) { | |
| this.gestured = true; | |
| this.onFirstGesture?.(); | |
| } | |
| } | |
| // True once per press. | |
| consume(action) { | |
| if (!this.pressed.has(action)) return false; | |
| this.pressed.delete(action); | |
| return true; | |
| } | |
| isHeld(action) { | |
| return this.held.has(action); | |
| } | |
| // Drop queued presses that were not handled this frame so they cannot fire | |
| // late, for example a jump buffered while the game over screen was up. | |
| endFrame() { | |
| this.pressed.clear(); | |
| } | |
| destroy() { | |
| this.target.removeEventListener('keydown', this.keydown); | |
| this.target.removeEventListener('keyup', this.keyup); | |
| this.target.removeEventListener('blur', this.blur); | |
| this.target.removeEventListener('touchstart', this.touchstart); | |
| this.target.removeEventListener('touchend', this.touchend); | |
| this.target.removeEventListener('pointerdown', this.pointerdown); | |
| } | |
| } | |