Änderung: Erweiterung der Logik zur Überprüfung von Rotlichtverstößen im Taxi-Spiel

Änderungen:
- Hinzufügung von Variablen zur Speicherung der letzten Position des Taxis und zur Verwaltung von Rotlichtverstößen.
- Implementierung einer neuen Methode `checkRedLightViolation`, die überprüft, ob das Taxi bei Rotlicht über die Haltelinie fährt.
- Anpassung der Minimap-Zeichnung, um die Leistung durch Drosselung der Zeichenintervalle zu verbessern.
- Verbesserung der Logik zur Darstellung von Ampeln und deren Phasen.

Diese Anpassungen erhöhen die Realitätsnähe und die Spielmechanik im Taxi-Minispiel, indem sie eine präzisere Verkehrsüberwachung ermöglichen.
This commit is contained in:
Torsten Schulz (local)
2025-09-19 07:57:42 +02:00
parent 5142243a88
commit 83e83f6ba9

View File

@@ -243,6 +243,9 @@ export default {
trafficLightStates: {}, trafficLightStates: {},
lastTrafficLightTick: 0, lastTrafficLightTick: 0,
redLightViolations: 0, redLightViolations: 0,
redLightLastCount: {},
lastMinimapDraw: 0,
minimapDrawInterval: 120,
maps: [], // Geladene Maps aus der Datenbank maps: [], // Geladene Maps aus der Datenbank
currentMap: null, // Aktuell verwendete Map currentMap: null, // Aktuell verwendete Map
selectedMapId: null, // ID der ausgewählten Map selectedMapId: null, // ID der ausgewählten Map
@@ -256,6 +259,8 @@ export default {
selectedStreetName: null selectedStreetName: null
,motorStopTimeout: null ,motorStopTimeout: null
,houseNumbers: {} ,houseNumbers: {}
,prevTaxiX: 250
,prevTaxiY: 250
} }
}, },
computed: { computed: {
@@ -728,8 +733,12 @@ export default {
this.checkCollisions(); this.checkCollisions();
this.render(); this.render();
// Minimap zeichnen // Minimap zeichnen (gedrosselt)
this.drawMinimap(); const nowTs = Date.now();
if (nowTs - this.lastMinimapDraw >= this.minimapDrawInterval) {
this.drawMinimap();
this.lastMinimapDraw = nowTs;
}
this.gameLoop = requestAnimationFrame(this.update); this.gameLoop = requestAnimationFrame(this.update);
}, },
@@ -784,6 +793,9 @@ export default {
this.lastSteerChange = currentTime; this.lastSteerChange = currentTime;
} }
// Vorherige Position merken für Richtungs-/Grenzprüfungen
this.prevTaxiX = this.taxi.x;
this.prevTaxiY = this.taxi.y;
// Aktualisiere Position (+20% Geschwindigkeit) // Aktualisiere Position (+20% Geschwindigkeit)
this.taxi.x += Math.cos(this.taxi.angle) * this.taxi.speed * 0.12; this.taxi.x += Math.cos(this.taxi.angle) * this.taxi.speed * 0.12;
this.taxi.y += Math.sin(this.taxi.angle) * this.taxi.speed * 0.12; this.taxi.y += Math.sin(this.taxi.angle) * this.taxi.speed * 0.12;
@@ -860,6 +872,10 @@ export default {
if (!this.isPaused && !this.isTaxiOnRoad()) { if (!this.isPaused && !this.isTaxiOnRoad()) {
this.handleCrash(); this.handleCrash();
} }
// Rotlichtverstöße prüfen
if (!this.isPaused) {
this.checkRedLightViolation();
}
// Prüfe Hindernisse nur wenn das Spiel nicht pausiert ist // Prüfe Hindernisse nur wenn das Spiel nicht pausiert ist
if (!this.isPaused) { if (!this.isPaused) {
@@ -870,6 +886,84 @@ export default {
}); });
} }
}, },
// Prüft Überfahren der (virtuell verdoppelten) Haltelinie aus der straßenzugewandten Seite
checkRedLightViolation() {
if (!this.currentMap || !this.currentMap.mapData) return;
const tileSize = this.tiles.size;
const tileType = this.getCurrentTileType();
const approaches = this.getTileApproaches(tileType);
if (!approaches.top && !approaches.right && !approaches.bottom && !approaches.left) return;
if (!this.getTrafficLightFor(this.currentTile.col + (this.currentMap.offsetX||0), this.currentTile.row + (this.currentMap.offsetY||0))) return;
const phase = this.getTrafficLightPhase(this.currentTile.col + (this.currentMap.offsetX||0), this.currentTile.row + (this.currentMap.offsetY||0));
const rects = this.getVirtualStopLineRects(tileSize, approaches);
// Einseitige Prüfung je Richtung und Phasen-Logik
// Zu prüfende Ecken laut Vorgabe:
// Horizontal: links unten (BL) und rechts oben (TR) müssen rot sein, um Verstoß zu zählen
// Vertikal: rechts unten (BR) und links oben (TL) müssen rot sein
// Diagonale Synchronisierung: TL/BR sind vertikal gekoppelt, TR/BL horizontal
const isHorRed = (phase === 2 || phase === 3);
const isVerRed = (phase === 0 || phase === 1);
let violated = false;
// Von welcher Seite kommt das Taxi? Delta der Mittelpunkt-Position
const prevCX = this.prevTaxiX + this.taxi.width / 2;
const prevCY = this.prevTaxiY + this.taxi.height / 2;
const currCX = this.taxi.x + this.taxi.width / 2;
const currCY = this.taxi.y + this.taxi.height / 2;
const vx = currCX - prevCX;
const vy = currCY - prevCY;
// Top-Band: von oben nach unten (prev oben, curr unten), Eintritt über obere Bandkante y0
if (rects.top && approaches.top) {
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 inX = (prevCX >= x0 && prevCX <= x1) || (currCX >= x0 && currCX <= x1);
if (vy > 0 && inX && prevCY < y0 && currCY >= y0) {
if (isVerRed) violated = true;
}
}
// Bottom-Band: von unten nach oben (prev unten, curr oben), Eintritt über untere Bandkante y1
if (rects.bottom && approaches.bottom) {
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 inX = (prevCX >= x0 && prevCX <= x1) || (currCX >= x0 && currCX <= x1);
if (vy < 0 && inX && prevCY > y1 && currCY <= y1) {
if (isVerRed) violated = true;
}
}
// Left-Band: von links nach rechts (prev links, curr rechts), Eintritt über linke Bandkante x0
if (rects.left && approaches.left) {
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 inY = (prevCY >= y0 && prevCY <= y1) || (currCY >= y0 && currCY <= y1);
if (vx > 0 && inY && prevCX < x0 && currCX >= x0) {
if (isHorRed) violated = true;
}
}
// Right-Band: von rechts nach links (prev rechts, curr links), Eintritt über rechte Bandkante x1
if (rects.right && approaches.right) {
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 inY = (prevCY >= y0 && prevCY <= y1) || (currCY >= y0 && currCY <= y1);
if (vx < 0 && inY && prevCX > x1 && currCX <= x1) {
if (isHorRed) violated = true;
}
}
if (violated) {
// Entprellen: pro Tile nur einmal pro tatsächlichem Übertritt zählen
const key = `${this.currentTile.col},${this.currentTile.row}`;
const now = Date.now();
const last = this.redLightLastCount[key] || 0;
if (now - last > 500) { // 0.5s Sperrzeit
this.redLightLastCount[key] = now;
this.redLightViolations += 1;
}
}
},
handleCrash() { handleCrash() {
// Verhindere mehrfache Crashes in kurzer Zeit // Verhindere mehrfache Crashes in kurzer Zeit
@@ -1239,23 +1333,23 @@ export default {
if (hide.TR) showTR = false; if (hide.TR) showTR = false;
if (hide.BL) showBL = false; if (hide.BL) showBL = false;
if (hide.BR) showBR = false; if (hide.BR) showBR = false;
const drawCorner = (corner, x, y) => { const drawByAxis = (axis, x, y) => {
let img = imgRed; // axis: 'H' (horizontal) oder 'V' (vertikal)
if (corner === 'top') { let img = imgRed;
if (phase === 0) img = imgGreen; else if (phase === 1) img = imgYellow; else if (phase === 2) img = imgRed; else img = imgRedYellow; if (axis === 'H') {
} else if (corner === 'bottom') { // Horizontal: Phase 0=grün, 1=gelb, 2=rot, 3=rot-gelb
if (phase === 0) img = imgRed; else if (phase === 1) img = imgRedYellow; else if (phase === 2) img = imgGreen; else img = imgYellow; if (phase === 0) img = imgGreen; else if (phase === 1) img = imgYellow; else if (phase === 2) img = imgRed; else img = imgRedYellow;
} else if (corner === 'left') { } else {
if (phase === 0) img = imgGreen; else if (phase === 1) img = imgYellow; else if (phase === 2) img = imgRed; else img = imgRedYellow; // Vertikal: Gegenphase: 0=rot, 1=rot-gelb, 2=grün, 3=gelb
} else if (corner === 'right') { if (phase === 0) img = imgRed; else if (phase === 1) img = imgRedYellow; else if (phase === 2) img = imgGreen; else img = imgYellow;
if (phase === 0) img = imgRed; else if (phase === 1) img = imgRedYellow; else if (phase === 2) img = imgGreen; else img = imgYellow; }
} if (img && img.complete) this.ctx.drawImage(img, x, y, sTL, sTL);
if (img && img.complete) this.ctx.drawImage(img, x, y, sTL, sTL); };
}; // Diagonale Synchronisation: TL/BR vertikal, TR/BL horizontal
if (showTL) drawCorner('left', leftX, topY); if (showTL) drawByAxis('V', leftX, topY);
if (showTR) drawCorner('right', rightX, topY); if (showTR) drawByAxis('H', rightX, topY);
if (showBL) drawCorner('left', leftX, bottomY); if (showBL) drawByAxis('H', leftX, bottomY);
if (showBR) drawCorner('right', rightX, bottomY); if (showBR) drawByAxis('V', rightX, bottomY);
} }
} }
this.drawStreetNamesOnMainCanvas(this.ctx, tileSize, tileType, absCol, absRow); this.drawStreetNamesOnMainCanvas(this.ctx, tileSize, tileType, absCol, absRow);
@@ -1395,6 +1489,24 @@ export default {
if (approaches.right) ctx.fillRect(size - m, (size - width) / 2 - 30, thickness, width); if (approaches.right) ctx.fillRect(size - m, (size - width) / 2 - 30, thickness, width);
ctx.restore(); ctx.restore();
}, },
// Liefert die virtuellen (breit verdoppelten) Haltelinienrechtecke pro Seite, ohne zu zeichnen
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 width = 60; // Länge der Linie unverändert
const rects = {};
// Oben
if (approaches.top) rects.top = { x: (size - width) / 2 - 30, y: m - thickness, width: width, height: thickness };
// Unten
if (approaches.bottom) rects.bottom = { x: (size - width) / 2 + 30, y: size - m, width: width, height: thickness };
// Links
if (approaches.left) rects.left = { x: m - thickness, y: (size - width) / 2 + 30, width: thickness, height: width };
// Rechts
if (approaches.right) rects.right = { x: size - m, y: (size - width) / 2 - 30, width: thickness, height: width };
return rects;
},
// Liefert, von welchen Seiten eine Straße an dieses Tile anbindet // Liefert, von welchen Seiten eine Straße an dieses Tile anbindet
getTileApproaches(tileType) { getTileApproaches(tileType) {
switch (tileType) { switch (tileType) {