Menü-Demo läuft mit variabler Zeitschrittweite und damit mit anderer Physik als das Spiel #10

Closed
opened 2026-08-31 21:01:57 +02:00 by gilli · 1 comment
Owner

⁨⁩⁨Menü-Demo läuft mit variabler Zeitschrittweite und damit mit anderer Physik als das Spiel⁩ ⁨⁩#⁨10⁩⁨⁩"
By ⁨⁩⁨gilli⁩⁨⁩ — ⁨⁨⁩Closed⁨⁩⁩
bug
consistency
low
physics

Komponente: GAME (updateDemo, simStepDemo)
Betroffene Version: main @ 238ed66, index.html Zeilen 2731–2754

Beschreibung

Der laufende Hintergrund des Hauptmenüs ist ein echter Autopilot-Lauf – er verwendet aber die
Bildschirm-Framerate als Simulationsschritt, während das Spiel selbst mit
FIXED_TIMESTEP = 1/120 in einer Akkumulatorschleife rechnet:

// index.html:2731-2747  updateDemo(dt)
CORE.generateAhead(d.world, d.gen, {...}, d.camX + LW * 1.8);
CORE.pruneWorld(d.world, d.camX - LW);
...
simStepDemo(dt);              // dt = Framezeit * 0.85 (bzw. * 0.6 bei reduced)
d.t += dt;

// index.html:2749-2754  simStepDemo(dt)
const cmd = CORE.autopilot(d.body, d.world, { speed: speed }, d.strat, d.t, d);
CORE.stepBody(d.body, d.world, dt, cmd, speed, d.t, {});

Der Spielerpfad dagegen (update, Zeile 2652–2657):

S.acc += dt * S.timeScale;
while (S.acc >= K.FIXED_TIMESTEP && steps < 8) { simStep(K.FIXED_TIMESTEP); ... }

Auswirkung

  • Die Demo-Physik ist bildratenabhängig: Pendelbewegung, Sprunghöhe und Landezeit unterscheiden
    sich zwischen 60 Hz, 120 Hz und reducedMotion (dt * 0.6).
  • Die Menü-Demo ist damit kein valider Referenzlauf: Was dort an Schwüngen und Sprüngen zu
    sehen ist, muss im Spiel anders aussehen – und umgekehrt deckt die Demo Regressionen im
    Fixzeitschritt nicht auf.
  • Bei langen Bildschreibzeiten (> MAX_FRAME_TIME = 33 ms, z. B. Backend-Tabs) wird die Demo
    zusätzlich verzögert, aber nicht schrittstabil gerechnet.

Lösungsvorschlag

Wie im Spiel akkumulieren:

d.acc = (d.acc || 0) + dt;
let steps = 0;
while (d.acc >= K.FIXED_TIMESTEP && steps < 6) {
  const cmd = CORE.autopilot(d.body, d.world, { speed }, d.strat, d.t, d);
  CORE.stepBody(d.body, d.world, K.FIXED_TIMESTEP, cmd, speed, d.t, {});
  d.t += K.FIXED_TIMESTEP; d.acc -= K.FIXED_TIMESTEP; steps++;
}

Dokumentiert in issues/10-menue-demo-variable-zeitschrittweite.md (Commit c171694).

1 comments

⁨⁩⁨Menü-Demo läuft mit variabler Zeitschrittweite und damit mit anderer Physik als das Spiel⁩ ⁨⁩#⁨10⁩⁨⁩" By ⁨⁩⁨gilli⁩⁨⁩ — ⁨⁨⁩Closed⁨⁩⁩ bug consistency low physics > **Komponente:** GAME (`updateDemo`, `simStepDemo`) > **Betroffene Version:** `main` @ `238ed66`, `index.html` Zeilen 2731–2754 > > ## Beschreibung > > Der laufende Hintergrund des Hauptmenüs ist ein echter Autopilot-Lauf – er verwendet aber die > Bildschirm-Framerate als Simulationsschritt, während das Spiel selbst mit > `FIXED_TIMESTEP = 1/120` in einer Akkumulatorschleife rechnet: > > ```js > // index.html:2731-2747 updateDemo(dt) > CORE.generateAhead(d.world, d.gen, {...}, d.camX + LW * 1.8); > CORE.pruneWorld(d.world, d.camX - LW); > ... > simStepDemo(dt); // dt = Framezeit * 0.85 (bzw. * 0.6 bei reduced) > d.t += dt; > > // index.html:2749-2754 simStepDemo(dt) > const cmd = CORE.autopilot(d.body, d.world, { speed: speed }, d.strat, d.t, d); > CORE.stepBody(d.body, d.world, dt, cmd, speed, d.t, {}); > ``` > > Der Spielerpfad dagegen (`update`, Zeile 2652–2657): > > ```js > S.acc += dt * S.timeScale; > while (S.acc >= K.FIXED_TIMESTEP && steps < 8) { simStep(K.FIXED_TIMESTEP); ... } > ``` > > ## Auswirkung > > * Die Demo-Physik ist bildratenabhängig: Pendelbewegung, Sprunghöhe und Landezeit unterscheiden > sich zwischen 60 Hz, 120 Hz und `reducedMotion` (`dt * 0.6`). > * Die Menü-Demo ist damit **kein** valider Referenzlauf: Was dort an Schwüngen und Sprüngen zu > sehen ist, muss im Spiel anders aussehen – und umgekehrt deckt die Demo Regressionen im > Fixzeitschritt nicht auf. > * Bei langen Bildschreibzeiten (> `MAX_FRAME_TIME` = 33 ms, z. B. Backend-Tabs) wird die Demo > zusätzlich verzögert, aber nicht schrittstabil gerechnet. > > ## Lösungsvorschlag > > Wie im Spiel akkumulieren: > > ```js > d.acc = (d.acc || 0) + dt; > let steps = 0; > while (d.acc >= K.FIXED_TIMESTEP && steps < 6) { > const cmd = CORE.autopilot(d.body, d.world, { speed }, d.strat, d.t, d); > CORE.stepBody(d.body, d.world, K.FIXED_TIMESTEP, cmd, speed, d.t, {}); > d.t += K.FIXED_TIMESTEP; d.acc -= K.FIXED_TIMESTEP; steps++; > } > ``` > > --- > > *Dokumentiert in `issues/10-menue-demo-variable-zeitschrittweite.md` (Commit `c171694`).* 1 comments
Author
Owner

Behoben in 552557e.

updateDemo(dt) akkumuliert jetzt wie der Spielerpfad: d.acc (neu in startDemo()) sammelt die Framezeit, geschritten wird in K.FIXED_TIMESTEP, Obergrenze DEMO_MAX_STEPS = 6, der überzählige Rest verfällt statt nachzuklingen. simStepDemo() nimmt ausschließlich den festen Schritt entgegen – Seilkühlzeit (hook.coolT) und Pendelintegration hängen an genau diesem dt. Balancing-Zahlen in CORE.K sind unverändert, es ist allein die Schrittweite.

Gemessen vor dem Fix (Headless-Chromium, requestAnimationFrame stillgelegt, Same 17, identischer Warmlauf 2,2 s, je 2,6 s Wandzeit): stepBody erhielt je nach Bildrate 0,007083 / 0,014167 / 0,028333 / 0,034 / 0,0425 s. Die Schritt-für-Schritt-Spur lief nach 2,6 s um bis zu 644,6 px auseinander (60 fps gegenüber 120 fps: 390,4 px), mit reducedMotion 268,0 px.

Nach dem Fix: 265 Schritte in jeder Bildrate, jedes dt exakt K.FIXED_TIMESTEP, Spur identisch (Δx = Δy = 0 px), reducedMotion bremst weiterhin ab und rechnet dabei dieselbe Spur.

Abgedeckt von tests/browser_issue10.py (neu, hängt in tests/run-all.sh): derselbe Same, gleicher Warmlauf, fünf Bildraten (120/60/30/25/20 fps) plus Frame-Klumpen mit Aussetzern bis über MAX_FRAME_TIME; geprüft werden dt in stepBody, Schrittzahl, Schritt-für-Schritt-Spur, reducedMotion-Bremsung und der Welthaushalt (generateAhead/pruneWorld). Gegen git show HEAD:index.html: 9 Prüfungen rot – auf main grün. Übrige Suite unverändert grün (browser_flow.py bleibt die in AGENTS.md dokumentate Messunschärfe, Trefferquote gegen alte und neue Fassung vergleichbar).

Behoben in `552557e`. `updateDemo(dt)` akkumuliert jetzt wie der Spielerpfad: `d.acc` (neu in `startDemo()`) sammelt die Framezeit, geschritten wird in `K.FIXED_TIMESTEP`, Obergrenze `DEMO_MAX_STEPS = 6`, der überzählige Rest verfällt statt nachzuklingen. `simStepDemo()` nimmt ausschließlich den festen Schritt entgegen – Seilkühlzeit (`hook.coolT`) und Pendelintegration hängen an genau diesem dt. Balancing-Zahlen in `CORE.K` sind unverändert, es ist allein die Schrittweite. **Gemessen vor dem Fix** (Headless-Chromium, `requestAnimationFrame` stillgelegt, Same 17, identischer Warmlauf 2,2 s, je 2,6 s Wandzeit): `stepBody` erhielt je nach Bildrate 0,007083 / 0,014167 / 0,028333 / 0,034 / 0,0425 s. Die Schritt-für-Schritt-Spur lief nach 2,6 s um bis zu **644,6 px** auseinander (60 fps gegenüber 120 fps: 390,4 px), mit `reducedMotion` 268,0 px. **Nach dem Fix:** 265 Schritte in jeder Bildrate, jedes `dt` exakt `K.FIXED_TIMESTEP`, Spur identisch (Δx = Δy = 0 px), `reducedMotion` bremst weiterhin ab und rechnet dabei dieselbe Spur. **Abgedeckt von** `tests/browser_issue10.py` (neu, hängt in `tests/run-all.sh`): derselbe Same, gleicher Warmlauf, fünf Bildraten (120/60/30/25/20 fps) plus Frame-Klumpen mit Aussetzern bis über `MAX_FRAME_TIME`; geprüft werden dt in `stepBody`, Schrittzahl, Schritt-für-Schritt-Spur, reducedMotion-Bremsung und der Welthaushalt (`generateAhead`/`pruneWorld`). Gegen `git show HEAD:index.html`: 9 Prüfungen rot – auf `main` grün. Übrige Suite unverändert grün (`browser_flow.py` bleibt die in `AGENTS.md` dokumentate Messunschärfe, Trefferquote gegen alte und neue Fassung vergleichbar).
gilli closed this issue 2026-09-01 16:06:26 +02:00
Sign in to join this conversation.
No description provided.