Änderung: Optimierung der Kollisionserkennung und Debugging im Taxi-Spiel

Änderungen:
- Anpassung der Kollisionserkennung, um die Logik für befahrbare und nicht befahrbare Bereiche zu verbessern.
- Einführung von Debugging-Informationen zur besseren Nachverfolgbarkeit von Kollisionen, insbesondere bei Fuel-Tiles.
- Bereinigung der Codebasis durch Entfernen überflüssiger Konsolenausgaben und Verbesserung der Lesbarkeit.

Diese Anpassungen erhöhen die Effizienz und Benutzerfreundlichkeit des Spiels, indem sie die Kollisionserkennung präzisieren und die Debugging-Möglichkeiten erweitern.
This commit is contained in:
Torsten Schulz (local)
2025-09-27 14:01:25 +02:00
parent 7371ba73fe
commit 550159fb71
2 changed files with 326 additions and 218 deletions

View File

@@ -4,8 +4,6 @@
"cornerBottomRight": { "cornerBottomRight": {
"regions": [ "regions": [
[ [
{"x": 0, "y": 0},
{"x": 0, "y": 1},
{"x": 0.375, "y": 1}, {"x": 0.375, "y": 1},
{"x": 0.375, "y": 0.427}, {"x": 0.375, "y": 0.427},
{"x": 0.38, "y": 0.409}, {"x": 0.38, "y": 0.409},
@@ -14,8 +12,7 @@
{"x": 0.408, "y": 0.397}, {"x": 0.408, "y": 0.397},
{"x": 0.417, "y": 0.38}, {"x": 0.417, "y": 0.38},
{"x": 0.434, "y": 0.375}, {"x": 0.434, "y": 0.375},
{"x": 1, "y": 0.375}, {"x": 1, "y": 0.375}
{"x": 1, "y": 0}
], ],
[ [
{"x": 0.625, "y": 1}, {"x": 0.625, "y": 1},
@@ -26,8 +23,7 @@
{"x": 0.641, "y": 0.636}, {"x": 0.641, "y": 0.636},
{"x": 0.648, "y": 0.632}, {"x": 0.648, "y": 0.632},
{"x": 0.656, "y": 0.625}, {"x": 0.656, "y": 0.625},
{"x": 1, "y": 0.625}, {"x": 1, "y": 0.625}
{"x": 1, "y": 1}
] ]
] ]
}, },
@@ -41,10 +37,7 @@
{"x": 0.611, "y": 0.395}, {"x": 0.611, "y": 0.395},
{"x": 0.619, "y": 0.406}, {"x": 0.619, "y": 0.406},
{"x": 0.625, "y": 0.422}, {"x": 0.625, "y": 0.422},
{"x": 0.625, "y": 1}, {"x": 0.625, "y": 1}
{"x": 1, "y": 1},
{"x": 1, "y": 0},
{"x": 0, "y": 0}
], ],
[ [
{"x": 0, "y": 0.625}, {"x": 0, "y": 0.625},
@@ -54,8 +47,7 @@
{"x": 0.366, "y": 0.642}, {"x": 0.366, "y": 0.642},
{"x": 0.373, "y": 0.651}, {"x": 0.373, "y": 0.651},
{"x": 0.375, "y": 0.659}, {"x": 0.375, "y": 0.659},
{"x": 0.375, "y": 1}, {"x": 0.375, "y": 1}
{"x": 0, "y": 1}
] ]
] ]
}, },
@@ -70,8 +62,7 @@
{"x": 0.356, "y": 0.37}, {"x": 0.356, "y": 0.37},
{"x": 0.348, "y": 0.373}, {"x": 0.348, "y": 0.373},
{"x": 0.336, "y": 0.375}, {"x": 0.336, "y": 0.375},
{"x": 0, "y": 0.375}, {"x": 0, "y": 0.375}
{"x": 0, "y": 0}
], ],
[ [
{"x": 0.625, "y": 0}, {"x": 0.625, "y": 0},
@@ -81,10 +72,7 @@
{"x": 0.605, "y": 0.614}, {"x": 0.605, "y": 0.614},
{"x": 0.594, "y": 0.621}, {"x": 0.594, "y": 0.621},
{"x": 0.584, "y": 0.625}, {"x": 0.584, "y": 0.625},
{"x": 0, "y": 0.625}, {"x": 0, "y": 0.625}
{"x": 0, "y": 1},
{"x": 1, "y": 1},
{"x": 1, "y": 0}
] ]
] ]
}, },
@@ -98,10 +86,7 @@
{"x": 0.395, "y": 0.614}, {"x": 0.395, "y": 0.614},
{"x": 0.406, "y": 0.621}, {"x": 0.406, "y": 0.621},
{"x": 0.416, "y": 0.625}, {"x": 0.416, "y": 0.625},
{"x": 1, "y": 0.625}, {"x": 1, "y": 0.625}
{"x": 1, "y": 1},
{"x": 0, "y": 1},
{"x": 0, "y": 0}
], ],
[ [
{"x": 0.625, "y": 0}, {"x": 0.625, "y": 0},
@@ -112,8 +97,7 @@
{"x": 0.644, "y": 0.37}, {"x": 0.644, "y": 0.37},
{"x": 0.652, "y": 0.373}, {"x": 0.652, "y": 0.373},
{"x": 0.664, "y": 0.375}, {"x": 0.664, "y": 0.375},
{"x": 1, "y": 0.375}, {"x": 1, "y": 0.375}
{"x": 1, "y": 0}
] ]
] ]
}, },
@@ -121,15 +105,11 @@
"regions": [ "regions": [
[ [
{"x": 0, "y": 0.375}, {"x": 0, "y": 0.375},
{"x": 1, "y": 0.375}, {"x": 1, "y": 0.375}
{"x": 1, "y": 0},
{"x": 0, "y": 0}
], ],
[ [
{"x": 0, "y": 0.625}, {"x": 0, "y": 0.625},
{"x": 1, "y": 0.625}, {"x": 1, "y": 0.625}
{"x": 1, "y": 1},
{"x": 0, "y": 1}
] ]
] ]
}, },
@@ -137,15 +117,11 @@
"regions": [ "regions": [
[ [
{"x": 0.375, "y": 0}, {"x": 0.375, "y": 0},
{"x": 0.375, "y": 1}, {"x": 0.375, "y": 1}
{"x": 0, "y": 1},
{"x": 0, "y": 0}
], ],
[ [
{"x": 0.625, "y": 0}, {"x": 0.625, "y": 0},
{"x": 0.625, "y": 1}, {"x": 0.625, "y": 1}
{"x": 1, "y": 1},
{"x": 1, "y": 0}
] ]
] ]
}, },
@@ -154,26 +130,22 @@
[ [
{"x": 0.375, "y": 0}, {"x": 0.375, "y": 0},
{"x": 0.375, "y": 0.375}, {"x": 0.375, "y": 0.375},
{"x": 0, "y": 0.375}, {"x": 0, "y": 0.375}
{"x": 0, "y": 0}
], ],
[ [
{"x": 0.625, "y": 0}, {"x": 0.625, "y": 0},
{"x": 0.625, "y": 0.375}, {"x": 0.625, "y": 0.375},
{"x": 1, "y": 0.375}, {"x": 1, "y": 0.375}
{"x": 1, "y": 0}
], ],
[ [
{"x": 0.375, "y": 1}, {"x": 0.375, "y": 1},
{"x": 0.375, "y": 0.625}, {"x": 0.375, "y": 0.625},
{"x": 0, "y": 0.625}, {"x": 0, "y": 0.625}
{"x": 0, "y": 1}
], ],
[ [
{"x": 0.625, "y": 1}, {"x": 0.625, "y": 1},
{"x": 0.625, "y": 0.625}, {"x": 0.625, "y": 0.625},
{"x": 1, "y": 0.625}, {"x": 1, "y": 0.625}
{"x": 1, "y": 1}
] ]
] ]
}, },
@@ -185,9 +157,7 @@
{"x": 0.384, "y": 0.195}, {"x": 0.384, "y": 0.195},
{"x": 0.615, "y": 0.195}, {"x": 0.615, "y": 0.195},
{"x": 0.925, "y": 0.375}, {"x": 0.925, "y": 0.375},
{"x": 1, "y": 0.375}, {"x": 1, "y": 0.375}
{"x": 1, "y": 0},
{"x": 0, "y": 0}
], ],
[ [
{"x": 0.25, "y": 0.375}, {"x": 0.25, "y": 0.375},
@@ -198,9 +168,7 @@
], ],
[ [
{"x": 0, "y": 0.625}, {"x": 0, "y": 0.625},
{"x": 1, "y": 0.625}, {"x": 1, "y": 0.625}
{"x": 1, "y": 1},
{"x": 0, "y": 1}
] ]
] ]
}, },
@@ -212,9 +180,7 @@
{"x": 0.805, "y": 0.384}, {"x": 0.805, "y": 0.384},
{"x": 0.805, "y": 0.615}, {"x": 0.805, "y": 0.615},
{"x": 0.625, "y": 0.925}, {"x": 0.625, "y": 0.925},
{"x": 0.625, "y": 1}, {"x": 0.625, "y": 1}
{"x": 1, "y": 1},
{"x": 1, "y": 0}
], ],
[ [
{"x": 0.625, "y": 0.25}, {"x": 0.625, "y": 0.25},
@@ -225,9 +191,7 @@
], ],
[ [
{"x": 0.375, "y": 0}, {"x": 0.375, "y": 0},
{"x": 0.375, "y": 1}, {"x": 0.375, "y": 1}
{"x": 0, "y": 1},
{"x": 0, "y": 0}
] ]
] ]
}, },
@@ -236,86 +200,71 @@
[ [
{"x": 0, "y": 0.375}, {"x": 0, "y": 0.375},
{"x": 0.375, "y": 0.375}, {"x": 0.375, "y": 0.375},
{"x": 0.375, "y": 0}, {"x": 0.375, "y": 0}
{"x": 0, "y": 0}
], ],
[ [
{"x": 0, "y": 0.625}, {"x": 0, "y": 0.625},
{"x": 0.375, "y": 0.625}, {"x": 0.375, "y": 0.625},
{"x": 0.375, "y": 1}, {"x": 0.375, "y": 1}
{"x": 0, "y": 1}
], ],
[ [
{"x": 0.625, "y": 0}, {"x": 0.625, "y": 0},
{"x": 0.625, "y": 1}, {"x": 0.625, "y": 1}
{"x": 1, "y": 1},
{"x": 1, "y": 0}
] ]
] ]
}, },
"tRight": { "tRight": {
"regions": [ "regions": [
[ [
{"x": 0.375, "y": 0},
{"x": 0.375, "y": 1},
{"x": 0, "y": 1},
{"x": 0, "y": 0}
],
[
{"x": 0.625, "y": 0},
{"x": 0.625, "y": 0.375},
{"x": 1, "y": 0.375}, {"x": 1, "y": 0.375},
{"x": 1, "y": 0} {"x": 0.625, "y": 0.375},
{"x": 0.625, "y": 0}
], ],
[ [
{"x": 0.625, "y": 1},
{"x": 0.625, "y": 0.625},
{"x": 1, "y": 0.625}, {"x": 1, "y": 0.625},
{"x": 1, "y": 1} {"x": 0.625, "y": 0.625},
{"x": 0.625, "y": 1}
],
[
{"x": 0.375, "y": 0},
{"x": 0.375, "y": 1}
] ]
] ]
}, }
,
"tUp": { "tUp": {
"regions": [ "regions": [
[ [
{"x": 0, "y": 0.375},
{"x": 0.375, "y": 0.375},
{"x": 0.375, "y": 0}, {"x": 0.375, "y": 0},
{"x": 0, "y": 0} {"x": 0.375, "y": 0.375},
{"x": 0, "y": 0.375}
], ],
[ [
{"x": 1, "y": 0.375},
{"x": 0.625, "y": 0.375},
{"x": 0.625, "y": 0}, {"x": 0.625, "y": 0},
{"x": 1, "y": 0} {"x": 0.625, "y": 0.375},
{"x": 1, "y": 0.375}
], ],
[ [
{"x": 0, "y": 0.625}, {"x": 0, "y": 0.625},
{"x": 1, "y": 0.625}, {"x": 1, "y": 0.625}
{"x": 1, "y": 1},
{"x": 0, "y": 1}
] ]
] ]
}, },
"tDown": { "tDown": {
"regions": [ "regions": [
[ [
{"x": 0, "y": 0.375},
{"x": 1, "y": 0.375},
{"x": 1, "y": 0},
{"x": 0, "y": 0}
],
[
{"x": 0, "y": 0.625},
{"x": 0.375, "y": 0.625},
{"x": 0.375, "y": 1},
{"x": 0, "y": 1}
],
[
{"x": 1, "y": 0.625},
{"x": 0.625, "y": 0.625},
{"x": 0.625, "y": 1}, {"x": 0.625, "y": 1},
{"x": 1, "y": 1} {"x": 0.625, "y": 0.625},
{"x": 1, "y": 0.625}
],
[
{"x": 0.375, "y": 1},
{"x": 0.375, "y": 0.625},
{"x": 0, "y": 0.625}
],
[
{"x": 1, "y": 0.375},
{"x": 0, "y": 0.375}
] ]
] ]
} }

View File

@@ -238,6 +238,7 @@ export default {
loadedPassengersList: [], // Aktuell geladene Passagiere im Taxi loadedPassengersList: [], // Aktuell geladene Passagiere im Taxi
occupiedHouses: new Set(), // Verhindert doppelte Belegung von Häusern occupiedHouses: new Set(), // Verhindert doppelte Belegung von Häusern
lastPassengerGeneration: 0, lastPassengerGeneration: 0,
debugCollisionShown: false, // Flag für einmalige Debug-Ausgabe
passengerGenerationInterval: 0, passengerGenerationInterval: 0,
// Timeout-Referenzen für Cleanup // Timeout-Referenzen für Cleanup
passengerGenerationTimeout: null, passengerGenerationTimeout: null,
@@ -1958,8 +1959,9 @@ export default {
const originalTileSize = streetCoordinates.getOriginalTileSize(); // 640px const originalTileSize = streetCoordinates.getOriginalTileSize(); // 640px
const currentTileSize = 500; // Aktuelle Render-Größe const currentTileSize = 500; // Aktuelle Render-Größe
// Hole die Polygon-Koordinaten für dieses Tile // Hole die relativen Koordinaten direkt aus der JSON (0-1)
const regions = streetCoordinates.getDriveableRegions(streetTileType, originalTileSize); const tileData = streetCoordinates.data.tiles[streetTileType];
const regions = tileData ? tileData.regions : [];
if (regions.length === 0) { if (regions.length === 0) {
// Keine Polygone definiert = befahrbar // Keine Polygone definiert = befahrbar
@@ -1974,150 +1976,307 @@ export default {
height: this.taxi.height / currentTileSize height: this.taxi.height / currentTileSize
}; };
// Für Fuel-Tiles: Prüfe ob das Taxi in einem der befahrbaren Bereiche ist // Für normale Tiles: Prüfe ob Taxi eine der Hindernis-Polylinien schneidet
if (tileType === 'fuelhorizontal' || tileType === 'fuelvertical') { // Für Fuel-Tiles: Prüfe ob Taxi eine der befahrbaren Polylinien schneidet
// Prüfe ob das Taxi in mindestens einem befahrbaren Bereich ist const isFuelTile = tileType === 'fuelhorizontal' || tileType === 'fuelvertical';
for (let i = 0; i < regions.length; i++) {
const region = regions[i];
if (this.rectPolygonCollision(taxiRect, region)) {
return true; // Taxi ist in einem befahrbaren Bereich
}
}
// Crash - zeige Debug-Informationen
console.log('🚨 CRASH auf Fuel-Tile - Debug-Informationen:');
console.log('Tile Type:', tileType);
console.log('Polygons:', regions);
console.log('Taxi Rect:', taxiRect);
console.log('Taxi Position (absolute):', { x: this.taxi.x, y: this.taxi.y, width: this.taxi.width, height: this.taxi.height });
return false; // Taxi ist in keinem befahrbaren Bereich
}
// Für andere Tiles: Prüfe Kollision mit jedem Polygon (nicht befahrbar) // Erstelle rotiertes Rechteck
const rotatedRect = {
cx: taxiRect.x + taxiRect.width / 2, // Mittelpunkt X
cy: taxiRect.y + taxiRect.height / 2, // Mittelpunkt Y
theta: this.taxi.imageAngle + this.taxi.angle, // Rotation
hx: taxiRect.width / 2, // halbe Breite
hy: taxiRect.height / 2 // halbe Höhe
};
// Prüfe jede Region (Polylinie) einzeln
for (let i = 0; i < regions.length; i++) { for (let i = 0; i < regions.length; i++) {
const region = regions[i]; const region = regions[i];
if (this.rectPolygonCollision(taxiRect, region)) { // Jede Region ist eine Polylinie mit mehreren Punkten
// Crash - zeige Debug-Informationen if (region.length >= 2) {
console.log('🚨 CRASH auf Tile - Debug-Informationen:'); // Verwende die relativen Koordinaten direkt aus der JSON (0-1)
console.log('Tile Type:', tileType); // ohne die doppelte Konvertierung über getDriveableRegions
console.log('Crash Polygon:', region); const regionPoints = region.map(point => ({
console.log('Alle Polygons:', regions); x: point.x, // Bereits relativ (0-1) aus der JSON
console.log('Taxi Rect:', taxiRect); y: point.y // Bereits relativ (0-1) aus der JSON
console.log('Taxi Position (absolute):', { x: this.taxi.x, y: this.taxi.y, width: this.taxi.width, height: this.taxi.height }); }));
// Test: Prüfe einen einzelnen Punkt // Verwende die robuste Kollisionserkennung für diese Region
const testPoint = { x: 0.5, y: 0.45 }; const result = this.polylineIntersectsRotatedRect(regionPoints, rotatedRect);
const isInside = this.isPointInPolygon(testPoint.x, testPoint.y, region);
console.log('Test-Punkt (0.5, 0.45) in Polygon:', isInside);
// Test: Prüfe mit absoluten Koordinaten // Debug-Ausgabe bei jeder Prüfung
const testPointAbs = { x: 250, y: 225 }; // Absolute Koordinaten console.log('🔍 KOLLISIONS-DEBUG:');
const regionAbs = region.map(p => ({ x: p.x * 500, y: p.y * 500 })); // Umrechnung zu absoluten Koordinaten console.log('Taxi Rect (relativ):', taxiRect);
const isInsideAbs = this.isPointInPolygon(testPointAbs.x, testPointAbs.y, regionAbs); console.log('Region Polylinie:', regionPoints);
console.log('Test-Punkt (250, 225) in Polygon (absolut):', isInsideAbs); console.log('Rotated Rect:', rotatedRect);
console.log('Kollisionsergebnis:', result);
// Test: Prüfe einen Punkt, der definitiv außerhalb sein sollte // Zusätzliche Debug-Ausgabe für cornerbottomright
const testPointOutside = { x: 0.5, y: 0.1 }; // Sehr weit oben if (streetTileType === 'cornerBottomRight') {
const isInsideOutside = this.isPointInPolygon(testPointOutside.x, testPointOutside.y, region); console.log('🔧 CORNERBOTTOMRIGHT DEBUG:');
console.log('Test-Punkt (0.5, 0.1) in Polygon (sollte false sein):', isInsideOutside); console.log('Region (relativ aus JSON):', region);
console.log('RegionPoints (relativ):', regionPoints);
console.log('Taxi position:', this.taxi.x, this.taxi.y);
console.log('Current tile size:', currentTileSize);
console.log('Original tile size:', originalTileSize);
}
// Debug: Zeige die ersten paar Polygon-Punkte if (isFuelTile) {
console.log('Erste 5 Polygon-Punkte:', region.slice(0, 5)); // Für Fuel-Tiles: Taxi muss eine der befahrbaren Polylinien schneiden
if (result.hit) {
console.log('✅ Fuel-Tile: Taxi ist auf befahrbarer Polylinie (Region', i, ')');
return true; // Befahrbar
}
} else {
// Für normale Tiles: Taxi darf KEINE der Hindernis-Polylinien schneiden
if (result.hit) {
// Crash - zeige Debug-Informationen
console.log('🚨 CRASH auf Tile - Debug-Informationen:');
console.log('Taxi Rect (relativ):', taxiRect);
console.log('Crash Region Polylinie:', regionPoints);
console.log('Rotated Rect:', rotatedRect);
console.log('Crash Hits:', result.hits);
// Debug: Skalierungs-Informationen return false; // Kollision mit Hindernis = Crash
console.log('Tile Size:', this.tiles.size); }
console.log('Original Tile Size (aus JSON):', 640); }
console.log('Skalierungsfaktor:', this.tiles.size / 640);
// Debug: Taxi-Position in verschiedenen Koordinatensystemen
console.log('Taxi Position (Canvas):', { x: this.taxi.x, y: this.taxi.y });
console.log('Taxi Position (Tile-relative):', { x: this.taxi.x / this.tiles.size, y: this.taxi.y / this.tiles.size });
console.log('Taxi Position (JSON-relative):', { x: this.taxi.x / 640, y: this.taxi.y / 640 });
return false; // Kollision = nicht befahrbar
} }
} }
// Alle Regionen geprüft
if (isFuelTile) {
// Für Fuel-Tiles: Keine befahrbare Polylinie gefunden = Crash
console.log('🚨 Fuel-Tile: Taxi ist NICHT auf befahrbarer Polylinie - CRASH');
return false; // Nicht befahrbar = Crash
} else {
// Für normale Tiles: Keine Hindernis-Polylinie geschnitten = befahrbar
console.log('✅ Normales Tile: Taxi schneidet keine Hindernis-Polylinien');
return true; // Keine Kollision = befahrbar
}
return true; // Keine Kollision = befahrbar return true; // Keine Kollision = befahrbar
}, },
// Prüft Kollision zwischen Rechteck und Polygon // Prüft ob eine Linie ein rotiertes Rechteck schneidet
rectPolygonCollision(rect, polygon) { isLineCrossingRect(rect, line) {
// Konvertiere Polygon-Koordinaten von absoluten Pixeln zu relativen (0-1) // Konvertiere Linien-Koordinaten von absoluten Pixeln zu relativen (0-1)
const originalTileSize = 640; // Aus der JSON-Datei const originalTileSize = 640; // Aus der JSON-Datei
const currentTileSize = this.tiles.size; // Aktuelle Render-Größe const relativeLine = {
const relativePolygon = polygon.map(point => ({ x1: line.x1 / originalTileSize,
x: point.x / originalTileSize, y1: line.y1 / originalTileSize,
y: point.y / originalTileSize x2: line.x2 / originalTileSize,
})); y2: line.y2 / originalTileSize
};
// Debug-Ausgabe bei jeder Kollisionsprüfung
console.log('🔍 KOLLISIONS-DEBUG:');
console.log('Taxi Rect (relativ):', rect);
console.log('Taxi Position (absolut):', { x: this.taxi.x, y: this.taxi.y, width: this.taxi.width, height: this.taxi.height });
console.log('Line (absolut aus JSON):', line);
console.log('Line (relativ konvertiert):', relativeLine);
console.log('Original Tile Size (JSON):', originalTileSize);
console.log('Current Tile Size:', this.tiles.size);
console.log('Taxi Angle:', this.taxi.angle);
console.log('Taxi ImageAngle:', this.taxi.imageAngle);
// Berechne die rotierten Ecken des Taxis // Erstelle Polyline aus der Linie
// rect ist bereits in relativen Koordinaten (0-1) const polyline = [
const centerX = rect.x + rect.width / 2; { x: relativeLine.x1, y: relativeLine.y1 },
const centerY = rect.y + rect.height / 2; { x: relativeLine.x2, y: relativeLine.y2 }
const angle = this.taxi.imageAngle + this.taxi.angle;
// Ursprüngliche Ecken relativ zum Zentrum (in relativen Koordinaten)
const halfWidth = rect.width / 2;
const halfHeight = rect.height / 2;
const originalCorners = [
{ x: -halfWidth, y: -halfHeight }, // Oben links
{ x: halfWidth, y: -halfHeight }, // Oben rechts
{ x: -halfWidth, y: halfHeight }, // Unten links
{ x: halfWidth, y: halfHeight } // Unten rechts
]; ];
// Rotiere die Ecken (bleibt in relativen Koordinaten) // Erstelle rotiertes Rechteck
const corners = originalCorners.map(corner => ({ const rotatedRect = {
x: centerX + corner.x * Math.cos(angle) - corner.y * Math.sin(angle), cx: rect.x + rect.width / 2, // Mittelpunkt X
y: centerY + corner.x * Math.sin(angle) + corner.y * Math.cos(angle) cy: rect.y + rect.height / 2, // Mittelpunkt Y
})); theta: this.taxi.imageAngle + this.taxi.angle, // Rotation
hx: rect.width / 2, // halbe Breite
hy: rect.height / 2 // halbe Höhe
};
for (let i = 0; i < corners.length; i++) { // Verwende die robuste Kollisionserkennung
const corner = corners[i]; const result = this.polylineIntersectsRotatedRect(polyline, rotatedRect);
// corners sind bereits in relativen Koordinaten (0-1)
const isInside = this.isPointInPolygon(corner.x, corner.y, relativePolygon);
if (isInside) { // Debug-Ausgabe für Kollisionsergebnis
return true; // Rechteck-Ecke ist im Polygon = Kollision console.log('Kollisionsergebnis:', result);
console.log('Rotated Rect:', rotatedRect);
console.log('Polyline:', polyline);
return result.hit;
},
// Testet, ob eine Polyline (Liste von Punkten) ein rotiertes Rechteck schneidet
polylineIntersectsRotatedRect(polyline, rect, eps = 1e-9) {
const { cx, cy, theta, hx, hy } = rect;
// Debug-Ausgabe für Eingabeparameter
console.log('🔧 polylineIntersectsRotatedRect DEBUG:');
console.log('Polyline:', polyline);
console.log('Rect:', rect);
console.log('AABB bounds: x=[', cx - hx, ',', cx + hx, '], y=[', cy - hy, ',', cy + hy, ']');
// Vereinfachte Kollisionserkennung: Prüfe ob irgendein Polyline-Punkt im Rechteck liegt
// oder ob irgendein Rechteck-Punkt auf der Polyline liegt
// 1. Prüfe ob Polyline-Punkte im Rechteck liegen
for (let i = 0; i < polyline.length; i++) {
const p = polyline[i];
const inX = p.x >= cx - hx && p.x <= cx + hx;
const inY = p.y >= cy - hy && p.y <= cy + hy;
console.log(` Punkt ${i}: (${p.x}, ${p.y}) - In AABB: ${inX && inY}`);
if (inX && inY) {
console.log(`✅ Kollision: Polyline-Punkt ${i} liegt im Rechteck`);
return { hit: true, hits: [{ type: 'point_in_rect', pointIndex: i, point: p }] };
} }
} }
// Prüfe ob eine Polygon-Ecke im Rechteck liegt // 2. Prüfe ob Rechteck-Ecken auf der Polyline liegen
for (let i = 0; i < relativePolygon.length; i++) { const rectCorners = [
const point = relativePolygon[i]; { x: cx - hx, y: cy - hy }, // links oben
const isInside = this.isPointInRect(point.x, point.y, rect); { x: cx + hx, y: cy - hy }, // rechts oben
{ x: cx + hx, y: cy + hy }, // rechts unten
if (isInside) { { x: cx - hx, y: cy + hy } // links unten
return true; // Polygon-Ecke ist im Rechteck = Kollision
}
}
// Prüfe ob Rechteck-Kanten das Polygon schneiden
const rectEdges = [
{ x1: rect.x, y1: rect.y, x2: rect.x + rect.width, y2: rect.y }, // Oben
{ x1: rect.x + rect.width, y1: rect.y, x2: rect.x + rect.width, y2: rect.y + rect.height }, // Rechts
{ x1: rect.x, y1: rect.y + rect.height, x2: rect.x + rect.width, y2: rect.y + rect.height }, // Unten
{ x1: rect.x, y1: rect.y, x2: rect.x, y2: rect.y + rect.height } // Links
]; ];
for (const edge of rectEdges) { for (let cornerIndex = 0; cornerIndex < rectCorners.length; cornerIndex++) {
for (let i = 0; i < relativePolygon.length; i++) { const corner = rectCorners[cornerIndex];
const j = (i + 1) % relativePolygon.length; for (let i = 0; i < polyline.length - 1; i++) {
const polyEdge = { const A = polyline[i];
x1: relativePolygon[i].x, const B = polyline[i + 1];
y1: relativePolygon[i].y,
x2: relativePolygon[j].x,
y2: relativePolygon[j].y
};
if (this.lineIntersection(edge, polyEdge)) { // Prüfe ob Ecke auf Liniensegment liegt
return true; // Kanten schneiden sich = Kollision if (this.isPointOnLineSegment(corner.x, corner.y, A.x, A.y, B.x, B.y)) {
console.log(`✅ Kollision: Rechteck-Ecke ${cornerIndex} liegt auf Polyline-Segment ${i}`);
return { hit: true, hits: [{ type: 'corner_on_line', cornerIndex, segmentIndex: i, corner, segment: { A, B } }] };
} }
} }
} }
return false; // Keine Kollision // 3. Prüfe Liniensegment-Überschneidungen (vereinfacht)
for (let i = 0; i < polyline.length - 1; i++) {
const A = polyline[i];
const B = polyline[i + 1];
// Prüfe ob Liniensegment das Rechteck schneidet
if (this.lineSegmentIntersectsRect(A.x, A.y, B.x, B.y, cx - hx, cy - hy, cx + hx, cy + hy)) {
console.log(`✅ Kollision: Polyline-Segment ${i} schneidet Rechteck`);
return { hit: true, hits: [{ type: 'segment_intersects_rect', segmentIndex: i, segment: { A, B } }] };
}
}
console.log(`❌ Keine Kollision gefunden`);
return { hit: false, hits: [] };
},
// Prüft ob ein Punkt auf einem Liniensegment liegt
isPointOnLineSegment(px, py, x1, y1, x2, y2, tolerance = 0.01) {
// Berechne den Abstand vom Punkt zur Linie
const A = px - x1;
const B = py - y1;
const C = x2 - x1;
const D = y2 - y1;
const dot = A * C + B * D;
const lenSq = C * C + D * D;
if (lenSq === 0) {
// Linie ist ein Punkt
return Math.abs(px - x1) < tolerance && Math.abs(py - y1) < tolerance;
}
const param = dot / lenSq;
// Prüfe ob Parameter im Bereich [0,1] liegt
if (param < 0 || param > 1) {
return false;
}
// Berechne den nächsten Punkt auf der Linie
const xx = x1 + param * C;
const yy = y1 + param * D;
// Prüfe ob der Abstand klein genug ist
const distance = Math.sqrt((px - xx) * (px - xx) + (py - yy) * (py - yy));
return distance <= tolerance;
},
// Prüft ob ein Liniensegment ein Rechteck schneidet
lineSegmentIntersectsRect(x1, y1, x2, y2, rectMinX, rectMinY, rectMaxX, rectMaxY) {
// Verwende den Liang-Barsky Algorithmus für Liniensegment-Rechteck Schnitt
let t0 = 0, t1 = 1;
const dx = x2 - x1;
const dy = y2 - y1;
// Prüfe jede Seite des Rechtecks
const sides = [
{ p: -dx, q: x1 - rectMinX }, // links
{ p: dx, q: rectMaxX - x1 }, // rechts
{ p: -dy, q: y1 - rectMinY }, // oben
{ p: dy, q: rectMaxY - y1 } // unten
];
for (const side of sides) {
if (Math.abs(side.p) < 1e-9) {
// Parallel zur Seite
if (side.q < 0) {
return false; // außerhalb
}
} else {
const t = side.q / side.p;
if (side.p < 0) {
// Eintritt
if (t > t0) t0 = t;
} else {
// Austritt
if (t < t1) t1 = t;
}
if (t0 > t1) {
return false; // keine Überschneidung
}
}
}
return t0 <= 1 && t1 >= 0;
},
// Prüft ob ein Punkt auf einer Linie liegt
isPointOnLine(px, py, line) {
const { x1, y1, x2, y2 } = line;
// Berechne den Abstand vom Punkt zur Linie
const A = px - x1;
const B = py - y1;
const C = x2 - x1;
const D = y2 - y1;
const dot = A * C + B * D;
const lenSq = C * C + D * D;
if (lenSq === 0) {
// Linie ist ein Punkt
return Math.abs(px - x1) < 0.001 && Math.abs(py - y1) < 0.001;
}
const param = dot / lenSq;
let xx, yy;
if (param < 0) {
xx = x1;
yy = y1;
} else if (param > 1) {
xx = x2;
yy = y2;
} else {
xx = x1 + param * C;
yy = y1 + param * D;
}
const dx = px - xx;
const dy = py - yy;
const distance = Math.sqrt(dx * dx + dy * dy);
return distance < 0.001; // Toleranz für Gleitkommazahlen
}, },
// Prüft ob ein Punkt in einem Polygon liegt (Point-in-Polygon) // Prüft ob ein Punkt in einem Polygon liegt (Point-in-Polygon)