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>
This commit is contained in:
1 parent
d6f6774815
commit
6e8e207afa
7 files changed
+775
-78
No files matched your search
+111
-59
@@ -7,87 +7,65 @@
|
||||
// constants.js und sollten NICHT geändert werden (sonst
|
||||
// drohen Mesh/Storage-Fehler).
|
||||
//
|
||||
// Nach jeder Änderung: Seite HARD-REFRESHEN (Ctrl+Shift+R),
|
||||
// 👉 Live anpassen: Im Spiel **P** drücken (oder ⚙ oben
|
||||
// rechts). Grafik-Werte greifen sofort, Welt-/Performance-
|
||||
// Werte brauchen einen Neustart (Button im Panel).
|
||||
// Alle Änderungen werden in localStorage gespeichert und
|
||||
// überschreiben diese Defaults beim Laden.
|
||||
//
|
||||
// Nach jeder Direktänderung hier: HARD-REFRESH (Ctrl+Shift+R),
|
||||
// damit der Browser die Module nicht aus dem Cache lädt.
|
||||
// ============================================================
|
||||
|
||||
import * as THREE from 'three';
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 🌍 WELT
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const WORLD_SEED = 1337; // gleicher Seed = gleiche Welt. Beliebige ganze Zahl.
|
||||
export const SEA_LEVEL = 26; // Höhe der Wasseroberfläche (muss < CHUNK_HEIGHT=64 bleiben)
|
||||
// Primitives sind `let`, damit gespeicherte Overrides (localStorage)
|
||||
// sie beim Laden überschreiben können. Objekte bleiben `const` und
|
||||
// werden per Object.assign gemutiert.
|
||||
export let WORLD_SEED = 1337; // gleicher Seed = gleiche Welt. Beliebige ganze Zahl.
|
||||
export let SEA_LEVEL = 26; // Höhe der Wasseroberfläche (muss < CHUNK_HEIGHT=64 bleiben)
|
||||
|
||||
// Spawn-Position (X/Z in Blöcken; Y wird automatisch auf den Boden gesetzt).
|
||||
export const SPAWN = { x: 8, z: 8 };
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// ⚡ PERFORMANCE
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const RENDER_DISTANCE = 6; // Chunk-Radius um den Spieler (höher = weiter sichtbar, mehr Last)
|
||||
export const MAX_OPS_PER_FRAME = 2; // Chunk-Generierung/Meshing pro Frame (höher = lädt schneller, ruckelt eher)
|
||||
export const MAX_PIXEL_RATIO = 2; // Cap der Gerät-Pixelratio (2 = scharf; 1 = schneller auf HiDPI-Screens)
|
||||
export let RENDER_DISTANCE = 6; // Chunk-Radius um den Spieler (höher = weiter sichtbar, mehr Last)
|
||||
export let MAX_OPS_PER_FRAME = 2; // Chunk-Generierung/Meshing pro Frame (höher = lädt schneller, ruckelt eher)
|
||||
export let MAX_PIXEL_RATIO = 2; // Cap der Gerät-Pixelratio (2 = scharf; 1 = schneller auf HiDPI-Screens)
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 🎨 RENDERER & TONE-MAPPING
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const ANTIALIAS = true; // MSAA direkt am Renderer (Kantenglättung ohne Composer)
|
||||
export const MSAA_SAMPLES = 4; // MSAA-Stufen im Post-Processing-Composer (WebGL2; 0 = aus)
|
||||
export const TONE_MAPPING = THREE.ACESFilmicToneMapping; // "Color Grading". Alternativen: THREE.Linear / Reinhard / Cineon
|
||||
export const TONE_MAPPING_EXPOSURE = 1.1; // globale Helligkeit (höher = heller). ACES dunkelt leicht ab.
|
||||
export let ANTIALIAS = true; // MSAA direkt am Renderer (Kantenglättung ohne Composer)
|
||||
export let MSAA_SAMPLES = 4; // MSAA-Stufen im Post-Processing-Composer (WebGL2; 0 = aus)
|
||||
export let TONE_MAPPING = THREE.ACESFilmicToneMapping; // "Color Grading". Alternativen: THREE.Linear / Reinhard / Cineon
|
||||
export let TONE_MAPPING_EXPOSURE = 1.1; // globale Helligkeit (höher = heller). ACES dunkelt leicht ab.
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 🌫️ HIMMEL & NEBEL
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const SKY_COLOR = 0xaaccff; // Hintergrund- und Nebelfarbe
|
||||
export const FOG_NEAR = 64; // Block-Distanz, ab der Nebel einsetzt (≈ (RENDER_DISTANCE-2)*16)
|
||||
export const FOG_FAR = 96; // Block-Distanz, ab der Nebel ganz undurchsichtig ist (≈ RENDER_DISTANCE*16)
|
||||
// Hinweis: bei Änderung von RENDER_DISTANCE FOG_NEAR/FOG_FAR passend nachziehen.
|
||||
export let SKY_COLOR = 0xaaccff; // Hintergrund- und Nebelfarbe
|
||||
export let FOG_NEAR = 64; // Block-Distanz, ab der Nebel einsetzt (≈ (RENDER_DISTANCE-2)*16)
|
||||
export let FOG_FAR = 96; // Block-Distanz, ab der Nebel ganz undurchsichtig ist (≈ RENDER_DISTANCE*16)
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 📷 KAMERA
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const CAMERA_FOV = 72; // Sichtfeld in Grad (höher = weiterer Blickwinkel / Fisheye)
|
||||
export const CAMERA_NEAR = 0.1;
|
||||
export const CAMERA_FAR = 1000;
|
||||
export let CAMERA_FOV = 72; // Sichtfeld in Grad (höher = weiterer Blickwinkel / Fisheye)
|
||||
export let CAMERA_NEAR = 0.1;
|
||||
export let CAMERA_FAR = 1000;
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 💡 BELEUCHTUNG
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const HEMISPHERE = { sky: 0xbfdfff, ground: 0x5a6b3a, intensity: 1.05 }; // weiche Aufhellung von oben/unten
|
||||
export const SUN = { color: 0xfff4d6, intensity: 1.2 }; // gerichtetes Hauptlicht
|
||||
// Sonnenrichtung (wird intern normalisiert). Bestimmt, aus welcher Seite Licht/Schatten kommen.
|
||||
export const SUN_DIRECTION = [0.55, 0.72, 0.42];
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 🌑 SCHATTEN
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const SHADOWS_ENABLED = true;
|
||||
export const SHADOW_MAP_SIZE = 2048; // Schatten-Textur-Auflösung (höher = schärfer + mehr Last; 1024/2048 typisch)
|
||||
export const SHADOW_AREA = 44; // halbe Kantenlänge des Schatten-Frustums um den Spieler (Blöcke)
|
||||
export const SHADOW_BIAS = 0.0002; // gegen "Shadow Acne" (strichartige Selbstschatten)
|
||||
export const SHADOW_NORMAL_BIAS = 0.02; // gegen Acne auf Voxelflächen (in Normalenrichtung verschieben)
|
||||
export let SHADOWS_ENABLED = true;
|
||||
export let SHADOW_MAP_SIZE = 2048; // Schatten-Textur-Auflösung (höher = schärfer + mehr Last; 1024/2048 typisch)
|
||||
export let SHADOW_AREA = 44; // halbe Kantenlänge des Schatten-Frustums um den Spieler (Blöcke)
|
||||
export let SHADOW_BIAS = 0.0002; // gegen "Shadow Acne" (strichartige Selbstschatten)
|
||||
export let SHADOW_NORMAL_BIAS = 0.02; // gegen Acne auf Voxelflächen (in Normalenrichtung verschieben)
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// ✨ POST-PROCESSING (Bloom / Glanz)
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const BLOOM_ENABLED = true;
|
||||
export let BLOOM_ENABLED = true;
|
||||
export const BLOOM = { strength: 0.4, radius: 0.5, threshold: 0.55 };
|
||||
// strength = Glanz-Stärke (höher = stärkeres Leuchten)
|
||||
// radius = Ausbreitung des Glanzes
|
||||
// threshold = nur Pixel HELLER als dieser Wert glühen (niedriger = mehr glüht)
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 🔲 AMBIENT OCCLUSION (Eck-Verdunkelung = "Smooth Lighting")
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// AO-Stufe pro Vertex: Index 0 = stärkste Verdeckung (dunkelste Ecke),
|
||||
// 3 = keine Verdeckung. Niedrigere Werte = dunklere Ecken/Kanten.
|
||||
export const AO_LEVEL = [0.2, 0.4, 0.5, 1.8];
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 🚶 SPIELER (Physik & Dimensionen)
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const PLAYER = {
|
||||
HALF: 0.3, // halbe Spielerbreite (X/Z) — AABB-Kollisionskasten
|
||||
HEIGHT: 1.8, // Körperhöhe
|
||||
@@ -100,12 +78,86 @@ export const PLAYER = {
|
||||
MAX_FALL: 50, // max. Fallgeschwindigkeit (Terminal-Speed)
|
||||
};
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 🖱️ STEUERUNG
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const MOUSE_SENSITIVITY = 0.0022; // Maus-Look-Empfindlichkeit (höher = schnellere Drehung)
|
||||
// Steuerungs-Werte (live änderbar). Zusammen im Objekt, damit das Panel
|
||||
// die Properties mutieren kann und Spieler/Raycast sie live sehen.
|
||||
export const CONTROLS = {
|
||||
MOUSE_SENSITIVITY: 0.0022, // Maus-Look-Empfindlichkeit (höher = schnellere Drehung)
|
||||
REACH: 6, // Reichweite für Abbauen/Bauen (Blöcke)
|
||||
};
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 🎯 GAMEPLAY
|
||||
// Defaults-Snapshot (für "Zurücksetzen" im Panel)
|
||||
// ────────────────────────────────────────────────────────────
|
||||
export const REACH = 6; // Reichweite für Abbauen/Bauen (Blöcke)
|
||||
// Wird VOR dem Anwenden gespeicherter Overrides erzeugt und
|
||||
// enthält die ursprünglichen Code-Defaults (als tiefe Kopie).
|
||||
const DEFAULTS = JSON.parse(JSON.stringify({
|
||||
WORLD_SEED, SEA_LEVEL, SPAWN,
|
||||
RENDER_DISTANCE, MAX_OPS_PER_FRAME, MAX_PIXEL_RATIO,
|
||||
ANTIALIAS, MSAA_SAMPLES, TONE_MAPPING, TONE_MAPPING_EXPOSURE,
|
||||
SKY_COLOR, FOG_NEAR, FOG_FAR,
|
||||
CAMERA_FOV, CAMERA_NEAR, CAMERA_FAR,
|
||||
HEMISPHERE, SUN, SUN_DIRECTION,
|
||||
SHADOWS_ENABLED, SHADOW_MAP_SIZE, SHADOW_AREA, SHADOW_BIAS, SHADOW_NORMAL_BIAS,
|
||||
BLOOM_ENABLED, BLOOM,
|
||||
AO_LEVEL,
|
||||
PLAYER,
|
||||
CONTROLS,
|
||||
}));
|
||||
export { DEFAULTS };
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// Persistenz (für das Live-Settings-Panel)
|
||||
// ────────────────────────────────────────────────────────────
|
||||
const STORAGE_KEY = 'mc-clone-settings';
|
||||
|
||||
/** Gespeicherte Overrides aus localStorage (oder {}). */
|
||||
export function loadSettings() {
|
||||
try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); }
|
||||
catch { return {}; }
|
||||
}
|
||||
|
||||
/** Kompletten Settings-Stand schreiben. */
|
||||
export function saveSettings(obj) {
|
||||
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(obj)); } catch { /* localStorage gesperrt */ }
|
||||
}
|
||||
|
||||
/** Gespeicherte Overrides löschen → nächster Load nutzt Code-Defaults. */
|
||||
export function clearSettings() {
|
||||
try { localStorage.removeItem(STORAGE_KEY); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
// Gespeicherte Overrides auf die Exporte anwenden — passiert beim Modul-Laden
|
||||
// (also VOR jedem Konsumenten, da ESM config.js zuerst vollständig auswertet).
|
||||
(() => {
|
||||
const s = loadSettings();
|
||||
if (!s) return;
|
||||
if (s.WORLD_SEED != null) WORLD_SEED = s.WORLD_SEED;
|
||||
if (s.SEA_LEVEL != null) SEA_LEVEL = s.SEA_LEVEL;
|
||||
if (s.RENDER_DISTANCE != null) RENDER_DISTANCE = s.RENDER_DISTANCE;
|
||||
if (s.MAX_OPS_PER_FRAME != null) MAX_OPS_PER_FRAME = s.MAX_OPS_PER_FRAME;
|
||||
if (s.MAX_PIXEL_RATIO != null) MAX_PIXEL_RATIO = s.MAX_PIXEL_RATIO;
|
||||
if (typeof s.ANTIALIAS === 'boolean') ANTIALIAS = s.ANTIALIAS;
|
||||
if (s.MSAA_SAMPLES != null) MSAA_SAMPLES = s.MSAA_SAMPLES;
|
||||
if (s.TONE_MAPPING != null) TONE_MAPPING = s.TONE_MAPPING;
|
||||
if (s.TONE_MAPPING_EXPOSURE != null) TONE_MAPPING_EXPOSURE = s.TONE_MAPPING_EXPOSURE;
|
||||
if (s.SKY_COLOR != null) SKY_COLOR = s.SKY_COLOR;
|
||||
if (s.FOG_NEAR != null) FOG_NEAR = s.FOG_NEAR;
|
||||
if (s.FOG_FAR != null) FOG_FAR = s.FOG_FAR;
|
||||
if (s.CAMERA_FOV != null) CAMERA_FOV = s.CAMERA_FOV;
|
||||
if (s.CAMERA_NEAR != null) CAMERA_NEAR = s.CAMERA_NEAR;
|
||||
if (s.CAMERA_FAR != null) CAMERA_FAR = s.CAMERA_FAR;
|
||||
if (typeof s.SHADOWS_ENABLED === 'boolean') SHADOWS_ENABLED = s.SHADOWS_ENABLED;
|
||||
if (s.SHADOW_MAP_SIZE != null) SHADOW_MAP_SIZE = s.SHADOW_MAP_SIZE;
|
||||
if (s.SHADOW_AREA != null) SHADOW_AREA = s.SHADOW_AREA;
|
||||
if (s.SHADOW_BIAS != null) SHADOW_BIAS = s.SHADOW_BIAS;
|
||||
if (s.SHADOW_NORMAL_BIAS != null) SHADOW_NORMAL_BIAS = s.SHADOW_NORMAL_BIAS;
|
||||
if (typeof s.BLOOM_ENABLED === 'boolean') BLOOM_ENABLED = s.BLOOM_ENABLED;
|
||||
if (s.SPAWN) Object.assign(SPAWN, s.SPAWN);
|
||||
if (s.HEMISPHERE) Object.assign(HEMISPHERE, s.HEMISPHERE);
|
||||
if (s.SUN) Object.assign(SUN, s.SUN);
|
||||
if (s.SUN_DIRECTION) for (let i = 0; i < 3; i++) if (s.SUN_DIRECTION[i] != null) SUN_DIRECTION[i] = s.SUN_DIRECTION[i];
|
||||
if (s.BLOOM) Object.assign(BLOOM, s.BLOOM);
|
||||
if (s.AO_LEVEL) for (let i = 0; i < 4; i++) if (s.AO_LEVEL[i] != null) AO_LEVEL[i] = s.AO_LEVEL[i];
|
||||
if (s.PLAYER) Object.assign(PLAYER, s.PLAYER);
|
||||
if (s.CONTROLS) Object.assign(CONTROLS, s.CONTROLS);
|
||||
})();
|
||||
Reference in new issue
Block a user