Änderung: Erweiterung der TaxiGame.vue und Aktualisierung des Taxi-SVG

Änderungen:
- Hinzufügung eines Kartenwählers zur Auswahl von Maps im Taxi-Spiel.
- Anpassung der Taxi-Parameter für eine verbesserte Darstellung und Positionierung.
- Implementierung von Methoden zum Laden von Taxi-Bildern und Maps aus der Datenbank.
- Aktualisierung des SVG-Bildes des Taxis mit neuen Pfaden und Farben.

Diese Anpassungen verbessern die Benutzeroberfläche und die Funktionalität des Taxi-Minispiels, indem sie eine dynamische Kartenwahl ermöglichen und die grafische Darstellung optimieren.
This commit is contained in:
Torsten Schulz (local)
2025-09-15 20:13:31 +02:00
parent 935928af75
commit 643c152194
3 changed files with 721 additions and 173 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 97 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 179 KiB

View File

@@ -137,6 +137,17 @@
<div class="minimap-card"> <div class="minimap-card">
<div class="minimap-header"> <div class="minimap-header">
<h3 class="minimap-title">Minimap</h3> <h3 class="minimap-title">Minimap</h3>
<div class="map-selector">
<select v-model="selectedMapId" @change="onMapChange" class="map-select">
<option
v-for="map in maps"
:key="map.id"
:value="map.id"
>
{{ map.name }}
</option>
</select>
</div>
</div> </div>
<div class="minimap-container"> <div class="minimap-container">
<canvas <canvas
@@ -176,21 +187,29 @@ export default {
minimapCtx: null, minimapCtx: null,
isStatsExpanded: true, isStatsExpanded: true,
taxi: { taxi: {
x: 200, x: 200, // Mitte des 400x400px Tiles
y: 200, y: 200, // Mitte des 400x400px Tiles
width: 20, width: 30, // Skalierte Breite (50px Höhe * 297/506)
height: 15, height: 50, // 50px Höhe
angle: 0, angle: 0, // Fahrtrichtung
imageAngle: Math.PI / 2, // Bildrotation (90° nach rechts)
speed: 0, speed: 0,
maxSpeed: 3 maxSpeed: 3,
image: null // Taxi-Bild
},
currentTile: {
row: 0,
col: 0
}, },
tiles: { tiles: {
size: 50, // 400px / 8 tiles = 50px per tile size: 400, // 400x400px pro Tile
images: {} images: {}
}, },
maps: [], // Geladene Maps aus der Datenbank
currentMap: null, // Aktuell verwendete Map
selectedMapId: null, // ID der ausgewählten Map
passengers: [], passengers: [],
destinations: [], destinations: [],
gasStations: [],
obstacles: [], obstacles: [],
keys: {} keys: {}
} }
@@ -199,6 +218,8 @@ export default {
this.initializeGame(); this.initializeGame();
this.initializeMinimap(); this.initializeMinimap();
this.loadTiles(); this.loadTiles();
this.loadTaxiImage();
this.loadMaps();
this.setupEventListeners(); this.setupEventListeners();
}, },
beforeUnmount() { beforeUnmount() {
@@ -209,10 +230,48 @@ export default {
this.canvas = this.$refs.gameCanvas; this.canvas = this.$refs.gameCanvas;
this.ctx = this.canvas.getContext('2d'); this.ctx = this.canvas.getContext('2d');
this.canvas.focus(); this.canvas.focus();
// Taxi in die Mitte des 400x400px Tiles positionieren
this.taxi.x = 200 - this.taxi.width/2; // Zentriert in der Mitte
this.taxi.y = 200 - this.taxi.height/2; // Zentriert in der Mitte
this.taxi.angle = 0; // Fahrtrichtung nach oben
this.taxi.speed = 0; // Geschwindigkeit zurücksetzen
// Aktuelle Tile-Position setzen
this.currentTile.row = 0;
this.currentTile.col = 0;
this.generateLevel(); this.generateLevel();
this.startGame(); this.startGame();
}, },
updateCanvasSize() {
// Canvas ist immer 400x400px für ein einzelnes Tile
this.canvas.width = 400;
this.canvas.height = 400;
},
updateCurrentTile() {
// Berechne aktuelle Tile-Position basierend auf Taxi-Position
if (this.currentMap && this.currentMap.mapData) {
const mapData = this.currentMap.mapData;
const rows = mapData.length;
const cols = mapData[0] ? mapData[0].length : 0;
// Berechne Tile-Koordinaten basierend auf Taxi-Position
const tileRow = Math.floor(this.taxi.y / this.tiles.size);
const tileCol = Math.floor(this.taxi.x / this.tiles.size);
// Begrenze auf Map-Grenzen
this.currentTile.row = Math.max(0, Math.min(rows - 1, tileRow));
this.currentTile.col = Math.max(0, Math.min(cols - 1, tileCol));
// Stelle sicher, dass Taxi innerhalb des Canvas bleibt
this.taxi.x = Math.max(0, Math.min(this.canvas.width - this.taxi.width, this.taxi.x));
this.taxi.y = Math.max(0, Math.min(this.canvas.height - this.taxi.height, this.taxi.y));
}
},
setupEventListeners() { setupEventListeners() {
document.addEventListener('keydown', this.handleKeyDown); document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('keyup', this.handleKeyUp); document.addEventListener('keyup', this.handleKeyUp);
@@ -229,7 +288,6 @@ export default {
generateLevel() { generateLevel() {
this.passengers = []; this.passengers = [];
this.destinations = []; this.destinations = [];
this.gasStations = [];
this.obstacles = []; this.obstacles = [];
// Generiere Passagiere (auf Straßen) // Generiere Passagiere (auf Straßen)
@@ -256,14 +314,6 @@ export default {
}); });
} }
// Generiere Tankstellen (auf Straßen)
const fuelPosition = this.getRandomRoadPosition();
this.gasStations.push({
x: fuelPosition.x,
y: fuelPosition.y,
width: 20,
height: 20
});
// Generiere Hindernisse (außerhalb der Straßen) // Generiere Hindernisse (außerhalb der Straßen)
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
@@ -346,7 +396,7 @@ export default {
this.updateTaxi(); this.updateTaxi();
this.handlePassengerActions(); this.handlePassengerActions();
this.checkCollisions(); // this.checkCollisions(); // Temporär deaktiviert für freie Fahrt
this.render(); this.render();
// Minimap zeichnen // Minimap zeichnen
@@ -356,29 +406,30 @@ export default {
}, },
updateTaxi() { updateTaxi() {
// Bewegung basierend auf gedrückten Tasten // Beschleunigung bei gedrücktem Pfeil nach oben
if (this.keys['ArrowUp'] || this.keys['w'] || this.keys['W']) { if (this.keys['ArrowUp'] || this.keys['w'] || this.keys['W']) {
this.taxi.speed = Math.min(this.taxi.speed + 0.2, this.taxi.maxSpeed); this.taxi.speed = Math.min(this.taxi.speed + 0.015625, this.taxi.maxSpeed);
} else if (this.keys['ArrowDown'] || this.keys['x'] || this.keys['X']) {
this.taxi.speed = Math.max(this.taxi.speed - 0.2, -this.taxi.maxSpeed);
} else {
this.taxi.speed *= 0.9; // Reibung
} }
// Abbremsen bei gedrücktem Pfeil nach unten
if (this.keys['ArrowDown'] || this.keys['x'] || this.keys['X']) {
this.taxi.speed = Math.max(this.taxi.speed - 0.0625, 0); // Bremsen bis 0, nicht rückwärts
}
// Lenkung
if (this.keys['ArrowLeft'] || this.keys['a'] || this.keys['A']) { if (this.keys['ArrowLeft'] || this.keys['a'] || this.keys['A']) {
this.taxi.angle -= 0.1; this.taxi.angle -= 0.025;
} }
if (this.keys['ArrowRight'] || this.keys['d'] || this.keys['D']) { if (this.keys['ArrowRight'] || this.keys['d'] || this.keys['D']) {
this.taxi.angle += 0.1; this.taxi.angle += 0.025;
} }
// Aktualisiere Position // Aktualisiere Position
this.taxi.x += Math.cos(this.taxi.angle) * this.taxi.speed; this.taxi.x += Math.cos(this.taxi.angle) * this.taxi.speed;
this.taxi.y += Math.sin(this.taxi.angle) * this.taxi.speed; this.taxi.y += Math.sin(this.taxi.angle) * this.taxi.speed;
// Begrenze auf Canvas // Aktualisiere aktuelle Tile-Position
this.taxi.x = Math.max(0, Math.min(this.canvas.width - this.taxi.width, this.taxi.x)); this.updateCurrentTile();
this.taxi.y = Math.max(0, Math.min(this.canvas.height - this.taxi.height, this.taxi.y));
// Verbrauche Treibstoff // Verbrauche Treibstoff
if (Math.abs(this.taxi.speed) > 0.1) { if (Math.abs(this.taxi.speed) > 0.1) {
@@ -518,44 +569,68 @@ export default {
} }
}); });
// Zeichne Tankstellen
this.gasStations.forEach(station => {
this.ctx.fillStyle = '#FFC107';
this.ctx.fillRect(station.x, station.y, station.width, station.height);
});
// Zeichne Taxi // Zeichne Taxi
this.ctx.save(); this.ctx.save();
this.ctx.translate(this.taxi.x + this.taxi.width/2, this.taxi.y + this.taxi.height/2); this.ctx.translate(this.taxi.x + this.taxi.width/2, this.taxi.y + this.taxi.height/2);
this.ctx.rotate(this.taxi.angle); this.ctx.rotate(this.taxi.imageAngle + this.taxi.angle);
if (this.taxi.image) {
// Zeichne Taxi-Bild
console.log('Zeichne Taxi-Bild:', this.taxi.image, 'Position:', this.taxi.x, this.taxi.y);
this.ctx.drawImage(
this.taxi.image,
-this.taxi.width/2,
-this.taxi.height/2,
this.taxi.width,
this.taxi.height
);
} else {
// Fallback: Zeichne blaues Rechteck wenn Bild nicht geladen
console.log('Taxi-Bild nicht geladen, zeichne Fallback-Rechteck');
this.ctx.fillStyle = '#2196F3'; this.ctx.fillStyle = '#2196F3';
this.ctx.fillRect(-this.taxi.width/2, -this.taxi.height/2, this.taxi.width, this.taxi.height); this.ctx.fillRect(-this.taxi.width/2, -this.taxi.height/2, this.taxi.width, this.taxi.height);
}
this.ctx.restore(); this.ctx.restore();
}, },
drawRoads() { drawRoads() {
const tileSize = this.tiles.size; const tileSize = this.tiles.size; // 400x400px
const cols = 8; // 400px / 50px = 8 tiles
const rows = 8;
// Erstelle ein 8x8 Raster mit Straßen // Zeichne nur das aktuelle Tile
for (let row = 0; row < rows; row++) { if (this.currentMap && this.currentMap.mapData) {
for (let col = 0; col < cols; col++) { const mapData = this.currentMap.mapData;
const x = col * tileSize; const rows = mapData.length;
const y = row * tileSize; const cols = mapData[0] ? mapData[0].length : 0;
// Bestimme Tile-Typ basierend auf Position // Prüfe ob aktuelle Tile-Position gültig ist
let tileType = this.getTileType(row, col, rows, cols); if (this.currentTile.row >= 0 && this.currentTile.row < rows &&
this.currentTile.col >= 0 && this.currentTile.col < cols) {
// Zeichne Straßenregionen basierend auf Koordinaten const tileType = mapData[this.currentTile.row][this.currentTile.col];
streetCoordinates.drawDriveableRegions(this.ctx, tileType, tileSize, x, y); if (tileType) {
// Zeichne Straßenregionen für das aktuelle Tile
streetCoordinates.drawDriveableRegions(this.ctx, tileType, tileSize, 0, 0);
// Zeichne Tile-Overlay falls verfügbar // Zeichne Tile-Overlay falls verfügbar
if (this.tiles.images[tileType]) { if (this.tiles.images[tileType]) {
this.ctx.drawImage(this.tiles.images[tileType], x, y, tileSize, tileSize); this.ctx.drawImage(this.tiles.images[tileType], 0, 0, tileSize, tileSize);
} }
} }
} }
} else {
// Fallback: Zeichne Standard-Tile wenn keine Map geladen ist
const tileType = 'cornertopleft'; // Standard-Tile
// Zeichne Straßenregionen
streetCoordinates.drawDriveableRegions(this.ctx, tileType, tileSize, 0, 0);
// Zeichne Tile-Overlay falls verfügbar
if (this.tiles.images[tileType]) {
this.ctx.drawImage(this.tiles.images[tileType], 0, 0, tileSize, tileSize);
}
}
}, },
getTileType(row, col, rows, cols) { getTileType(row, col, rows, cols) {
@@ -648,6 +723,71 @@ export default {
} }
}, },
loadTaxiImage() {
const img = new Image();
img.onload = () => {
console.log('Taxi-Bild erfolgreich geladen:', img);
this.taxi.image = img;
};
img.onerror = (error) => {
console.error('Fehler beim Laden des Taxi-Bildes:', error);
console.log('Versuche Pfad:', '/images/taxi/taxi.svg');
};
img.src = '/images/taxi/taxi.svg';
console.log('Lade Taxi-Bild von:', '/images/taxi/taxi.svg');
},
async loadMaps() {
try {
const response = await apiClient.get('/api/taxi-maps/maps');
this.maps = response.data.data || [];
// Verwende die erste verfügbare Map als Standard
if (this.maps.length > 0) {
this.currentMap = this.maps[0];
this.selectedMapId = this.maps[0].id;
console.log('Geladene Map:', this.currentMap);
// Canvas-Größe an geladene Map anpassen
this.updateCanvasSize();
// Minimap neu zeichnen nach dem Laden der Map
this.$nextTick(() => {
this.drawMinimap();
});
}
} catch (error) {
console.error('Fehler beim Laden der Maps:', error);
}
},
onMapChange() {
// Wechsle zur ausgewählten Map
const selectedMap = this.maps.find(map => map.id === this.selectedMapId);
if (selectedMap) {
this.currentMap = selectedMap;
console.log('Gewechselt zu Map:', selectedMap);
// Canvas-Größe an neue Map anpassen
this.updateCanvasSize();
// Taxi in die Mitte des 400x400px Tiles positionieren
this.taxi.x = 200 - this.taxi.width/2; // Zentriert in der Mitte
this.taxi.y = 200 - this.taxi.height/2; // Zentriert in der Mitte
this.taxi.angle = 0; // Fahrtrichtung nach oben
this.taxi.speed = 0; // Geschwindigkeit zurücksetzen
// Aktuelle Tile-Position zurücksetzen
this.currentTile.row = 0;
this.currentTile.col = 0;
// Minimap neu zeichnen
this.$nextTick(() => {
this.drawMinimap();
});
}
},
drawMinimap() { drawMinimap() {
if (!this.minimapCtx) return; if (!this.minimapCtx) return;
@@ -661,38 +801,93 @@ export default {
ctx.fillStyle = '#f0f0f0'; ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
// Skalierungsfaktor (Hauptspiel zu Minimap) // Zeichne Map-Tiles aus der geladenen Map
const scaleX = canvas.width / 400; if (this.currentMap && this.currentMap.mapData) {
const scaleY = canvas.height / 400; const mapData = this.currentMap.mapData;
const rows = mapData.length;
const cols = mapData[0] ? mapData[0].length : 0;
// Zeichne Map-Tiles // Berechne optimale Tile-Größe für quadratische Tiles
const tileSize = this.tiles.size; const maxTiles = Math.max(rows, cols);
const cols = 8; const tileSize = Math.min(canvas.width / maxTiles, canvas.height / maxTiles);
const rows = 8;
// Zentriere die Map im Canvas
const offsetX = (canvas.width - cols * tileSize) / 2;
const offsetY = (canvas.height - rows * tileSize) / 2;
for (let row = 0; row < rows; row++) { for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) { for (let col = 0; col < cols; col++) {
const x = col * tileSize * scaleX; const x = offsetX + col * tileSize;
const y = row * tileSize * scaleY; const y = offsetY + row * tileSize;
const width = tileSize * scaleX;
const height = tileSize * scaleY; const tileType = mapData[row][col];
if (tileType) {
const mapTileType = 'map-' + tileType;
// Zeichne Map-Tile falls verfügbar
if (this.tiles.images[mapTileType]) {
ctx.drawImage(this.tiles.images[mapTileType], x, y, tileSize, tileSize);
}
}
}
}
} else {
// Fallback: Zeichne statische Map wenn keine Map geladen ist
const cols = 8;
const rows = 8;
// Berechne optimale Tile-Größe für quadratische Tiles
const maxTiles = Math.max(rows, cols);
const tileSize = Math.min(canvas.width / maxTiles, canvas.height / maxTiles);
// Zentriere die Map im Canvas
const offsetX = (canvas.width - cols * tileSize) / 2;
const offsetY = (canvas.height - rows * tileSize) / 2;
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const x = offsetX + col * tileSize;
const y = offsetY + row * tileSize;
let tileType = this.getTileType(row, col, rows, cols); let tileType = this.getTileType(row, col, rows, cols);
let mapTileType = 'map-' + tileType; let mapTileType = 'map-' + tileType;
// Zeichne Map-Tile falls verfügbar // Zeichne Map-Tile falls verfügbar
if (this.tiles.images[mapTileType]) { if (this.tiles.images[mapTileType]) {
ctx.drawImage(this.tiles.images[mapTileType], x, y, width, height); ctx.drawImage(this.tiles.images[mapTileType], x, y, tileSize, tileSize);
} }
} }
} }
}
// Berechne Skalierung für Passagiere, Ziele und Tankstellen
let scaleX, scaleY, offsetX, offsetY;
if (this.currentMap && this.currentMap.mapData) {
const mapData = this.currentMap.mapData;
const rows = mapData.length;
const cols = mapData[0] ? mapData[0].length : 0;
const maxTiles = Math.max(rows, cols);
const tileSize = Math.min(canvas.width / maxTiles, canvas.height / maxTiles);
scaleX = tileSize / 400; // 400 ist die neue Tile-Größe
scaleY = tileSize / 400;
offsetX = (canvas.width - cols * tileSize) / 2;
offsetY = (canvas.height - rows * tileSize) / 2;
} else {
// Fallback für statische Map
const maxTiles = 8;
const tileSize = Math.min(canvas.width / maxTiles, canvas.height / maxTiles);
scaleX = tileSize / 400;
scaleY = tileSize / 400;
offsetX = (canvas.width - 8 * tileSize) / 2;
offsetY = (canvas.height - 8 * tileSize) / 2;
}
// Passagiere (grün) // Passagiere (grün)
ctx.fillStyle = '#4CAF50'; ctx.fillStyle = '#4CAF50';
this.passengers.forEach(passenger => { this.passengers.forEach(passenger => {
if (!passenger.pickedUp) { if (!passenger.pickedUp) {
ctx.beginPath(); ctx.beginPath();
ctx.arc(passenger.x * scaleX, passenger.y * scaleY, 2, 0, 2 * Math.PI); ctx.arc(offsetX + passenger.x * scaleX, offsetY + passenger.y * scaleY, 2, 0, 2 * Math.PI);
ctx.fill(); ctx.fill();
} }
}); });
@@ -702,32 +897,29 @@ export default {
this.destinations.forEach(dest => { this.destinations.forEach(dest => {
if (!dest.completed) { if (!dest.completed) {
ctx.beginPath(); ctx.beginPath();
ctx.arc(dest.x * scaleX, dest.y * scaleY, 2, 0, 2 * Math.PI); ctx.arc(offsetX + dest.x * scaleX, offsetY + dest.y * scaleY, 2, 0, 2 * Math.PI);
ctx.fill(); ctx.fill();
} }
}); });
// Tankstellen (gelb)
ctx.fillStyle = '#FF9800';
this.gasStations.forEach(station => {
ctx.beginPath();
ctx.arc(station.x * scaleX, station.y * scaleY, 2, 0, 2 * Math.PI);
ctx.fill();
});
// Taxi (blau) // Taxi (blau) - Position basierend auf aktueller Tile-Position
ctx.fillStyle = '#2196F3'; ctx.fillStyle = '#2196F3';
ctx.beginPath(); ctx.beginPath();
ctx.arc(this.taxi.x * scaleX, this.taxi.y * scaleY, 3, 0, 2 * Math.PI); const taxiGlobalX = this.currentTile.col * 400 + this.taxi.x;
const taxiGlobalY = this.currentTile.row * 400 + this.taxi.y;
ctx.arc(offsetX + taxiGlobalX * scaleX, offsetY + taxiGlobalY * scaleY, 3, 0, 2 * Math.PI);
ctx.fill(); ctx.fill();
// Taxi-Richtung anzeigen // Taxi-Richtung anzeigen
ctx.strokeStyle = '#2196F3'; ctx.strokeStyle = '#2196F3';
ctx.lineWidth = 1; ctx.lineWidth = 1;
ctx.beginPath(); ctx.beginPath();
const endX = this.taxi.x * scaleX + Math.cos(this.taxi.angle) * 6; const taxiX = offsetX + taxiGlobalX * scaleX;
const endY = this.taxi.y * scaleY + Math.sin(this.taxi.angle) * 6; const taxiY = offsetY + taxiGlobalY * scaleY;
ctx.moveTo(this.taxi.x * scaleX, this.taxi.y * scaleY); const endX = taxiX + Math.cos(this.taxi.angle) * 6;
const endY = taxiY + Math.sin(this.taxi.angle) * 6;
ctx.moveTo(taxiX, taxiY);
ctx.lineTo(endX, endY); ctx.lineTo(endX, endY);
ctx.stroke(); ctx.stroke();
} }
@@ -887,6 +1079,9 @@ export default {
background: #f8f9fa; background: #f8f9fa;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
padding: 15px 20px; padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
} }
.minimap-title { .minimap-title {
@@ -896,6 +1091,24 @@ export default {
color: #333; color: #333;
} }
.map-selector {
flex: 0 0 auto;
}
.map-select {
padding: 5px 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 0.9rem;
background: white;
cursor: pointer;
}
.map-select:focus {
outline: none;
border-color: #F9A22C;
}
.minimap-container { .minimap-container {
padding: 15px; padding: 15px;
text-align: center; text-align: center;