- Ein Klick faengt die Maus ein, danach dreht die blosse Mausbewegung den Blick. Maus nach rechts oben heisst Blick nach rechts oben. Esc gibt die Maus wieder frei, das Fadenkreuz zeigt den Zustand an. - Ohne Mausfang (direkt nach Esc oder wo er gesperrt ist, etwa in eingebetteten Vorschauen) dreht Ziehen den Blick in derselben Richtung - vorher zog man die Szene, was sich verkehrt anfuehlte. - Am Handy wischt man jetzt ebenfalls in die Blickrichtung statt dagegen. - Mit gefangener Maus fuehrt ein Klick zu dem Punkt unter dem Fadenkreuz, ohne Mausfang weiterhin zum angeklickten Punkt. - Beim Wechsel in die Uebersicht wird der Mausfang freigegeben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -249,10 +249,16 @@ ursprünglichen Rechenzeit.
|
|||||||
|
|
||||||
### Rundgang
|
### Rundgang
|
||||||
|
|
||||||
Bewusst einfach: **auf den Boden klicken** (am Handy tippen), dann läuft man
|
Umsehen wie in einem Ego-Shooter: **ein Klick fängt die Maus ein**, danach dreht
|
||||||
dorthin; **ziehen** zum Umsehen. Tastatur geht weiterhin (`W A S D`, `Shift`,
|
schon die blosse Mausbewegung den Blick – Maus nach rechts oben heisst Blick
|
||||||
`Leertaste` springt). Es gibt keinen Mausfang mehr – der Zeiger bleibt frei
|
nach rechts oben. `Esc` gibt die Maus wieder frei. Solange sie frei ist (direkt
|
||||||
für die Bedienelemente.
|
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:
|
Damit es nicht ruppig wirkt:
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@
|
|||||||
<div id="crosshair"></div>
|
<div id="crosshair"></div>
|
||||||
|
|
||||||
<div id="hint">
|
<div id="hint">
|
||||||
<span class="desktop">Auf den Boden klicken: dorthin gehen · Ziehen: umsehen · <kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> gehen · <kbd>Leertaste</kbd> springen</span>
|
<span class="desktop">Klicken: Maus übernimmt den Blick · <kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> gehen · <kbd>Leertaste</kbd> springen · <kbd>Esc</kbd> Maus freigeben</span>
|
||||||
<span class="touch">Auf den Boden tippen: dorthin gehen · rechts wischen: umsehen</span>
|
<span class="touch">Auf den Boden tippen: dorthin gehen · rechts wischen: umsehen</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+55
-18
@@ -1,11 +1,16 @@
|
|||||||
/**
|
/**
|
||||||
* Begehen des Gartens.
|
* Begehen des Gartens.
|
||||||
*
|
*
|
||||||
* Bewusst einfach gehalten:
|
* Umsehen wie in einem Ego-Shooter: ein Klick faengt die Maus ein, danach
|
||||||
* - Umsehen: mit der Maus ziehen (am Handy rechts wischen). Kein Mausfang,
|
* dreht schon die blosse Mausbewegung den Blick - nach rechts oben heisst
|
||||||
* der Mauszeiger bleibt frei fuer die Bedienelemente.
|
* nach rechts oben. `Esc` gibt die Maus wieder frei; solange sie frei ist,
|
||||||
* - Gehen: W A S D oder Pfeiltasten, am Handy der Joystick - oder einfach
|
* dreht Ziehen mit gedrueckter Taste den Blick auf dieselbe Weise. Am Handy
|
||||||
* auf den Boden klicken bzw. tippen, dann laeuft man dorthin.
|
* 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.
|
* - Leertaste springt.
|
||||||
*
|
*
|
||||||
* Damit es sich nicht ruppig anfuehlt, beschleunigt und bremst der Spieler
|
* 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 joystick = { x: 0, y: 0, active: false };
|
||||||
private lookDelta = { x: 0, y: 0 };
|
private lookDelta = { x: 0, y: 0 };
|
||||||
private enabled = true;
|
private enabled = true;
|
||||||
|
private gefangen = false;
|
||||||
/** Ziel fuer Klick-zum-Gehen. */
|
/** Ziel fuer Klick-zum-Gehen. */
|
||||||
private target: { x: number; z: number } | null = null;
|
private target: { x: number; z: number } | null = null;
|
||||||
private stuck = 0;
|
private stuck = 0;
|
||||||
|
|
||||||
/** Wird bei jedem neuen Gehziel aufgerufen (null = kein Ziel mehr). */
|
/** Wird bei jedem neuen Gehziel aufgerufen (null = kein Ziel mehr). */
|
||||||
onTarget?: (t: { x: number; z: number } | null) => void;
|
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) {
|
constructor(camera: PerspectiveCamera, options: WalkerOptions) {
|
||||||
this.camera = camera;
|
this.camera = camera;
|
||||||
@@ -84,7 +92,10 @@ export class Walker {
|
|||||||
|
|
||||||
setEnabled(on: boolean) {
|
setEnabled(on: boolean) {
|
||||||
this.enabled = on;
|
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) {
|
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('keyup', (e) => this.keys.delete(e.code));
|
||||||
window.addEventListener('blur', () => this.keys.clear());
|
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 down: { x: number; y: number; id: number } | null = null;
|
||||||
let dragged = false;
|
let dragged = false;
|
||||||
dom.addEventListener('pointerdown', (e) => {
|
dom.addEventListener('pointerdown', (e) => {
|
||||||
if (!this.enabled || e.pointerType !== 'mouse' || e.button !== 0) return;
|
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 };
|
down = { x: e.clientX, y: e.clientY, id: e.pointerId };
|
||||||
dragged = false;
|
dragged = false;
|
||||||
dom.setPointerCapture(e.pointerId);
|
dom.setPointerCapture(e.pointerId);
|
||||||
});
|
});
|
||||||
dom.addEventListener('pointermove', (e) => {
|
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 && Math.hypot(e.clientX - down.x, e.clientY - down.y) > 5) dragged = true;
|
||||||
if (dragged) {
|
if (dragged) {
|
||||||
this.lookDelta.x += e.movementX * 0.0042;
|
drehen(e.movementX, e.movementY, 0.0032);
|
||||||
this.lookDelta.y += e.movementY * 0.0042;
|
|
||||||
dom.style.cursor = 'grabbing';
|
dom.style.cursor = 'grabbing';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const up = (e: PointerEvent) => {
|
const up = (e: PointerEvent) => {
|
||||||
if (!down || e.pointerId !== down.id) return;
|
if (!down || e.pointerId !== down.id) return;
|
||||||
if (!dragged) this.walkTo(e.clientX, e.clientY);
|
|
||||||
down = null;
|
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('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 touchId: number | null = null;
|
||||||
let last = { x: 0, y: 0 };
|
let last = { x: 0, y: 0 };
|
||||||
let start = { x: 0, y: 0, t: 0 };
|
let start = { x: 0, y: 0, t: 0 };
|
||||||
@@ -158,8 +196,7 @@ export class Walker {
|
|||||||
if (!this.enabled) { touchId = null; return; }
|
if (!this.enabled) { touchId = null; return; }
|
||||||
for (const t of Array.from(e.changedTouches)) {
|
for (const t of Array.from(e.changedTouches)) {
|
||||||
if (t.identifier !== touchId) continue;
|
if (t.identifier !== touchId) continue;
|
||||||
this.lookDelta.x += (t.clientX - last.x) * 0.006;
|
drehen(t.clientX - last.x, t.clientY - last.y, 0.006);
|
||||||
this.lookDelta.y += (t.clientY - last.y) * 0.006;
|
|
||||||
last = { x: t.clientX, y: t.clientY };
|
last = { x: t.clientX, y: t.clientY };
|
||||||
}
|
}
|
||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
@@ -190,8 +227,8 @@ export class Walker {
|
|||||||
update(dt: number) {
|
update(dt: number) {
|
||||||
if (!this.enabled) return;
|
if (!this.enabled) return;
|
||||||
|
|
||||||
// Blick: man zieht die Szene - wie in der Uebersicht und bei Street View.
|
// Blick: die Richtung wird in `drehen` gesetzt - Maus oder Finger nach
|
||||||
// Nach rechts ziehen dreht den Blick nach links, nach unten ziehen hebt ihn.
|
// rechts dreht nach rechts, nach oben hebt den Blick.
|
||||||
this.euler.y += this.lookDelta.x;
|
this.euler.y += this.lookDelta.x;
|
||||||
this.euler.x += this.lookDelta.y;
|
this.euler.x += this.lookDelta.y;
|
||||||
this.euler.x = Math.max(-1.3, Math.min(1.3, this.euler.x));
|
this.euler.x = Math.max(-1.3, Math.min(1.3, this.euler.x));
|
||||||
|
|||||||
@@ -300,6 +300,7 @@ async function main() {
|
|||||||
marke.visible = false;
|
marke.visible = false;
|
||||||
marke.renderOrder = 5;
|
marke.renderOrder = 5;
|
||||||
scene.add(marke);
|
scene.add(marke);
|
||||||
|
walker.onPointerLockChange = (gefangen) => hud.setPointerLocked(gefangen);
|
||||||
walker.onTarget = (ziel) => {
|
walker.onTarget = (ziel) => {
|
||||||
marke.visible = !!ziel;
|
marke.visible = !!ziel;
|
||||||
if (ziel) marke.position.set(ziel.x, walkHeight(ziel.x, ziel.z) + 0.03, ziel.z);
|
if (ziel) marke.position.set(ziel.x, walkHeight(ziel.x, ziel.z) + 0.03, ziel.z);
|
||||||
|
|||||||
+5
-2
@@ -171,8 +171,11 @@ export function setupHud(opts: HudOptions): Hud {
|
|||||||
|
|
||||||
<h3>Bedienung</h3>
|
<h3>Bedienung</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Rundgang: auf den Boden klicken, dann läuft man dorthin. Mit der Maus ziehen
|
<li>Rundgang: einmal klicken, dann übernimmt die Maus den Blick – Maus nach
|
||||||
zum Umsehen.</li>
|
rechts oben heisst Blick nach rechts oben, wie im Spiel. <kbd>Esc</kbd> gibt die
|
||||||
|
Maus wieder frei.</li>
|
||||||
|
<li>Ein Klick mit gefangener Maus läuft zu dem Punkt unter dem Fadenkreuz; ohne
|
||||||
|
Mausfang zu dem angeklickten Punkt.</li>
|
||||||
<li>Oder mit <kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> gehen, <kbd>Shift</kbd> zum Rennen,
|
<li>Oder mit <kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> gehen, <kbd>Shift</kbd> zum Rennen,
|
||||||
<kbd>Leertaste</kbd> zum Springen</li>
|
<kbd>Leertaste</kbd> zum Springen</li>
|
||||||
<li>Treppen, Stufen und Sitzmauern sind begehbar. Das Gartentor öffnet sich,
|
<li>Treppen, Stufen und Sitzmauern sind begehbar. Das Gartentor öffnet sich,
|
||||||
|
|||||||
Reference in New Issue
Block a user