Files
racing-game/src/visual/Track.java

217 lines
5.9 KiB
Java

package visual;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import model.Game;
import model.car.BasicCar;
import model.map.Circuit;
import model.map.Map;
/**
* <code>Track</code> est une vue graphique représentant le circuit de course
* ainsi que les voitures qui y circulent.
* Cette classe hérite de <code>GameView</code> pour être intégrée au système de vues.
* <p>
* Le rendu est basé sur une grille définie par <code>Map</code> et chaque cellule
* est dessinée selon son type. Les voitures sont superposées sur la grille.
* </p>
*/
public class Track extends GameView {
/** La carte du circuit */
private Map map;
/** Liste des voitures à dessiner */
private ArrayList<BasicCar> cars;
/** Échelle utilisée pour ajuster la taille des voitures dans les cellules */
private final int scale = 80;
/**
* Construit la vue Track avec une carte et une liste de voitures.
*
* @param map La carte du circuit
* @param cars Liste des voitures
* @param title Titre de la fenêtre
* @param width Largeur de la fenêtre
* @param height Hauteur de la fenêtre
* @param x Position X de la fenêtre
* @param y Position Y de la fenêtre
*/
protected Track(Game game, String title, int width, int height, int x, int y)
{
super(game, title, width, height, x, y);
map = game.getMap();
cars = game.getCars();
}
/**
* Méthode de dessin appelée automatiquement par Swing.
* Dessine chaque cellule de la carte ainsi que les voitures.
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int rows = map.getHeight();
int cols = map.getWidth();
// Calcul de la taille d'une cellule en pixels selon la taille de la fenêtre
int[] cellSize = new int[] {
(getWidth() + 10) / cols,
(getHeight() + 10) / rows
};
// Définir une police monospace en gras pour les caractères du circuit
g.setFont(new Font("Monospaced", Font.BOLD, cellSize[1] / 2));
// Parcours de toutes les cellules du circuit
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
Circuit cell = this.map.getElement(x, y);
drawCell(g, cell, new int[] {x, y}, cellSize);
}
}
}
/**
* Retourne la couleur d'une cellule selon son type.
*/
private Color getCellColor(Circuit.Cell cell) {
return switch (cell) {
case ROAD -> Color.GRAY;
case START -> Color.YELLOW;
case FINISH -> Color.YELLOW;
case EMPTY -> Color.GREEN;
case YROAD -> Color.YELLOW;
};
}
/**
* Retourne le caractère à afficher pour une cellule du circuit.
*/
private char getCellChar(Circuit cell) {
return switch (cell.getType()) {
case ROAD -> '\0';
case START -> 'D';
case FINISH -> 'A';
case EMPTY -> '\0';
case YROAD -> (char)('0' + cell.getValue());
};
}
/**
* Dessine toutes les voitures sur une cellule donnée.
* Les voitures sont légèrement décalées pour ne pas avoir exactement la même taille.
*/
private void drawCars(Graphics g, int[] cellCoord) {
int size = 1;
for (BasicCar c : cars) {
int i = c.getPos();
Point p = this.map.getPath(i);
int[] ncoord = new int[] {p.x, p.y};
drawInnerBlock(g, ncoord, cellCoord, c.getColor(), scale - size);
size += 3; // pour différencier visuellement les voitures
}
}
/**
* Dessine une cellule complète avec son contenu (caractère et voitures).
*/
private void drawCell(Graphics g, Circuit cell, int[] coord, int[] cellCoord) {
drawBlock(g, cell, coord, cellCoord);
char c = getCellChar(cell);
if (c != '\0') {
drawCharacter(g, coord, cellCoord, c);
}
drawCars(g, cellCoord);
}
/**
* Dessine un caractère centré dans une cellule.
*/
private void drawCharacter(Graphics g, int[] coord, int[] cellCoord, char c) {
int x = coord[0];
int y = coord[1];
int cellWidth = cellCoord[0];
int cellHeight = cellCoord[1];
String s = c + "";
g.setColor(Color.BLACK);
FontMetrics fm = g.getFontMetrics();
// taille du string + font
int textWidth = fm.stringWidth(s);
// hauteur haute par exemple: h depasse en haut
int textHeight = fm.getAscent();
// hauteur basse par exemple: g depasse en bas
int descent = fm.getDescent();
int cx = x * cellWidth + (cellWidth - textWidth) / 2;
int cy = y * cellHeight + (cellHeight + textHeight - descent) / 2;
g.drawString(s, cx, cy);
}
/**
* Dessine un bloc intérieur plus petit, utilisé pour représenter une voiture
*/
private void drawInnerBlock(Graphics g, int[] coord, int[] cellCoord, Color c, int scale) {
// scale de la taille du bloc
int w = cellCoord[0] * scale / 100;
int h = cellCoord[1] * scale / 100;
// calcul de la position avec la nouvelle taille
int ox = coord[0] * cellCoord[0] + (cellCoord[0] - w) / 2;
int oy = coord[1] * cellCoord[1] + (cellCoord[1] - h) / 2;
// dessine le bloc plus petit
g.setColor(c);
g.fillRect(ox, oy, w, h);
}
/**
* Dessine une cellule de base selon son type et ajoute un contour noir.
*/
private void drawBlock(Graphics g, Circuit cell, int[] coord, int[] cellCoord) {
int x = coord[0];
int y = coord[1];
int cellWidth = cellCoord[0];
int cellHeight = cellCoord[1];
switch (cell.getType()) {
case YROAD:
// dessine le bloc de route
g.setColor(getCellColor(Circuit.Cell.ROAD));
g.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight);
// dessine le sous bloc de route
drawInnerBlock(g, coord, cellCoord, getCellColor(cell.getType()), scale);
break;
default:
g.setColor(getCellColor(cell.getType()));
g.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight);
break;
}
// contour noir
g.setColor(Color.BLACK);
g.drawRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight);
}
/**
* Méthode appelée automatiquement pour mettre à jour l'affichage.
*/
@Override
public boolean apply()
{
repaint();
return true;
}
}