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
- - Rundgang: auf den Boden klicken, dann läuft man dorthin. Mit der Maus ziehen
- zum Umsehen.
+ - Rundgang: einmal klicken, dann übernimmt die Maus den Blick – Maus nach
+ rechts oben heisst Blick nach rechts oben, wie im Spiel. Esc gibt die
+ Maus wieder frei.
+ - Ein Klick mit gefangener Maus läuft zu dem Punkt unter dem Fadenkreuz; ohne
+ Mausfang zu dem angeklickten Punkt.
- Oder mit W A S D gehen, Shift zum Rennen,
Leertaste zum Springen
- Treppen, Stufen und Sitzmauern sind begehbar. Das Gartentor öffnet sich,