Files
minecraft-clone/js/player.js
T
minecraft-cloneandClaude 6e8e207afa feat: live settings panel (≈45 Werte, Live/Reload, Persistenz, AO live)
- panel.js: Drawer-Panel (P / ⚙), schema-getrieben, ●live/⟳reload-Badges
- Werte direkt im Panel einstellbar statt in config.js grabben
- Live-Apply über mutateNested (nur Plain-Objekte, nie Modul-Namespace)
  + registerApply-Callbacks für Three.js-Objekte (FOV, Belichtung, Fog, Licht, Schatten-Frustum, Bloom)
- PLAYER.*/CONTROLS.* live (player.js liest jeden Frame neu)
- AO jetzt live: debounced world.markAllDirty() remesht alle Chunks ohne F5
- Reload-Pflichtige Werte persistiert + blinkender Neu-Starten-Button
  (Seed, Meereshöhe, Spawn, MSAA, Tone-Mapping, Schatten an/Größe, AO-Level)
- Persistenz: localStorage (debounced), config.js lädt beim Start
- HUD/Overlay ausgeblendet, solange das Panel offen ist (Welt frei sichtbar)
- Buttons: Neu starten, Auf Standard zurücksetzen, Werte kopieren (JSON)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-21 16:31:25 +02:00

147 lines
5.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// First-Person-Spieler: Bewegung, Gravitation, AABB-Kollision, Springen,
// Flug-Modus, Schwimmen. yaw/pitch steuern die Kamera (extern gesetzt).
//
// WICHTIG: PLAYER- und CONTROLS-Werte werden LIVE aus config.js gelesen
// (jeder Frame), nicht beim Laden destrukturiert. So kann das Settings-
// Panel Geschwindigkeit/Gravitation/Maus-Empfindlichkeit usw. zur
// Laufzeit ändern.
import { isSolid, BLOCK } from './constants.js';
import { PLAYER, CONTROLS } from './config.js';
export class Player {
constructor(world) {
this.world = world;
this.pos = new Float32Array(3); // x,y,z der Fuesse-Mittelpunkt
this.vel = new Float32Array(3);
this.onGround = false;
this.flying = false;
this.yaw = 0;
this.pitch = 0;
}
setSpawn(x, y, z) {
this.pos[0] = x; this.pos[1] = y; this.pos[2] = z;
this.vel[0] = this.vel[1] = this.vel[2] = 0;
}
eyePosition(out) {
out.set(this.pos[0], this.pos[1] + PLAYER.EYE, this.pos[2]);
return out;
}
inWater() {
// Pruefe den Block auf Augen- und Fuss-Hoehe
const ex = Math.floor(this.pos[0]);
const ey = Math.floor(this.pos[1] + PLAYER.EYE * 0.6);
const ez = Math.floor(this.pos[2]);
return this.world.getBlock(ex, ey, ez) === BLOCK.WATER;
}
update(input, dt) {
// --- Maus-Look (Empfindlichkeit live aus config) ---
const sens = CONTROLS.MOUSE_SENSITIVITY;
this.yaw -= input.mouseDX * sens;
this.pitch -= input.mouseDY * sens;
const lim = Math.PI / 2 - 0.001;
if (this.pitch > lim) this.pitch = lim;
if (this.pitch < -lim) this.pitch = -lim;
// --- Flug-Toggle ---
if (input.keys.has('KeyF') && !this._fHeld) {
this._fHeld = true;
this.flying = !this.flying;
this.vel[1] = 0;
}
if (!input.keys.has('KeyF')) this._fHeld = false;
// --- Wunschrichtung im XZ-Plane (yaw=0 -> Blickrichtung ist -Z) ---
const fwdX = -Math.sin(this.yaw);
const fwdZ = -Math.cos(this.yaw);
const rightX = -fwdZ; // = cos(yaw)
const rightZ = fwdX; // = -sin(yaw)
let mx = 0, mz = 0;
if (input.down('KeyW')) { mx += fwdX; mz += fwdZ; }
if (input.down('KeyS')) { mx -= fwdX; mz -= fwdZ; }
if (input.down('KeyD')) { mx += rightX; mz += rightZ; }
if (input.down('KeyA')) { mx -= rightX; mz -= rightZ; }
const len = Math.hypot(mx, mz);
if (len > 0) { mx /= len; mz /= len; }
// Sprint: Shift (Ctrl ist im Browser unbequem – Browser-Shortcuts).
// ShiftLeft/Right dient im Fly-Modus gleichzeitig als "Runter" – kein
// Konflikt, da Sprint nur beim Gehen greift (Fly nutzt konstante FLY_SPEED).
const sprint = input.down('ShiftLeft') || input.down('ShiftRight');
const speed = this.flying ? PLAYER.FLY_SPEED : (sprint ? PLAYER.SPRINT : PLAYER.WALK);
if (this.flying) {
this.vel[0] = mx * speed;
this.vel[2] = mz * speed;
let vy = 0;
if (input.down('Space')) vy += 1;
if (input.down('ShiftLeft')) vy -= 1;
this.vel[1] = vy * speed;
} else {
const water = this.inWater();
const move = water ? speed * 0.5 : speed;
this.vel[0] = mx * move;
this.vel[2] = mz * move;
// Gravitation / Schwimmen
if (water) {
this.vel[1] -= PLAYER.GRAVITY * 0.28 * dt;
if (this.vel[1] < -6) this.vel[1] = -6;
if (input.down('Space')) this.vel[1] = 4.0; // nach oben schwimmen
} else {
this.vel[1] -= PLAYER.GRAVITY * dt;
if (this.vel[1] < -PLAYER.MAX_FALL) this.vel[1] = -PLAYER.MAX_FALL;
if (input.down('Space') && this.onGround) {
this.vel[1] = PLAYER.JUMP_VEL;
this.onGround = false;
}
}
}
// --- Bewegung mit per-Achsen-Kollision ---
this.onGround = false;
this._moveAxis(0, this.vel[0] * dt);
this._moveAxis(2, this.vel[2] * dt);
this._moveAxis(1, this.vel[1] * dt);
}
// axis: 0=x,1=y,2=z ; amount = Verschiebung auf dieser Achse
_moveAxis(axis, amount) {
this.pos[axis] += amount;
if (amount === 0) return;
const HALF = PLAYER.HALF, HEIGHT = PLAYER.HEIGHT;
const x = this.pos[0], y = this.pos[1], z = this.pos[2];
const minX = Math.floor(x - HALF), maxX = Math.floor(x + HALF);
const minY = Math.floor(y), maxY = Math.floor(y + HEIGHT - 0.001);
const minZ = Math.floor(z - HALF), maxZ = Math.floor(z + HALF);
for (let bx = minX; bx <= maxX; bx++) {
for (let by = minY; by <= maxY; by++) {
for (let bz = minZ; bz <= maxZ; bz++) {
if (!isSolid(this.world.getBlock(bx, by, bz))) continue;
if (axis === 0) {
if (amount > 0) this.pos[0] = Math.min(this.pos[0], bx - HALF - 1e-4);
else this.pos[0] = Math.max(this.pos[0], bx + 1 + HALF + 1e-4);
this.vel[0] = 0;
} else if (axis === 2) {
if (amount > 0) this.pos[2] = Math.min(this.pos[2], bz - HALF - 1e-4);
else this.pos[2] = Math.max(this.pos[2], bz + 1 + HALF + 1e-4);
this.vel[2] = 0;
} else {
if (amount > 0) { // nach oben gegen Decke
this.pos[1] = Math.min(this.pos[1], by - HEIGHT - 1e-4);
} else { // gelandet
this.pos[1] = Math.max(this.pos[1], by + 1 + 1e-4);
this.onGround = true;
}
this.vel[1] = 0;
}
}
}
}
}
}