Dans cet article, je vous propose de poursuivre la conception d’une façade pour un jeux 2D en tuiles (article précédent). Nous allons ajouter deux types de services: l’un pour gérer le cycle de création et de destruction de la fenêtre, et l’autre pour dessiner. Chaque service est géré par un lot de méthodes dans l’interface GUIFacade
:
Les quatre premières méthodes gèrent la vie de la fenêtre:
- La méthode
createWindow()
fabrique la fenêtre avec un titre donné; - La méthode
setClosingRequested()
permet de définir si le jeu doit être terminé; - La méthode
isClosingRequested()
permet de savoir si le jeu doit être terminé; - La méthode
dispose()
détruit la fenêtre et tout les éléments associés.
Les trois dernières méthodes permettent de dessiner dans la fenêtre:
- La méthode
beginPaint()
permet de démarrer le dessin, et renvoietrue
si cela est possible; - La méthode
drawLine()
dessine une ligne. C’est un exemple parmi d’autres de méthode de dessin, toute une collection peut être imaginée pour dessiner des rectangles, des cercles, etc.; - La méthode
endPaint()
permet de terminer le dessin.
La plupart des bibliothèques graphiques suivent ce type de schéma où il faut commencer par “préparer” le dessin avec une méthode comme beginPaint()
, puis le “libérer” avec une méthode comme endPaint()
. En outre, il y a généralement une forme de blocage entre ces deux appels, ce qui signifie qu’il faut dessiner “au plus vite” si on souhaite proposer une expérience utilisateur agréable.
Ces méthodes peuvent être exploitées de la manière suivante: la fenêtre est créée (l. 2) et on répète tant que le jeu n’est pas terminé (l. 3). S’il est possible de commencer à dessiner (l. 4), on dessine une ligne (l. 5) puis on termine le dessin (l. 6). Enfin, la fenêtre est détruite (l. 9):
public static void run(GUIFacade gui) { gui.createWindow("Facade librairie graphique AWT"); while(!gui.isClosingRequested()) { if (gui.beginPaint()) { gui.drawLine(100, 150, 600, 350); gui.endPaint(); } } gui.dispose(); }
Il n’y a pas de limitation du nombre d’images par secondes. Cela peut saturer un cœur de processeur et poser des soucis mineurs sur certaines configurations.
L’utilisation de la façade ne dépend pas de son implantation: il est possible d’exploiter différentes implantations, par exemple la bibliothèque AWT incluse dans la bibliothèque standard de Java:
public class AWTGUIFacade implements GUIFacade { private AWTWindow window; private boolean closingRequested = false; private BufferStrategy bufferStrategy; private Graphics graphics; @Override public void createWindow(String title) { window = new AWTWindow(this); window.init(title); window.setLocationRelativeTo(null); window.setVisible(true); window.createBufferStrategy(2); } @Override public void setClosingRequested(boolean b) { closingRequested = b; } @Override public boolean isClosingRequested() { return closingRequested; } @Override public void dispose() { window.dispose(); } @Override public boolean beginPaint() { bufferStrategy = window.getBufferStrategy(); if (bufferStrategy == null) return false; graphics = bufferStrategy.getDrawGraphics(); if (graphics == null) return false; graphics.setColor(Color.black); graphics.fillRect(0, 0, window.getWidth(), window.getHeight()); return true; } @Override public void drawLine(int x1, int y1, int x2, int y2) { graphics.setColor(Color.white); graphics.drawLine(x1, y1, x2, y2); } @Override public void endPaint() { graphics.dispose(); bufferStrategy.show(); } }
L’attribut window
est une référence vers une classe AWTWindow
qui hérite de java.awt.Frame
, présentée ci-dessous. L’attribut closingRequested
est true
si le jeu doit se terminer. Les attributs bufferStrategy
et graphics
permettent de gérer un rendu par double tampon.
La méthode init()
est la même que dans l’article précédent, sinon la création d’un double tampon d’affichage avec la ligne window.createBufferStrategy(2);
. Les méthodes setClosingRequested()
et isClosingRequested()
sont des assesseurs de l’attribut closingRequested
. La méthode dispose()
commande la destruction de la fenêtre window
.
Les méthodes beginPaint()
et endPaint()
gèrent le cycle d’affichage. Pour la première, le tampon actuel est obtenu (l. 37), puis un java.awt.Graphics
est déduit de celui-ci (l. 40). Dans les deux cas, diverses raisons peuvent interdire ces obtentions, d’où le renvoie d’une valeur false
lorsque ceux-ci se présentent. La fin de la méthode beginPaint()
efface tout le contenu: cette approche peut être intéressante ou non, suivant les cas. La méthode endPaint()
détruit graphics
(l. 56) et inverse les deux tampons (l. 57). Enfin, la méthode drawLine()
utilise graphics
pour dessiner une ligne: il est facile d’imaginer d’autres cas d’utilisation pour dessiner des rectangles, des cercles, etc.
La classe AWTWindow
est une fenêtre AWT initialisée par la méthode init()
avec une configuration précise. Lorsque sa fermeture est demandée par l’utilisateur, par exemple en cliquant sur la croix dans son cadre, la façade est informée que le jeu doit être terminé (l. 16):
public class AWTWindow extends Frame { private final AWTGUIFacade gui; public AWTWindow(AWTGUIFacade gui) { this.gui = gui; } public void init(String title) { setTitle(title); setSize(640, 480); setResizable(false); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent we) { gui.setClosingRequested(true); } }); } }
La méthode de rendu est simple mais non optimale: il est préférable d’utiliser des canvas pour dessiner à l’intérieur d’une fenêtre. Il n’est pas impossible que ce code produise quelques effets indésirables sur certaines configurations.
Le code de cet article peut être téléchargé ici.
Pour compiler, saisissez: javac fr/phtools/awtfacade02/Main.java
Pour lancer, saisissez: java fr.phtools.awtfacade02.Main