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; /** * Track est une vue graphique représentant le circuit de course * ainsi que les voitures qui y circulent. * Cette classe hérite de GameView pour être intégrée au système de vues. *

* Le rendu est basé sur une grille définie par Map et chaque cellule * est dessinée selon son type. Les voitures sont superposées sur la grille. *

*/ public class Track extends GameView { /** La carte du circuit */ private Map map; /** Liste des voitures à dessiner */ private ArrayList 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; } }