Änderung: Verbesserung der Rotlichtüberprüfung und Debugging im Taxi-Spiel

Änderungen:
- Einführung eines Flags `skipRedLightOneFrame`, um die Rotlichtprüfung beim ersten Tile-Wechsel auszusetzen und falsche Positives zu vermeiden.
- Anpassung der Logik zur Überprüfung von Rotlichtverstößen, um die Bedingungen für die Verletzung präziser zu gestalten.
- Erweiterung der Debugging-Ausgaben zur besseren Nachverfolgbarkeit von Rotlichtverstößen und Taxi-Bewegungen.

Diese Anpassungen erhöhen die Genauigkeit der Rotlichtüberprüfung und verbessern die Debugging-Möglichkeiten im Spiel.
This commit is contained in:
Torsten Schulz (local)
2025-09-25 13:44:02 +02:00
parent f418b59e14
commit 6eb97c9902

View File

@@ -309,6 +309,7 @@ export default {
,houseNumbers: {} ,houseNumbers: {}
,prevTaxiX: 250 ,prevTaxiX: 250
,prevTaxiY: 250 ,prevTaxiY: 250
,skipRedLightOneFrame: false
} }
}, },
computed: { computed: {
@@ -489,6 +490,10 @@ export default {
this.taxi.y = 250 - this.taxi.height/2; // Zentriert in der Mitte this.taxi.y = 250 - this.taxi.height/2; // Zentriert in der Mitte
this.taxi.angle = 0; // Fahrtrichtung nach oben this.taxi.angle = 0; // Fahrtrichtung nach oben
this.taxi.speed = 0; // Geschwindigkeit zurücksetzen this.taxi.speed = 0; // Geschwindigkeit zurücksetzen
// Vorherige Position und Skip-Flag setzen, damit erste Prüfung nicht triggert
this.prevTaxiX = this.taxi.x;
this.prevTaxiY = this.taxi.y;
this.skipRedLightOneFrame = true;
// Aktuelle Tile-Position setzen // Aktuelle Tile-Position setzen
this.currentTile.row = 0; this.currentTile.row = 0;
@@ -551,6 +556,11 @@ export default {
if (moved) { if (moved) {
this.currentTile.row = newRow; this.currentTile.row = newRow;
this.currentTile.col = newCol; this.currentTile.col = newCol;
// Nach Tile-Wechsel: eine Frame lang Rotlicht-Prüfung aussetzen
// und prev-Position auf aktuelle setzen, um false positives zu vermeiden
this.prevTaxiX = this.taxi.x;
this.prevTaxiY = this.taxi.y;
this.skipRedLightOneFrame = true;
// Radar mit 12% Wahrscheinlichkeit aktivieren beim Betreten des neuen Tiles // Radar mit 12% Wahrscheinlichkeit aktivieren beim Betreten des neuen Tiles
this.activeRadar = Math.random() < 0.12; this.activeRadar = Math.random() < 0.12;
// Achse und Position festlegen: Messung findet bei 150px vom Rand statt // Achse und Position festlegen: Messung findet bei 150px vom Rand statt
@@ -1585,7 +1595,11 @@ export default {
} }
// Rotlichtverstöße prüfen // Rotlichtverstöße prüfen
if (!this.isPaused) { if (!this.isPaused) {
this.checkRedLightViolation(); if (!this.skipRedLightOneFrame) {
this.checkRedLightViolation();
}
// Einmal-Flag nach der Prüfung zurücksetzen
if (this.skipRedLightOneFrame) this.skipRedLightOneFrame = false;
} }
// Prüfe Hindernisse nur wenn das Spiel nicht pausiert ist // Prüfe Hindernisse nur wenn das Spiel nicht pausiert ist
@@ -1608,6 +1622,8 @@ export default {
const phase = this.getTrafficLightPhase(this.currentTile.col + (this.currentMap.offsetX||0), this.currentTile.row + (this.currentMap.offsetY||0)); const phase = this.getTrafficLightPhase(this.currentTile.col + (this.currentMap.offsetX||0), this.currentTile.row + (this.currentMap.offsetY||0));
const rects = this.getVirtualStopLineRects(tileSize, approaches); const rects = this.getVirtualStopLineRects(tileSize, approaches);
// Kleine Toleranz für Segmenttreffer (dick=5px, daher +-4px ausreichend)
const tol = 4;
// Einseitige Prüfung je Richtung und Phasen-Logik // Einseitige Prüfung je Richtung und Phasen-Logik
// Zu prüfende Ecken laut Vorgabe: // Zu prüfende Ecken laut Vorgabe:
@@ -1627,19 +1643,41 @@ export default {
const vx = currCX - prevCX; const vx = currCX - prevCX;
const vy = currCY - prevCY; const vy = currCY - prevCY;
// WICHTIG: Nur prüfen wenn das Taxi tatsächlich bewegt wurde (mindestens 1 Pixel) // DEBUG: Umfassendes Logging
const movementThreshold = 1; console.log('=== ROTLICHT DEBUG ===');
if (Math.abs(vx) < movementThreshold && Math.abs(vy) < movementThreshold) { console.log('Tile:', this.currentTile.col, this.currentTile.row);
return; // Keine Bewegung, keine Verletzung möglich console.log('TileType:', tileType);
} console.log('Approaches:', approaches);
console.log('Phase:', phase, 'isHorRed:', isHorRed, 'isVerRed:', isVerRed);
console.log('Taxi Position: prev(' + prevCX.toFixed(1) + ',' + prevCY.toFixed(1) + ') curr(' + currCX.toFixed(1) + ',' + currCY.toFixed(1) + ')');
console.log('Movement: vx=' + vx.toFixed(1) + ', vy=' + vy.toFixed(1));
console.log('StopLine Rects:', rects);
// Hinweis: Keine globale Bewegungsschwelle mehr.
// Die Richtungsbedingungen (vy/vx > 0 bzw. < 0) in den jeweiligen Fällen
// stellen bereits sicher, dass nur bei tatsächlicher Bewegung geprüft wird.
// Top-Band: von oben nach unten (prev oben, curr unten), Eintritt über obere Bandkante y0 // Top-Band: von oben nach unten (prev oben, curr unten), Eintritt über obere Bandkante y0
if (rects.top && approaches.top) { if (rects.top && approaches.top) {
const x0 = rects.top.x, x1 = rects.top.x + rects.top.width; const x0 = rects.top.x, x1 = rects.top.x + rects.top.width;
const y0 = rects.top.y, y1 = rects.top.y + rects.top.height; const y0 = rects.top.y, y1 = rects.top.y + rects.top.height;
const inX = (prevCX >= x0 && prevCX <= x1) || (currCX >= x0 && currCX <= x1); // Für horizontale Haltelinien: Y-Position muss in der Nähe der Haltelinie sein
// Nur verletzen wenn das Taxi tatsächlich die Linie überquert (von außerhalb zu innerhalb) const isNearStopLine = currCY >= (y0 - 50) && currCY <= (y1 + 50);
if (vy > 0 && inX && prevCY < y0 && currCY >= y0 && isVerRed) { // Zusätzlich: innerhalb des horizontalen Liniensegments (X-Span) sein
const withinXSpan = (
(prevCX >= x0 - tol && prevCX <= x1 + tol) ||
(currCX >= x0 - tol && currCX <= x1 + tol) ||
// oder Strecke zwischen prevCX und currCX schneidet [x0, x1]
(Math.min(prevCX, currCX) <= x1 + tol && Math.max(prevCX, currCX) >= x0 - tol)
);
console.log('TOP-Band: x0=' + x0 + ', x1=' + x1 + ', y0=' + y0 + ', y1=' + y1);
console.log('TOP: vy=' + vy + ', prevCY=' + prevCY.toFixed(1) + ', currCY=' + currCY.toFixed(1) + ', isVerRed=' + isVerRed + ', isNearStopLine=' + isNearStopLine + ', withinXSpan=' + withinXSpan);
console.log('TOP: prevCY < y0 =', prevCY < y0, ', currCY >= y0 =', currCY >= y0);
if (vy > 0 && prevCY < y0 && currCY >= y0 && isVerRed && isNearStopLine && withinXSpan) {
console.log('🚨 TOP-Band VIOLATION!');
violated = true; violated = true;
} }
} }
@@ -1647,9 +1685,21 @@ export default {
if (rects.bottom && approaches.bottom) { if (rects.bottom && approaches.bottom) {
const x0 = rects.bottom.x, x1 = rects.bottom.x + rects.bottom.width; const x0 = rects.bottom.x, x1 = rects.bottom.x + rects.bottom.width;
const y0 = rects.bottom.y, y1 = rects.bottom.y + rects.bottom.height; const y0 = rects.bottom.y, y1 = rects.bottom.y + rects.bottom.height;
const inX = (prevCX >= x0 && prevCX <= x1) || (currCX >= x0 && currCX <= x1); // Für horizontale Haltelinien: Y-Position muss in der Nähe der Haltelinie sein
// Nur verletzen wenn das Taxi tatsächlich die Linie überquert (von außerhalb zu innerhalb) const isNearStopLine = currCY >= (y0 - 50) && currCY <= (y1 + 50);
if (vy < 0 && inX && prevCY > y1 && currCY <= y1 && isVerRed) { // Zusätzlich: innerhalb des horizontalen Liniensegments (X-Span) sein
const withinXSpan = (
(prevCX >= x0 - tol && prevCX <= x1 + tol) ||
(currCX >= x0 - tol && currCX <= x1 + tol) ||
(Math.min(prevCX, currCX) <= x1 + tol && Math.max(prevCX, currCX) >= x0 - tol)
);
console.log('BOTTOM-Band: x0=' + x0 + ', x1=' + x1 + ', y0=' + y0 + ', y1=' + y1);
console.log('BOTTOM: vy=' + vy + ', prevCY=' + prevCY.toFixed(1) + ', currCY=' + currCY.toFixed(1) + ', isVerRed=' + isVerRed + ', isNearStopLine=' + isNearStopLine + ', withinXSpan=' + withinXSpan);
console.log('BOTTOM: prevCY > y1 =', prevCY > y1, ', currCY <= y1 =', currCY <= y1);
if (vy < 0 && prevCY > y1 && currCY <= y1 && isVerRed && isNearStopLine && withinXSpan) {
console.log('🚨 BOTTOM-Band VIOLATION!');
violated = true; violated = true;
} }
} }
@@ -1657,9 +1707,21 @@ export default {
if (rects.left && approaches.left) { if (rects.left && approaches.left) {
const x0 = rects.left.x, x1 = rects.left.x + rects.left.width; const x0 = rects.left.x, x1 = rects.left.x + rects.left.width;
const y0 = rects.left.y, y1 = rects.left.y + rects.left.height; const y0 = rects.left.y, y1 = rects.left.y + rects.left.height;
const inY = (prevCY >= y0 && prevCY <= y1) || (currCY >= y0 && currCY <= y1); // Für vertikale Haltelinien: X-Position muss in der Nähe der Haltelinie sein
// Nur verletzen wenn das Taxi tatsächlich die Linie überquert (von außerhalb zu innerhalb) const isNearStopLine = currCX >= (x0 - 50) && currCX <= (x1 + 50);
if (vx > 0 && inY && prevCX < x0 && currCX >= x0 && isHorRed) { // Zusätzlich: innerhalb des vertikalen Liniensegments (Y-Span) sein
const withinYSpan = (
(prevCY >= y0 - tol && prevCY <= y1 + tol) ||
(currCY >= y0 - tol && currCY <= y1 + tol) ||
(Math.min(prevCY, currCY) <= y1 + tol && Math.max(prevCY, currCY) >= y0 - tol)
);
console.log('LEFT-Band: x0=' + x0 + ', x1=' + x1 + ', y0=' + y0 + ', y1=' + y1);
console.log('LEFT: vx=' + vx + ', prevCX=' + prevCX.toFixed(1) + ', currCX=' + currCX.toFixed(1) + ', isHorRed=' + isHorRed + ', isNearStopLine=' + isNearStopLine + ', withinYSpan=' + withinYSpan);
console.log('LEFT: prevCX < x0 =', prevCX < x0, ', currCX >= x0 =', currCX >= x0);
if (vx > 0 && prevCX < x0 && currCX >= x0 && isHorRed && isNearStopLine && withinYSpan) {
console.log('🚨 LEFT-Band VIOLATION!');
violated = true; violated = true;
} }
} }
@@ -1667,19 +1729,37 @@ export default {
if (rects.right && approaches.right) { if (rects.right && approaches.right) {
const x0 = rects.right.x, x1 = rects.right.x + rects.right.width; const x0 = rects.right.x, x1 = rects.right.x + rects.right.width;
const y0 = rects.right.y, y1 = rects.right.y + rects.right.height; const y0 = rects.right.y, y1 = rects.right.y + rects.right.height;
const inY = (prevCY >= y0 && prevCY <= y1) || (currCY >= y0 && currCY <= y1); // Für vertikale Haltelinien: X-Position muss in der Nähe der Haltelinie sein
// Nur verletzen wenn das Taxi tatsächlich die Linie überquert (von außerhalb zu innerhalb) const isNearStopLine = currCX >= (x0 - 50) && currCX <= (x1 + 50);
if (vx < 0 && inY && prevCX > x1 && currCX <= x1 && isHorRed) { // Zusätzlich: innerhalb des vertikalen Liniensegments (Y-Span) sein
const withinYSpan = (
(prevCY >= y0 - tol && prevCY <= y1 + tol) ||
(currCY >= y0 - tol && currCY <= y1 + tol) ||
(Math.min(prevCY, currCY) <= y1 + tol && Math.max(prevCY, currCY) >= y0 - tol)
);
console.log('RIGHT-Band: x0=' + x0 + ', x1=' + x1 + ', y0=' + y0 + ', y1=' + y1);
console.log('RIGHT: vx=' + vx + ', prevCX=' + prevCX.toFixed(1) + ', currCX=' + currCX.toFixed(1) + ', isHorRed=' + isHorRed + ', isNearStopLine=' + isNearStopLine + ', withinYSpan=' + withinYSpan);
console.log('RIGHT: prevCX < x0 =', prevCX < x0, ', currCX >= x0 =', currCX >= x0);
if (vx < 0 && prevCX > x1 && currCX <= x1 && isHorRed && isNearStopLine && withinYSpan) {
console.log('🚨 RIGHT-Band VIOLATION!');
violated = true; violated = true;
} }
} }
console.log('=== FINAL RESULT ===');
console.log('Violated:', violated);
if (violated) { if (violated) {
// Entprellen: pro Tile nur einmal pro tatsächlichem Übertritt zählen // Entprellen: pro Tile nur einmal pro tatsächlichem Übertritt zählen
const key = `${this.currentTile.col},${this.currentTile.row}`; const key = `${this.currentTile.col},${this.currentTile.row}`;
const now = Date.now(); const now = Date.now();
const last = this.redLightLastCount[key] || 0; const last = this.redLightLastCount[key] || 0;
console.log('Violation detected! Key:', key, 'Last:', last, 'Now:', now, 'Diff:', now - last);
if (now - last > 500) { // 0.5s Sperrzeit if (now - last > 500) { // 0.5s Sperrzeit
console.log('✅ VIOLATION REGISTERED! Count:', this.redLightViolations + 1);
this.redLightLastCount[key] = now; this.redLightLastCount[key] = now;
this.redLightViolations += 1; this.redLightViolations += 1;
this.playRedLightSound(); this.playRedLightSound();
@@ -1697,8 +1777,12 @@ export default {
} }
}); });
} }
} else {
console.log('❌ VIOLATION IGNORED (too soon)');
} }
} }
console.log('=== END ROTLICHT DEBUG ===');
}, },
checkRadarMeasurement() { checkRadarMeasurement() {
@@ -1892,6 +1976,10 @@ export default {
this.taxi.y = 250 - this.taxi.height/2; // Mitte des Canvas (500px / 2) this.taxi.y = 250 - this.taxi.height/2; // Mitte des Canvas (500px / 2)
this.taxi.speed = 0; this.taxi.speed = 0;
this.taxi.angle = 0; this.taxi.angle = 0;
// Prev-Position synchronisieren und erste Prüfung aussetzen
this.prevTaxiX = this.taxi.x;
this.prevTaxiY = this.taxi.y;
this.skipRedLightOneFrame = true;
}, },
isTaxiOnRoad() { isTaxiOnRoad() {
@@ -2360,41 +2448,45 @@ export default {
); );
}, },
// Haltelinien: 120px vom Rand, 5px dick, 40px breit, an allen vier Zufahrten // Haltelinien: Basierend auf den korrigierten Koordinaten
drawStopLinesOnMainCanvas(ctx, size, approaches = { top:true, right:true, bottom:true, left:true }) { drawStopLinesOnMainCanvas(ctx, size, approaches = { top:true, right:true, bottom:true, left:true }) {
const margin = 120; // Basis-Abstand vom Rand
const extra = 30; // 10px zurück (von +40 auf +30)
const m = margin + extra;
const thickness = 5; const thickness = 5;
const width = 60; // +20px breiter (vorher 40)
ctx.save(); ctx.save();
ctx.fillStyle = '#ffffff'; ctx.fillStyle = '#ffffff';
// Oben (horizontale Linie, zentriert)
if (approaches.top) ctx.fillRect((size - width) / 2 - 30, m - thickness, width, thickness); // Basierend auf den korrigierten Koordinaten:
// Unten // Links: sichtbar: 145/294 - 150/313
if (approaches.bottom) ctx.fillRect((size - width) / 2 + 30, size - m, width, thickness); if (approaches.left) ctx.fillRect(145, 294, thickness, 19);
// Links (vertikale Linie, zentriert)
if (approaches.left) ctx.fillRect(m - thickness, (size - width) / 2 + 30, thickness, width); // Rechts: sichtbar: 150/186 - 355/293
// Rechts if (approaches.right) ctx.fillRect(350, 186, thickness, 107);
if (approaches.right) ctx.fillRect(size - m, (size - width) / 2 - 30, thickness, width);
// Unten: sichtbar: 250/360 - 312/365
if (approaches.bottom) ctx.fillRect(250, 360, 62, thickness);
// Oben: berechnet aus den anderen Angaben (symmetrisch zu unten)
if (approaches.top) ctx.fillRect(250, 5, 62, thickness);
ctx.restore(); ctx.restore();
}, },
// Liefert die virtuellen (breit verdoppelten) Haltelinienrechtecke pro Seite, ohne zu zeichnen // Liefert die virtuellen (breit verdoppelten) Haltelinienrechtecke pro Seite, ohne zu zeichnen
getVirtualStopLineRects(size, approaches = { top:true, right:true, bottom:true, left:true }) { getVirtualStopLineRects(size, approaches = { top:true, right:true, bottom:true, left:true }) {
const margin = 120;
const extra = 30;
const m = margin + extra;
const thickness = 5 * 2; // doppelte Breite für Erkennung const thickness = 5 * 2; // doppelte Breite für Erkennung
const width = 60; // Länge der Linie unverändert
const rects = {}; const rects = {};
// Oben
if (approaches.top) rects.top = { x: (size - width) / 2 - 30, y: m - thickness, width: width, height: thickness }; // Basierend auf den korrigierten Koordinaten:
// Unten // Links: virtuell: 145/186 - 150/313; sichtbar: 145/294 - 150/313
if (approaches.bottom) rects.bottom = { x: (size - width) / 2 + 30, y: size - m, width: width, height: thickness }; if (approaches.left) rects.left = { x: 145, y: 186, width: thickness, height: 127 };
// Links
if (approaches.left) rects.left = { x: m - thickness, y: (size - width) / 2 + 30, width: thickness, height: width }; // Rechts: virtuell: 350/186 - 355/313; sichtbar: 150/186 - 355/293
// Rechts if (approaches.right) rects.right = { x: 350, y: 186, width: thickness, height: 127 };
if (approaches.right) rects.right = { x: size - m, y: (size - width) / 2 - 30, width: thickness, height: width };
// Unten: virtuell: 187/360- 312/365; sichtbar: 250/360 - 312/365
if (approaches.bottom) rects.bottom = { x: 187, y: 360, width: 125, height: thickness };
// Oben: berechnet aus den anderen Angaben (symmetrisch zu unten)
if (approaches.top) rects.top = { x: 187, y: 5, width: 125, height: thickness };
return rects; return rects;
}, },
// Liefert, von welchen Seiten eine Straße an dieses Tile anbindet // Liefert, von welchen Seiten eine Straße an dieses Tile anbindet