111 lines
4.6 KiB
JavaScript
111 lines
4.6 KiB
JavaScript
/*!
|
|
* Tectum
|
|
* Copyright © 2026 Machiel Heinen.
|
|
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
|
* Zie LICENTIE.md.
|
|
*/
|
|
// ============================================================
|
|
// core/walk-controls.js — lopen in de 3D-weergave (first person).
|
|
// W/S of pijl omhoog/omlaag vooruit/achteruit
|
|
// A/D opzij stappen
|
|
// pijl links/rechts draaien
|
|
// Shift rennen
|
|
// slepen met de muis rondkijken
|
|
// Botsen (optioneel) en vloerhoogte komen van de aanroeper:
|
|
// opts.blocked(x, z) -> true als je daar niet kunt staan (m); standaard nooit
|
|
// opts.floorAt(x, z) -> hoogte van de ondergrond (m)
|
|
// opts.eye -> ooghoogte boven de ondergrond (m, standaard 1,65)
|
|
// Plain script (geen ES-module); gebruikt THREE.
|
|
//
|
|
// const walk = createWalkControls(camera, domElement, opts);
|
|
// walk.start({ x, z, yaw }); walk.update(dt); walk.stop();
|
|
// ============================================================
|
|
|
|
function createWalkControls(camera, domElement, opts) {
|
|
const o = Object.assign({ eye: 1.65, speed: 1.68, run: 3.84, turn: Math.PI / 2, blocked: () => false, floorAt: () => 0 }, opts || {});
|
|
const keys = new Set();
|
|
let active = false;
|
|
let x = 0, z = 0, yaw = 0, pitch = 0;
|
|
let drag = null;
|
|
let speedFactor = 1; // bijv. 1,3 in de shooter-modus; reset bij start()
|
|
|
|
const inField = () => ['INPUT', 'SELECT', 'TEXTAREA'].includes(document.activeElement && document.activeElement.tagName);
|
|
const MOVE_KEYS = ['w', 'a', 's', 'd', 'arrowup', 'arrowdown', 'arrowleft', 'arrowright', 'shift'];
|
|
function onKeyDown(e) {
|
|
if (!active || inField() || e.ctrlKey || e.metaKey || e.altKey) return;
|
|
const k = e.key.toLowerCase();
|
|
if (!MOVE_KEYS.includes(k)) return;
|
|
keys.add(k);
|
|
e.preventDefault();
|
|
}
|
|
function onKeyUp(e) { keys.delete(e.key.toLowerCase()); }
|
|
function onDown(e) { if (active && e.button === 0) drag = { x: e.clientX, y: e.clientY }; }
|
|
function onMove(e) {
|
|
if (!active || !drag) return;
|
|
const w = domElement.clientWidth || 1;
|
|
yaw -= (e.clientX - drag.x) / w * Math.PI;
|
|
pitch = Math.max(-1.2, Math.min(1.2, pitch - (e.clientY - drag.y) / w * Math.PI));
|
|
drag = { x: e.clientX, y: e.clientY };
|
|
}
|
|
function onUp() { drag = null; }
|
|
window.addEventListener('keydown', onKeyDown);
|
|
window.addEventListener('keyup', onKeyUp);
|
|
domElement.addEventListener('pointerdown', onDown);
|
|
window.addEventListener('pointermove', onMove);
|
|
window.addEventListener('pointerup', onUp);
|
|
|
|
function apply() {
|
|
camera.position.set(x, o.floorAt(x, z) + o.eye, z);
|
|
// yaw 0 = kijkrichting +Z; pitch > 0 = omhoog.
|
|
const dir = new THREE.Vector3(Math.sin(yaw) * Math.cos(pitch), Math.sin(pitch), Math.cos(yaw) * Math.cos(pitch));
|
|
camera.lookAt(camera.position.clone().add(dir));
|
|
}
|
|
|
|
// Stap met botsen: eerst de hele stap, anders per as (langs een muur glijden).
|
|
function step(dx, dz) {
|
|
if (!o.blocked(x + dx, z + dz)) { x += dx; z += dz; return; }
|
|
if (!o.blocked(x + dx, z)) x += dx;
|
|
else if (!o.blocked(x, z + dz)) z += dz;
|
|
}
|
|
|
|
return {
|
|
get active() { return active; },
|
|
start(pose) {
|
|
x = pose.x; z = pose.z; yaw = pose.yaw || 0; pitch = 0;
|
|
speedFactor = 1;
|
|
keys.clear();
|
|
active = true;
|
|
apply();
|
|
},
|
|
// Rondkijken van buitenaf (bijv. muis gevangen in core/shooter.js).
|
|
set speedFactor(f) { speedFactor = f; },
|
|
look(dYaw, dPitch) {
|
|
yaw += dYaw;
|
|
pitch = Math.max(-1.4, Math.min(1.4, pitch + dPitch));
|
|
},
|
|
stop() { active = false; keys.clear(); drag = null; },
|
|
update(dt) {
|
|
if (!active) return;
|
|
const t = Math.min(dt, 0.1);
|
|
if (keys.has('arrowleft')) yaw += o.turn * t;
|
|
if (keys.has('arrowright')) yaw -= o.turn * t;
|
|
const f = (keys.has('w') || keys.has('arrowup') ? 1 : 0) - (keys.has('s') || keys.has('arrowdown') ? 1 : 0);
|
|
const r = (keys.has('d') ? 1 : 0) - (keys.has('a') ? 1 : 0);
|
|
if (f || r) {
|
|
const v = (keys.has('shift') ? o.run : o.speed) * speedFactor * t / Math.hypot(f, r);
|
|
const fx = Math.sin(yaw), fz = Math.cos(yaw); // vooruit
|
|
const rx = -fz, rz = fx; // rechts (kijkend langs +Z is rechts -X)
|
|
step((fx * f + rx * r) * v, (fz * f + rz * r) * v);
|
|
}
|
|
apply();
|
|
},
|
|
dispose() {
|
|
window.removeEventListener('keydown', onKeyDown);
|
|
window.removeEventListener('keyup', onKeyUp);
|
|
domElement.removeEventListener('pointerdown', onDown);
|
|
window.removeEventListener('pointermove', onMove);
|
|
window.removeEventListener('pointerup', onUp);
|
|
}
|
|
};
|
|
}
|