Projet Bomberman JavaFX — Jeu d’Action avec Architecture MVC
Présentation du projet
Bomberman JavaFX est une version personnalisée du célèbre jeu d’arcade Bomberman, développée entièrement en Java avec JavaFX. Le jeu propose une expérience complète avec déplacement du joueur.
Le projet met l’accent sur l’utilisation avancée de JavaFX, la programmation orientée objet, les collections observables, les liaisons de propriétés (bindings), et l’architecture MVC (Modèle-Vue-Contrôleur).
Objectifs du projet
- Développer une interface graphique complexe avec animations fluidessimple, carte dynamique générée aléatoirement et interface utilisateur réactive.
- Implémenter des mouvements automatisés pour les ennemis
- Appliquer l’architecture MVC dans un contexte de jeu temps réel
- Gérer les animations temporelles avec Timeline pour les déplacements
🎮 Fonctionnalités implémentées
Système de jeu principal
- Déplacement du joueur contrôlé par les touches fléchées
- Déplacement automatique des ennemis via Timeline JavaFX avec mouvements aléatoires
- Carte dynamique générée avec murs indestructibles et briques destructibles
- Gestion des collisions avec validation des tuiles marchables
- Placement aléatoire des personnages sur des cases libres
Système de personnages
- Joueur principal avec 3 points de vie
- Ennemis diversifiés : balloon, ghost, robot avec 1 point de vie chacun
- Animation continue des ennemis toutes les secondes
- Intelligence artificielle simple avec déplacements aléatoires dans 4 directions
Interface utilisateur dynamique
- Affichage en temps réel avec liaisons automatiques (bindings)
- Grille de jeu responsive générée dynamiquement
- Superposition des personnages sur la carte via overlay
- Gestion des événements clavier pour les contrôles
🏗️ Architecture technique
Structure MVC appliquée
Modèle (Model)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
// Classe abstraite pour tous les personnages
public abstract class AbstractCharacter {
private IntegerProperty row;
private IntegerProperty column;
private int healthPoints;
public abstract String getName();
public void setPosition(int row, int column) {
this.row.set(row);
this.column.set(column);
}
}
// Joueur principal
public class Player extends AbstractCharacter {
public Player() {
super(3); // 3 points de vie
}
@Override
public String getName() {
return "player";
}
}
// Ennemis avec IA automatisée
public class Enemy extends AbstractCharacter {
private final String name;
private Timeline timeline;
private GameFacade game;
public Enemy(String name) {
super(1); // 1 point de vie
this.name = name;
}
public void animate() {
this.timeline = new Timeline(
new KeyFrame(Duration.seconds(1), e -> moveRandomly())
);
this.timeline.setCycleCount(Animation.INDEFINITE);
this.timeline.play();
}
private void moveRandomly() {
if (game == null) return;
int direction = (int) (Math.random() * 4);
switch (direction) {
case 0 -> game.moveEnemyUp(this);
case 1 -> game.moveEnemyDown(this);
case 2 -> game.moveEnemyLeft(this);
case 3 -> game.moveEnemyRight(this);
}
}
}
|
Vue (View)
Interface réactive avec bindings automatiques :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public class BombermanController implements IBombermanController {
@FXML private GridPane gridMap;
@FXML private Pane overlayPane; // Superposition pour les personnages
private GameFacade facade;
private ImageView playerView;
private List<ImageView> enemyViews;
@Override
public void setPlayer(AbstractCharacter player) {
this.player = player;
// Création de l'image du joueur
playerView = new ImageView(loadImage(player.getName()));
playerView.setFitWidth(TAILLE_CASE);
playerView.setFitHeight(TAILLE_CASE);
// Liaisons automatiques position -> affichage
playerView.xProperty().bind(
Bindings.multiply(player.columnProperty(), TAILLE_CASE)
);
playerView.yProperty().bind(
Bindings.multiply(player.rowProperty(), TAILLE_CASE)
);
overlayPane.getChildren().add(playerView);
}
@Override
public void addEnemy(Enemy enemy) {
ImageView enemyView = new ImageView(loadImage(enemy.getName()));
// Liaisons automatiques des ennemis
enemyView.xProperty().bind(
Bindings.multiply(enemy.columnProperty(), TAILLE_CASE)
);
enemyView.yProperty().bind(
Bindings.multiply(enemy.rowProperty(), TAILLE_CASE)
);
enemyViews.add(enemyView);
overlayPane.getChildren().add(enemyView);
// Démarrage de l'animation automatique
enemy.animate();
}
}
|
Contrôleur/Façade
Coordination centrale du jeu :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
public class GameFacade {
private Player player;
private List<Enemy> enemies;
private GameMap gameMap;
private IBombermanController controller;
public void startNewGame() {
// Création du joueur
player = new Player();
// Génération de la carte avec placement du joueur
gameMap = GameMapFactory.createMapWithRandomBrickWallsAndPlayer(
11, 13, 30, player
);
// Création des ennemis
enemies.clear();
createEnemies();
// Configuration de l'affichage
if (controller != null) {
controller.initializeMapView(gameMap);
controller.setPlayer(player);
// Placement et ajout des ennemis
for (Enemy enemy : enemies) {
placeCharacterRandomly(enemy);
controller.addEnemy(enemy);
}
}
}
private void createEnemies() {
String[] enemyNames = {"balloon", "ghost", "robot"};
int numberOfEnemies = 3;
for (int i = 0; i < numberOfEnemies; i++) {
String enemyName = enemyNames[i % enemyNames.length];
Enemy enemy = new Enemy(enemyName);
enemy.setGame(this); // Référence pour les déplacements
enemies.add(enemy);
}
}
public boolean moveCharacter(AbstractCharacter character, Direction direction) {
int newRow = character.getRow();
int newCol = character.getColumn();
// Calcul de la nouvelle position
switch (direction) {
case UP -> newRow--;
case DOWN -> newRow++;
case LEFT -> newCol--;
case RIGHT -> newCol++;
}
// Vérification des limites et walkability
if (gameMap.isOnMap(newRow, newCol) &&
gameMap.get(newRow, newCol).isWalkable()) {
// Mise à jour des tuiles
Tile oldTile = gameMap.get(character.getRow(), character.getColumn());
oldTile.setCharacter(null);
character.setPosition(newRow, newCol);
Tile newTile = gameMap.get(newRow, newCol);
newTile.setCharacter(character);
return true;
}
return false;
}
}
|
🛠️ Technologies et concepts avancés
Bindings et Observables JavaFX
1
2
3
4
5
6
7
8
9
10
11
12
|
// Liaison automatique des positions des personnages
playerView.xProperty().bind(
Bindings.multiply(player.columnProperty(), TAILLE_CASE)
);
// Liaison dynamique des images de tuiles
imageView.imageProperty().bind(
Bindings.createObjectBinding(
() -> loadImage(tile.getContent().getName()),
tile.contentProperty()
)
);
|
Animation avec Timeline
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public void animate() {
this.timeline = new Timeline(
new KeyFrame(Duration.seconds(1), e -> moveRandomly())
);
this.timeline.setCycleCount(Animation.INDEFINITE);
this.timeline.play();
}
public void stopAnimation() {
if (timeline != null) {
timeline.stop();
}
}
|
Gestion des événements
1
2
3
4
5
6
7
8
9
10
11
|
private void handleKeyPressed(KeyEvent event) {
if (facade != null) {
switch (event.getCode()) {
case UP -> facade.movePlayerUp();
case DOWN -> facade.movePlayerDown();
case LEFT -> facade.movePlayerLeft();
case RIGHT -> facade.movePlayerRight();
}
}
event.consume();
}
|
🎯 Points techniques remarquables
Architecture modulaire et extensible
- Séparation claire des responsabilités avec l’architecture MVC
- Façade centralisée (GameFacade) pour simplifier les interactions
- Système d’ennemis extensible avec différents types (balloon, ghost, robot)
- Interface découplée via IBombermanController
Gestion temps réel avec JavaFX
- Timeline automatiques pour les mouvements des ennemis
- Bindings reactifs pour la synchronisation interface-modèle
- Gestion thread-safe des mises à jour graphiques
Interface utilisateur dynamique
- GridPane dynamique généré selon la taille de la carte
- Overlay Pane pour superposer les personnages sur la carte
- Placement aléatoire intelligent évitant les collisions
- Chargement dynamique des images depuis les resources
- Collections observables pour les mises à jour efficaces
- Placement optimisé des personnages sur tuiles libres
- Gestion mémoire des Timeline et animations
- Validation systématique des déplacements
📚 Structure du projet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
src/main/java/
├── model/
│ ├── AbstractCharacter.java # Classe de base pour tous les personnages
│ ├── Player.java # Entité joueur
│ ├── Enemy.java # Ennemis avec IA automatisée
│ ├── Direction.java # Énumération des directions
│ ├── Tile.java # Cases de la carte
│ └── GameMap.java # Gestion de la carte de jeu
├── facade/
│ └── GameFacade.java # Façade principale du jeu
├── controller/
│ ├── IBombermanController.java # Interface du contrôleur
│ └── BombermanController.java # Contrôleur principal JavaFX
└── resources/
└── images/
├── player.png # Sprite du joueur
├── balloon.png # Sprite ennemi balloon
├── ghost.png # Sprite ennemi ghost
├── robot.png # Sprite ennemi robot
├── wall.png # Mur indestructible
└── brick.png # Brique destructible
|
📈 Résultats et apprentissages
Ce projet m’a permis de maîtriser :
- Les bindings JavaFX pour des interfaces automatiquement synchronisées
- La gestion d’animations continues avec Timeline et KeyFrame
- L’architecture MVC appliquée à un jeu temps réel
- Les collections observables pour la gestion dynamique des ennemis
- La programmation orientée objet avec héritage
- L’optimisation des performances graphiques en JavaFX
- La gestion des événements utilisateur en temps réel
- Le placement algorithmique d’éléments sur une grille
Le résultat est une application de jeu fluide et extensible qui démontre une maîtrise approfondie de JavaFX, des animations temps réel et des bonnes pratiques de développement orienté objet.
