diff --git a/README.md b/README.md index d012a21..e2d84b0 100644 --- a/README.md +++ b/README.md @@ -249,10 +249,16 @@ ursprünglichen Rechenzeit. ### Rundgang -Bewusst einfach: **auf den Boden klicken** (am Handy tippen), dann läuft man -dorthin; **ziehen** zum Umsehen. Tastatur geht weiterhin (`W A S D`, `Shift`, -`Leertaste` springt). Es gibt keinen Mausfang mehr – der Zeiger bleibt frei -für die Bedienelemente. +Umsehen wie in einem Ego-Shooter: **ein Klick fängt die Maus ein**, danach dreht +schon die blosse Mausbewegung den Blick – Maus nach rechts oben heisst Blick +nach rechts oben. `Esc` gibt die Maus wieder frei. Solange sie frei ist (direkt +nach `Esc`, oder wenn der Mausfang gesperrt ist, etwa in eingebetteten +Vorschauen), dreht **Ziehen** den Blick auf dieselbe Weise. Am Handy wischt man +in die Richtung, in die man schauen will. + +Gehen: `W A S D` oder Pfeiltasten, `Shift` rennt, `Leertaste` springt, am Handy +der Joystick. Ein **Klick auf den Boden** (am Handy tippen) läuft dorthin – mit +gefangener Maus zu dem Punkt unter dem Fadenkreuz. Damit es nicht ruppig wirkt: diff --git a/index.html b/index.html index a6b615c..148e83a 100644 --- a/index.html +++ b/index.html @@ -47,7 +47,7 @@
- Auf den Boden klicken: dorthin gehen · Ziehen: umsehen · WASD gehen · Leertaste springen + Klicken: Maus übernimmt den Blick · WASD gehen · Leertaste springen · Esc Maus freigeben Auf den Boden tippen: dorthin gehen · rechts wischen: umsehen
diff --git a/src/controls/walker.ts b/src/controls/walker.ts index 68a8bfb..6b95b4e 100644 --- a/src/controls/walker.ts +++ b/src/controls/walker.ts @@ -1,11 +1,16 @@ /** * Begehen des Gartens. * - * Bewusst einfach gehalten: - * - Umsehen: mit der Maus ziehen (am Handy rechts wischen). Kein Mausfang, - * der Mauszeiger bleibt frei fuer die Bedienelemente. - * - Gehen: W A S D oder Pfeiltasten, am Handy der Joystick - oder einfach - * auf den Boden klicken bzw. tippen, dann laeuft man dorthin. + * Umsehen wie in einem Ego-Shooter: ein Klick faengt die Maus ein, danach + * dreht schon die blosse Mausbewegung den Blick - nach rechts oben heisst + * nach rechts oben. `Esc` gibt die Maus wieder frei; solange sie frei ist, + * dreht Ziehen mit gedrueckter Taste den Blick auf dieselbe Weise. Am Handy + * wischt man in die Richtung, in die man schauen will. + * + * Weiter: + * - Gehen: W A S D oder Pfeiltasten, am Handy der Joystick - oder auf den + * Boden klicken bzw. tippen, dann laeuft man dorthin. Mit gefangener Maus + * gilt der Klick dem Punkt unter dem Fadenkreuz. * - Leertaste springt. * * Damit es sich nicht ruppig anfuehlt, beschleunigt und bremst der Spieler @@ -62,12 +67,15 @@ export class Walker { private joystick = { x: 0, y: 0, active: false }; private lookDelta = { x: 0, y: 0 }; private enabled = true; + private gefangen = false; /** Ziel fuer Klick-zum-Gehen. */ private target: { x: number; z: number } | null = null; private stuck = 0; /** Wird bei jedem neuen Gehziel aufgerufen (null = kein Ziel mehr). */ onTarget?: (t: { x: number; z: number } | null) => void; + /** Meldet, ob die Maus gerade gefangen ist (fuer Fadenkreuz und Hinweis). */ + onPointerLockChange?: (gefangen: boolean) => void; constructor(camera: PerspectiveCamera, options: WalkerOptions) { this.camera = camera; @@ -84,7 +92,10 @@ export class Walker { setEnabled(on: boolean) { this.enabled = on; - if (!on) this.setTarget(null); + if (!on) { + this.setTarget(null); + if (document.pointerLockElement) document.exitPointerLock(); + } } teleport(x: number, z: number, yawDeg: number) { @@ -113,34 +124,61 @@ export class Walker { window.addEventListener('keyup', (e) => this.keys.delete(e.code)); window.addEventListener('blur', () => this.keys.clear()); - // Maus: ziehen = umsehen, kurzer Klick = dorthin gehen. + // Maus: ein Klick faengt sie ein, danach dreht die Bewegung den Blick. + // Ohne Mausfang (etwa direkt nach Esc oder in eingebetteten Vorschauen) + // dreht Ziehen mit gedrueckter Taste genauso. + const drehen = (dx: number, dy: number, staerke: number) => { + // Nach rechts bewegen dreht nach rechts, nach oben hebt den Blick. + this.lookDelta.x -= dx * staerke; + this.lookDelta.y -= dy * staerke; + }; + + document.addEventListener('pointerlockchange', () => { + this.gefangen = document.pointerLockElement === dom; + dom.style.cursor = this.gefangen ? 'none' : ''; + this.onPointerLockChange?.(this.gefangen); + }); + document.addEventListener('mousemove', (e) => { + if (!this.gefangen || !this.enabled) return; + drehen(e.movementX, e.movementY, 0.0024); + }); + let down: { x: number; y: number; id: number } | null = null; let dragged = false; dom.addEventListener('pointerdown', (e) => { if (!this.enabled || e.pointerType !== 'mouse' || e.button !== 0) return; + if (this.gefangen) { down = { x: 0, y: 0, id: e.pointerId }; dragged = false; return; } down = { x: e.clientX, y: e.clientY, id: e.pointerId }; dragged = false; dom.setPointerCapture(e.pointerId); }); dom.addEventListener('pointermove', (e) => { - if (!down || e.pointerId !== down.id) return; + if (this.gefangen || !down || e.pointerId !== down.id) return; if (!dragged && Math.hypot(e.clientX - down.x, e.clientY - down.y) > 5) dragged = true; if (dragged) { - this.lookDelta.x += e.movementX * 0.0042; - this.lookDelta.y += e.movementY * 0.0042; + drehen(e.movementX, e.movementY, 0.0032); dom.style.cursor = 'grabbing'; } }); const up = (e: PointerEvent) => { if (!down || e.pointerId !== down.id) return; - if (!dragged) this.walkTo(e.clientX, e.clientY); down = null; - dom.style.cursor = ''; + dom.style.cursor = this.gefangen ? 'none' : ''; + if (dragged) return; + if (this.gefangen) { + // Mit gefangener Maus zielt das Fadenkreuz in der Bildmitte. + this.walkTo(window.innerWidth / 2, window.innerHeight / 2); + } else if (!isTouch()) { + // In eingebetteten Vorschauen ist der Mausfang gesperrt - dann bleibt + // es beim Ziehen, und der Klick fuehrt zum angeklickten Punkt. + Promise.resolve(dom.requestPointerLock()).catch(() => this.walkTo(e.clientX, e.clientY)); + } }; dom.addEventListener('pointerup', up); - dom.addEventListener('pointercancel', () => { down = null; dom.style.cursor = ''; }); + dom.addEventListener('pointercancel', () => { down = null; dom.style.cursor = this.gefangen ? 'none' : ''; }); - // Touch: rechts wischen = umsehen, tippen = dorthin gehen + // Touch: rechts wischen = umsehen (in die Richtung, in die man schaut), + // tippen = dorthin gehen let touchId: number | null = null; let last = { x: 0, y: 0 }; let start = { x: 0, y: 0, t: 0 }; @@ -158,8 +196,7 @@ export class Walker { if (!this.enabled) { touchId = null; return; } for (const t of Array.from(e.changedTouches)) { if (t.identifier !== touchId) continue; - this.lookDelta.x += (t.clientX - last.x) * 0.006; - this.lookDelta.y += (t.clientY - last.y) * 0.006; + drehen(t.clientX - last.x, t.clientY - last.y, 0.006); last = { x: t.clientX, y: t.clientY }; } }, { passive: true }); @@ -190,8 +227,8 @@ export class Walker { update(dt: number) { if (!this.enabled) return; - // Blick: man zieht die Szene - wie in der Uebersicht und bei Street View. - // Nach rechts ziehen dreht den Blick nach links, nach unten ziehen hebt ihn. + // Blick: die Richtung wird in `drehen` gesetzt - Maus oder Finger nach + // rechts dreht nach rechts, nach oben hebt den Blick. this.euler.y += this.lookDelta.x; this.euler.x += this.lookDelta.y; this.euler.x = Math.max(-1.3, Math.min(1.3, this.euler.x)); diff --git a/src/main.ts b/src/main.ts index ffca5f7..a60d0d5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -300,6 +300,7 @@ async function main() { marke.visible = false; marke.renderOrder = 5; scene.add(marke); + walker.onPointerLockChange = (gefangen) => hud.setPointerLocked(gefangen); walker.onTarget = (ziel) => { marke.visible = !!ziel; if (ziel) marke.position.set(ziel.x, walkHeight(ziel.x, ziel.z) + 0.03, ziel.z); diff --git a/src/ui/hud.ts b/src/ui/hud.ts index 84763dd..c956186 100644 --- a/src/ui/hud.ts +++ b/src/ui/hud.ts @@ -171,8 +171,11 @@ export function setupHud(opts: HudOptions): Hud {

Bedienung