Lichtschranke (GATE) hat keine Kollisionsbox und wird als 22-px-Stummel am oberen Bildschirmrand gezeichnet #1
Labels
No labels
accessibility
bug
cleanup
consistency
crash
critical
game-feel
gameplay
high
input
level-generation
low
maintainability
medium
pause
physics
responsive
security
spec-gap
ui
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gilli/pulse-sprint#1
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Komponente: CORE (Level-Generator, Kollision), RENDER
Betroffene Version:
main@238ed66(übernommen auskelpie@014b986),index.htmlBeschreibung
Der Hindernistyp
GATE(Lichtschranke – „unter hohen Hindernissen hindurchrollen“) hat imspielbaren Zustand keine wirksame Hitbox. Der Kollisionsrechteck liegt bei
y = 0 … 22,also ganz oben am Bildschirmrand, statt bei der sichtbaren Balkenunterkante (
bottom ≈ 514).Die Figur kann in normaler Körperhaltung ungehindert jede Lichtschranke durchlaufen.
Zusätzlich wird das Hindernis beim Passieren als erfolgreiches Ausweichmanöver gewertet.
Das Visualisierungsproblem ist dieselbe Ursache:
drawObstacle()leitet die sichtbareBalkenunterkante aus demselben Rechteck ab (
bottom = r.y + r.h) und zeichnet die Lichtschrankedadurch als ca. 22 px hohen Stummel direkt am oberen Rand – nicht als bis auf Kopfhöhe
abgehängendes Tor.
Ursache
In
mkObs()werden Höhe und Anker fürGATE/MGATEgesetzt, aber die Y-Position auf0belassen:
obstacleRect()kennt nur fürMGATEeine Neu-Berechnung auso.bottom; fürGATEwirdo.yunverwendet weitergereicht:makeObstacle()(Zeile 499–503) berechneto.yzunächst korrekt, wird aber vonmkObs()wieder überschrieben. Der
MGATE-Zweig funktioniert nur, weilobstacleRect()ihn auso.bottomrekonstruiert.Reproduktion
Isoliert (Node, ohne Browser)
Der CORE-Block ist über die Marker
/*__CORE_START__*///*__CORE_END__*/extrahierbar:Ausgabe:
Im echten Spiel (Playwright,
window.PULSE)Eine einzelne
GATEauf flachem Boden gesetzt, 1,2 s ohne Eingabe gelaufen:Erwartetes Verhalten
ROLL_OFF = 17) passiert den Balken mit lichter HöheGATE_CLEAR = 46.bottom = baseY - GATE_CLEAR = 514.Auswirkung
ROLL,JUMP_ROLL,ROLL_JUMPundHOOK_TO_ROLLsind ohne Rollpflicht.(
simStep, Zeile 2477–2483:+50Punkte,+4 %Fokus).verifyContent()/simStrategy()nutzendieselbe
hitTest()/testBox()-Funktion. Damit verifiziert die LösbarkeitsprüfungLichtschranken-Muster gegen eine nicht existende Kollision. Die Pflicht aus AGENTS.md
„Kein Haken darf so platziert werden, dass die Figur … unter die Unterkante einer
Lichtschranke schlägt“ ist dadurch nicht durchsetzbar – der Solver kann diesen Konflikt
nicht sehen.
Lösungsvorschlag
Entweder Y-Position beim Erzeugen setzen:
oder – robuster, weil
o.ydann nie mehr von Hand gepflegt werden muss – einenGATE-Zweigin
obstacleRect()ergänzen:Danach ist auch
drawObstacle()(Zeile 1669) wieder konsistent, und die Lösbarkeitsprüfungerfasst Lichtschranken automatisch korrekt. Zusätzlich sollte ein Regressionstest die drei
Fälle abdecken: stehend = Treffer, rollend = kein Treffer, Sprung über
bottom= kein Treffer.Dokumentiert in
gitlab-issues/01-gate-lichtschranke-keine-kollision.md(Commit238ed66). Alle Befunde ausgeführt und gemessen; Repro-Schritte im Text.changed the description
changed the description
mentioned in commit
174aeb72a1Behoben in
174aeb7(main).Umsetzung
obstacleRect()leitet die Tor-Geometrie jetzt auso.bottomab;mkObs()pflegto.ynicht mehr von Hand. Die Kollisionsbox ist die komplette Säule von der Decke bis zur
Balkenunterkante – exakt das, was gezeichnet wird (zwei Seile bis
y = 0, Balken unten).Rollen ist der einzige Weg durch; Springen greift nicht mehr.
MGATE_CLEAR64 → 48. Mit 64 lag der tiefste Punkt des beweglichen Tors über derstehenden Hitbox (Oberkante 500) und konnte damit nie treffen. Der tiefste Punkt ist jetzt
512, der Solver prüft gegen genau diese Lage (
worstCase,osc = 0), in der höchsten Lage(
bottom − amp≈ 434…456) passt die stehende Figur durch – die Periode entscheidet alsowirklich mit.
bodengleiche 34 px (526…560) → 12 px frei, stehende Hitbox 60 px (500…560) → 14 px Überlappung.
Nebenbefund beim Testen (wichtig)
Sobald das Tor eine echte Hitbox hatte, verschwanden
ROLLundMOVING_GATEkomplett aus derGenerierung (nur noch
SAFEals Fallback). Ursache lag nicht am Tor, sondern am Solver:autopilot()lasseg.x1als Grubenkante und deutete damit jede Naht zweier Bodensegmentederselben Höhe als Abgrund – in der Simulationswelt stehen Mustersegmente unverbunden neben-
eininander. Der Autopilot sprang also an jedem Musteranfang und kollidierte mit dem kurz
dahinter liegenden Tor; alle 54 Strategien fielen durch. Neu:
groundEdgeAfter()folgt-zusammenhängenden Segmenten derselben Höhe und kennt nur noch echte Kanten.
Nach beiden Änderungen erscheinen wieder alle 14 Musterfamilien, und
verifyContentakzeptiertROLL/MOVING_GATEüber alle Geschwindigkeiten (360/450/640 px/s, je 20 Seeds).Tests
node .pi/tests/issue1.mjs– 27 Prüfungen: Geometrie, Treffer stehend / frei rollend,Seitentoleranz, Solver-Marge, MGATE-Oszillation (tiefster Moment tödlich, höchster durchlässig),
alle 200 erzeugten Lichtschranken, Solver-Gegentest (ohne Rollen =
hit).node .pi/tests/soak.mjs– 10 000 generierte Muster: keine Lichtschranke direkt nach einerGrube, kein Hindernis im Anlauf einer Kante, keine Invariantenverletzung, SAFE-Fallbacks 0 %.
.pi/tests/browser_smoke.py): Runtime-Hitbox, echter Tod gegen das Tor,-rollender Durchgang zählt als Ausweichmanöver, Pixelprobe: heller Querbalken bei
logisch y ≈ 514, am oberen Rand nur die beiden Seile.
mentioned in issue #6