UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

225 lines (224 loc) 6.09 kB
import { InputSource } from "../input.js"; import { movementState } from "../utils.js"; const PASSIVE = { passive: false }; const KEY_CODES = { A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9, K: 10, L: 11, M: 12, N: 13, O: 14, P: 15, Q: 16, R: 17, S: 18, T: 19, U: 20, V: 21, W: 22, X: 23, Y: 24, Z: 25, "0": 26, "1": 27, "2": 28, "3": 29, "4": 30, "5": 31, "6": 32, "7": 33, "8": 34, "9": 35, UP: 36, DOWN: 37, LEFT: 38, RIGHT: 39, SPACE: 40, SHIFT: 41, CTRL: 42 }; const KEY_COUNT = Object.keys(KEY_CODES).length; const array = Array(KEY_COUNT).fill(0); class KeyboardMouseSource extends InputSource { _movementState = movementState(); static keyCode = KEY_CODES; _pointerId = -1; _pointerLock; _keyMap = /* @__PURE__ */ new Map(); _keyPrev = Array(KEY_COUNT).fill(0); _keyNow = Array(KEY_COUNT).fill(0); _button = Array(3).fill(0); constructor({ pointerLock = false } = {}) { super({ key: Array(KEY_COUNT).fill(0), button: [0, 0, 0], mouse: [0, 0], wheel: [0] }); this._pointerLock = pointerLock ?? false; const { keyCode } = KeyboardMouseSource; for (let i = 0; i < 26; i++) { const code = `Key${String.fromCharCode("A".charCodeAt(0) + i)}`; this._keyMap.set(code, keyCode.A + i); } for (let i = 0; i < 10; i++) { const code = `Digit${i}`; this._keyMap.set(code, keyCode["0"] + i); } this._keyMap.set("ArrowUp", keyCode.UP); this._keyMap.set("ArrowDown", keyCode.DOWN); this._keyMap.set("ArrowLeft", keyCode.LEFT); this._keyMap.set("ArrowRight", keyCode.RIGHT); this._keyMap.set("Space", keyCode.SPACE); this._keyMap.set("ShiftLeft", keyCode.SHIFT); this._keyMap.set("ShiftRight", keyCode.SHIFT); this._keyMap.set("ControlLeft", keyCode.CTRL); this._keyMap.set("ControlRight", keyCode.CTRL); this._onWheel = this._onWheel.bind(this); this._onPointerDown = this._onPointerDown.bind(this); this._onPointerMove = this._onPointerMove.bind(this); this._onPointerUp = this._onPointerUp.bind(this); this._onContextMenu = this._onContextMenu.bind(this); this._onKeyDown = this._onKeyDown.bind(this); this._onKeyUp = this._onKeyUp.bind(this); } _onWheel(event) { event.preventDefault(); this.deltas.wheel.append([event.deltaY]); } _onPointerDown(event) { this._movementState.down(event); if (event.pointerType !== "mouse") { return; } if (this._pointerLock) { if (document.pointerLockElement !== this._element) { this._element?.requestPointerLock(); } } else { this._element?.setPointerCapture(event.pointerId); } this._clearButtons(); this._button[event.button] = 1; this.deltas.button.append(this._button); if (this._pointerId !== -1) { return; } this._pointerId = event.pointerId; } _onPointerMove(event) { const [movementX, movementY] = this._pointerLock && document.pointerLockElement === this._element ? [event.movementX, event.movementY] : this._movementState.move(event); if (event.pointerType !== "mouse") { return; } if (event.target !== this._element) { return; } if (this._pointerLock) { if (document.pointerLockElement !== this._element) { return; } } else { if (this._pointerId !== event.pointerId) { return; } } this.deltas.mouse.append([movementX, movementY]); } _onPointerUp(event) { this._movementState.up(event); if (event.pointerType !== "mouse") { return; } if (!this._pointerLock) { this._element?.releasePointerCapture(event.pointerId); } this._clearButtons(); this.deltas.button.append(this._button); if (this._pointerId !== event.pointerId) { return; } this._pointerId = -1; } _onContextMenu(event) { event.preventDefault(); } _onKeyDown(event) { if (this._pointerLock && document.pointerLockElement !== this._element) { return; } event.stopPropagation(); this._setKey(event.code, 1); } _onKeyUp(event) { event.stopPropagation(); this._setKey(event.code, 0); } _clearButtons() { for (let i = 0; i < this._button.length; i++) { if (this._button[i] === 1) { this._button[i] = -1; continue; } this._button[i] = 0; } } _setKey(code, value) { if (!this._keyMap.has(code)) { return; } this._keyNow[this._keyMap.get(code) ?? 0] = value; } attach(element) { super.attach(element); this._element = element; this._element.addEventListener("wheel", this._onWheel, PASSIVE); this._element.addEventListener("pointerdown", this._onPointerDown); this._element.addEventListener("pointermove", this._onPointerMove); this._element.addEventListener("pointerup", this._onPointerUp); this._element.addEventListener("pointercancel", this._onPointerUp); this._element.addEventListener("pointerleave", this._onPointerUp); this._element.addEventListener("lostpointercapture", this._onPointerUp); this._element.addEventListener("contextmenu", this._onContextMenu); window.addEventListener("keydown", this._onKeyDown, false); window.addEventListener("keyup", this._onKeyUp, false); } detach() { if (!this._element) { return; } this._element.removeEventListener("wheel", this._onWheel, PASSIVE); this._element.removeEventListener("pointerdown", this._onPointerDown); this._element.removeEventListener("pointermove", this._onPointerMove); this._element.removeEventListener("pointerup", this._onPointerUp); this._element.removeEventListener("pointercancel", this._onPointerUp); this._element.removeEventListener("pointerleave", this._onPointerUp); this._element.removeEventListener("lostpointercapture", this._onPointerUp); this._element.removeEventListener("contextmenu", this._onContextMenu); window.removeEventListener("keydown", this._onKeyDown, false); window.removeEventListener("keyup", this._onKeyUp, false); this._keyNow.fill(0); this._keyPrev.fill(0); super.detach(); } read() { for (let i = 0; i < array.length; i++) { array[i] = this._keyNow[i] - this._keyPrev[i]; this._keyPrev[i] = this._keyNow[i]; } this.deltas.key.append(array); return super.read(); } } export { KeyboardMouseSource };