Spaces:
Running
Running
import { Camera } from "../cameras/Camera"; | |
import { Quaternion } from "../math/Quaternion"; | |
import { Matrix3 } from "../math/Matrix3"; | |
import { Vector3 } from "../math/Vector3"; | |
class FPSControls { | |
moveSpeed: number = 1.5; | |
lookSpeed: number = 0.7; | |
dampening: number = 0.5; | |
update: () => void; | |
dispose: () => void; | |
constructor(camera: Camera, canvas: HTMLCanvasElement) { | |
const keys: { [key: string]: boolean } = {}; | |
let pitch = camera.rotation.toEuler().x; | |
let yaw = camera.rotation.toEuler().y; | |
let targetPosition = camera.position; | |
let pointerLock = false; | |
const onMouseDown = () => { | |
canvas.requestPointerLock(); | |
}; | |
const onPointerLockChange = () => { | |
pointerLock = document.pointerLockElement === canvas; | |
if (pointerLock) { | |
canvas.addEventListener("mousemove", onMouseMove); | |
} else { | |
canvas.removeEventListener("mousemove", onMouseMove); | |
} | |
}; | |
const onMouseMove = (e: MouseEvent) => { | |
const mouseX = e.movementX; | |
const mouseY = e.movementY; | |
yaw += mouseX * this.lookSpeed * 0.001; | |
pitch -= mouseY * this.lookSpeed * 0.001; | |
pitch = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, pitch)); | |
}; | |
const onKeyDown = (e: KeyboardEvent) => { | |
keys[e.code] = true; | |
// Map arrow keys to WASD keys | |
if (e.code === "ArrowUp") keys["KeyW"] = true; | |
if (e.code === "ArrowDown") keys["KeyS"] = true; | |
if (e.code === "ArrowLeft") keys["KeyA"] = true; | |
if (e.code === "ArrowRight") keys["KeyD"] = true; | |
}; | |
const onKeyUp = (e: KeyboardEvent) => { | |
keys[e.code] = false; | |
// Map arrow keys to WASD keys | |
if (e.code === "ArrowUp") keys["KeyW"] = false; | |
if (e.code === "ArrowDown") keys["KeyS"] = false; | |
if (e.code === "ArrowLeft") keys["KeyA"] = false; | |
if (e.code === "ArrowRight") keys["KeyD"] = false; | |
if (e.code === "Escape") document.exitPointerLock(); | |
}; | |
this.update = () => { | |
const R = Matrix3.RotationFromQuaternion(camera.rotation).buffer; | |
const forward = new Vector3(-R[2], -R[5], -R[8]); | |
const right = new Vector3(R[0], R[3], R[6]); | |
let move = new Vector3(0, 0, 0); | |
if (keys["KeyS"]) { | |
move = move.add(forward); | |
} | |
if (keys["KeyW"]) { | |
move = move.subtract(forward); | |
} | |
if (keys["KeyA"]) { | |
move = move.subtract(right); | |
} | |
if (keys["KeyD"]) { | |
move = move.add(right); | |
} | |
move = new Vector3(move.x, 0, move.z); | |
if (move.magnitude() > 0) { | |
move = move.normalize(); | |
} | |
targetPosition = targetPosition.add(move.multiply(this.moveSpeed * 0.01)); | |
camera.position = camera.position.add(targetPosition.subtract(camera.position).multiply(this.dampening)); | |
camera.rotation = Quaternion.FromEuler(new Vector3(pitch, yaw, 0)); | |
}; | |
const preventDefault = (e: Event) => { | |
e.preventDefault(); | |
e.stopPropagation(); | |
}; | |
this.dispose = () => { | |
canvas.removeEventListener("dragenter", preventDefault); | |
canvas.removeEventListener("dragover", preventDefault); | |
canvas.removeEventListener("dragleave", preventDefault); | |
canvas.removeEventListener("contextmenu", preventDefault); | |
canvas.removeEventListener("mousedown", onMouseDown); | |
document.removeEventListener("pointerlockchange", onPointerLockChange); | |
window.removeEventListener("keydown", onKeyDown); | |
window.removeEventListener("keyup", onKeyUp); | |
}; | |
window.addEventListener("keydown", onKeyDown); | |
window.addEventListener("keyup", onKeyUp); | |
canvas.addEventListener("dragenter", preventDefault); | |
canvas.addEventListener("dragover", preventDefault); | |
canvas.addEventListener("dragleave", preventDefault); | |
canvas.addEventListener("contextmenu", preventDefault); | |
canvas.addEventListener("mousedown", onMouseDown); | |
document.addEventListener("pointerlockchange", onPointerLockChange); | |
this.update(); | |
} | |
} | |
export { FPSControls }; | |