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.List; import model.Game; import model.car.Car; 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 List cars; /** Échelle utilisée pour ajuster la taille des voitures dans les cellules */ private final int scale = 80; @Override protected void init(Game game) { super.init(game); map = game.getMap(); cars = game.getCars(); } /** * 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 */ public Track(Game game, String title, int width, int height, int x, int y) { super(game, title, width, height, x, y); if (game != null) init(game); } /** * 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(); // Taille d'un cube sur la fenetre Point size = new Point( (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, size.y / 2)); drawCells(g, size); drawCars(g, size); } /** * Dessine une cellule complète avec son contenu (caractère et voitures). */ private void drawCells(Graphics g, Point squareSize) { for (int y = 0; y < map.getHeight(); y++) { for (int x = 0; x < map.getWidth(); x++) { Point pos = new Point(x, y); Circuit element = map.getElement(x, y); drawBlock(g, element, pos, squareSize); } } } /** * 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, Point squaresize) { int size = cars.size(); for (int i = 0; i < size; i++) { Car car = cars.get(i); Point p = map.getPath(car.getPosition()); drawInnerBlock(g, p, squaresize, car.getColor(), scale / size, 2 + i * (squaresize.y / size)); } } /** * Dessine une cellule de base selon son type et ajoute un contour noir. */ private void drawBlock(Graphics g, Circuit element, Point coord, Point squareSize) { g.setColor(switch (element.getType()) { case ROAD -> Color.GRAY; case EMPTY -> Color.GREEN; default -> Color.YELLOW; }); g.fillRect( coord.x * squareSize.x, coord.y * squareSize.y, squareSize.x, squareSize.y); // contour noir g.setColor(Color.BLACK); g.drawRect(coord.x * squareSize.x, coord.y * squareSize.y, squareSize.x, squareSize.y); drawCharacter(g, element, coord, squareSize); } /** * Dessine un caractère centré dans une cellule. */ private void drawCharacter(Graphics g, Circuit element, Point coord, Point squareSize) { String character = switch (element.getType()) { case START -> 'D'; case FINISH -> 'A'; case YROAD -> (char) ('0' + element.getValue()); default -> ""; } + ""; if (character.isEmpty()) return; g.setColor(Color.BLACK); FontMetrics fm = g.getFontMetrics(); // taille du string + font int textWidth = fm.stringWidth(character); // hauteur haute par exemple: h depasse en haut int textHeight = fm.getAscent(); // hauteur basse par exemple: g depasse en bas int descent = fm.getDescent(); Point cp = new Point( coord.x * squareSize.x + (squareSize.x - textWidth) / 2, coord.y * squareSize.y + (squareSize.y + textHeight - descent) / 2); g.drawString(character, cp.x, cp.y); } /** * Dessine un bloc intérieur plus petit, utilisé pour représenter une voiture */ private void drawInnerBlock(Graphics g, Point coord, Point squareSize, Color c, int scale, int y) { // scale de la taille du bloc Point size = new Point( squareSize.x * scale / 100, squareSize.y * scale / 100); // calcul de la position avec la nouvelle taille Point pos = new Point( coord.x * squareSize.x + (squareSize.x - size.x) / 2, coord.y * squareSize.y + y); // dessine le bloc plus petit g.setColor(c); g.fillRect(pos.x, pos.y, size.x, size.y); } /** * Méthode appelée automatiquement pour mettre à jour l'affichage. */ @Override public boolean apply() { if (!super.apply()) { System.out.println("ERREUR Track: game est null"); return false; } repaint(); return true; } }