Ä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-header">
<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 class="minimap-container">
<canvas
@@ -176,21 +187,29 @@ export default {
minimapCtx: null,
isStatsExpanded: true,
taxi: {
x: 200,
y: 200,
width: 20,
height: 15,
angle: 0,
x: 200, // Mitte des 400x400px Tiles
y: 200, // Mitte des 400x400px Tiles
width: 30, // Skalierte Breite (50px Höhe * 297/506)
height: 50, // 50px Höhe
angle: 0, // Fahrtrichtung
imageAngle: Math.PI / 2, // Bildrotation (90° nach rechts)
speed: 0,
maxSpeed: 3
maxSpeed: 3,
image: null // Taxi-Bild
},
currentTile: {
row: 0,
col: 0
},
tiles: {
size: 50, // 400px / 8 tiles = 50px per tile
size: 400, // 400x400px pro Tile
images: {}
},
maps: [], // Geladene Maps aus der Datenbank
currentMap: null, // Aktuell verwendete Map
selectedMapId: null, // ID der ausgewählten Map
passengers: [],
destinations: [],
gasStations: [],
obstacles: [],
keys: {}
}
@@ -199,6 +218,8 @@ export default {
this.initializeGame();
this.initializeMinimap();
this.loadTiles();
this.loadTaxiImage();
this.loadMaps();
this.setupEventListeners();
},
beforeUnmount() {
@@ -209,10 +230,48 @@ export default {
this.canvas = this.$refs.gameCanvas;
this.ctx = this.canvas.getContext('2d');
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.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() {
document.addEventListener('keydown', this.handleKeyDown);
document.addEventListener('keyup', this.handleKeyUp);
@@ -229,7 +288,6 @@ export default {
generateLevel() {
this.passengers = [];
this.destinations = [];
this.gasStations = [];
this.obstacles = [];
// 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)
for (let i = 0; i < 3; i++) {
@@ -346,7 +396,7 @@ export default {
this.updateTaxi();
this.handlePassengerActions();
this.checkCollisions();
// this.checkCollisions(); // Temporär deaktiviert für freie Fahrt
this.render();
// Minimap zeichnen
@@ -356,29 +406,30 @@ export default {
},
updateTaxi() {
// Bewegung basierend auf gedrückten Tasten
// Beschleunigung bei gedrücktem Pfeil nach oben
if (this.keys['ArrowUp'] || this.keys['w'] || this.keys['W']) {
this.taxi.speed = Math.min(this.taxi.speed + 0.2, 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
this.taxi.speed = Math.min(this.taxi.speed + 0.015625, this.taxi.maxSpeed);
}
// 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']) {
this.taxi.angle -= 0.1;
this.taxi.angle -= 0.025;
}
if (this.keys['ArrowRight'] || this.keys['d'] || this.keys['D']) {
this.taxi.angle += 0.1;
this.taxi.angle += 0.025;
}
// Aktualisiere Position
this.taxi.x += Math.cos(this.taxi.angle) * this.taxi.speed;
this.taxi.y += Math.sin(this.taxi.angle) * this.taxi.speed;
// Begrenze auf Canvas
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));
// Aktualisiere aktuelle Tile-Position
this.updateCurrentTile();
// Verbrauche Treibstoff
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
this.ctx.save();
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.fillRect(-this.taxi.width/2, -this.taxi.height/2, this.taxi.width, this.taxi.height);
}
this.ctx.restore();
},
drawRoads() {
const tileSize = this.tiles.size;
const cols = 8; // 400px / 50px = 8 tiles
const rows = 8;
const tileSize = this.tiles.size; // 400x400px
// Erstelle ein 8x8 Raster mit Straßen
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const x = col * tileSize;
const y = row * tileSize;
// Zeichne nur das aktuelle Tile
if (this.currentMap && this.currentMap.mapData) {
const mapData = this.currentMap.mapData;
const rows = mapData.length;
const cols = mapData[0] ? mapData[0].length : 0;
// Bestimme Tile-Typ basierend auf Position
let tileType = this.getTileType(row, col, rows, cols);
// Prüfe ob aktuelle Tile-Position gültig ist
if (this.currentTile.row >= 0 && this.currentTile.row < rows &&
this.currentTile.col >= 0 && this.currentTile.col < cols) {
// Zeichne Straßenregionen basierend auf Koordinaten
streetCoordinates.drawDriveableRegions(this.ctx, tileType, tileSize, x, y);
const tileType = mapData[this.currentTile.row][this.currentTile.col];
if (tileType) {
// Zeichne Straßenregionen für das aktuelle Tile
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], 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) {
@@ -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() {
if (!this.minimapCtx) return;
@@ -661,38 +801,93 @@ export default {
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Skalierungsfaktor (Hauptspiel zu Minimap)
const scaleX = canvas.width / 400;
const scaleY = canvas.height / 400;
// Zeichne Map-Tiles aus der geladenen Map
if (this.currentMap && this.currentMap.mapData) {
const mapData = this.currentMap.mapData;
const rows = mapData.length;
const cols = mapData[0] ? mapData[0].length : 0;
// Zeichne Map-Tiles
const tileSize = this.tiles.size;
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 = col * tileSize * scaleX;
const y = row * tileSize * scaleY;
const width = tileSize * scaleX;
const height = tileSize * scaleY;
const x = offsetX + col * tileSize;
const y = offsetY + row * tileSize;
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 mapTileType = 'map-' + tileType;
// Zeichne Map-Tile falls verfügbar
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)
ctx.fillStyle = '#4CAF50';
this.passengers.forEach(passenger => {
if (!passenger.pickedUp) {
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();
}
});
@@ -702,32 +897,29 @@ export default {
this.destinations.forEach(dest => {
if (!dest.completed) {
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();
}
});
// 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.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();
// Taxi-Richtung anzeigen
ctx.strokeStyle = '#2196F3';
ctx.lineWidth = 1;
ctx.beginPath();
const endX = this.taxi.x * scaleX + Math.cos(this.taxi.angle) * 6;
const endY = this.taxi.y * scaleY + Math.sin(this.taxi.angle) * 6;
ctx.moveTo(this.taxi.x * scaleX, this.taxi.y * scaleY);
const taxiX = offsetX + taxiGlobalX * scaleX;
const taxiY = offsetY + taxiGlobalY * 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.stroke();
}
@@ -887,6 +1079,9 @@ export default {
background: #f8f9fa;
border-bottom: 1px solid #ddd;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.minimap-title {
@@ -896,6 +1091,24 @@ export default {
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 {
padding: 15px;
text-align: center;