In this article, I propose to continue the design of a facade for a 2D tile game (previous post). I add two new features: creation/destruction of the window, and basic drawing. We manage each feature using methods in a GUIFacade interface.
This post is part of the AWT GUI Facade series.
I design the GUIFacade
interface in the following way:
The first four methods handle the window:
createWindow()
method builds the window with a given title;setClosingRequested()
method defines whether the game should be completed;isClosingRequested()
method lets you know if the user wants to end the game;dispose()
method destroys the window and all associated elements.The last three methods allow you to draw in the window:
beginPaint()
method starts the drawing, and returns true if it is possible;drawLine()
method draws a line. This is an example of a drawing method, we can imagine many others to draw rectangles, circles, etc .;endPaint()
method completes the drawing.Most graphic libraries used to follow these steps where you have to start by "preparing" the drawing with a method like beginPaint()
, then "free" it with a method like endPaint()
. Besides, there is usually a form of blocking between these two calls, which means that you have to draw "as quickly as possible" if you want to offer a good user experience.
I propose to use these methods as follows: the window is created (l.2) and we repeat until the game is finished (l.3). If it is possible to start drawing (l.4), draw a line (l.5) and finish the drawing (l.6). Finally, the window is destroyed (l. 9):
public static void run(GUIFacade gui) {
gui.createWindow("AWT GUI Facade");
while(!gui.isClosingRequested()) {
if (gui.beginPaint()) {
gui.drawLine(100, 150, 600, 350);
gui.endPaint();
}
}
gui.dispose();
}
Note: In this example, there is no limitation on the number of frames per second. This can saturate a processor core and cause minor issues on some configurations. We'll see in another post how to get a better frame rate.
The use of the facade does not depend on its implementation: it is possible to use different implementations, for example, the AWT library included in the standard Java library:
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();
}
}
The window attribute is a reference to an AWTWindow class that inherits from java.awt.Frame, shown below. The closingRequested
attribute is true if the game must end. The bufferStrategy
and graphics
attributes handle double buffering rendering.
The init()
method is the same as in the previous article, except for the creation of a double buffer with window.createBufferStrategy(2)
. The setClosingRequested()
and isClosingRequested()
methods manage the closingRequested
attribute. The dispose()
method controls the destruction of the window.
The beginPaint()
and endPaint()
methods handle the display cycle. The first one obtains the current buffer (1.37), and a java.awt.Graphics
is created from it (1.40). In both situations, various reasons may cause issues, in which case we return false
. Then, the end of the beginPaint()
method erases all the content: this approach can be interesting or not, depending on the case. The endPaint()
method destroys graphics (1. 56) and inverts the two buffers (1. 57). Finally, the drawLine()
method uses graphics to draw a line: it's easy to imagine other use cases for drawing rectangles, circles, and so on.
The AWTWindow
class is an AWT window initialized by the init()
method with a specific configuration. When the user requests the window closing, we ask to stop the game (1. 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);
}
});
}
}
Note: The rendering method is simple but not optimal: it is better to use canvas to draw inside a window. This implementation may cause some undesirable effects.
The code of this post can be downloaded here:
To compile: javac com/learngameprog/awtfacade02/Main.java
To run: java com.learngameprog.awtfacade02.Main