Jump to content
  • Advertisement
Sign in to follow this  
  • entries
    5
  • comments
    0
  • views
    370

About this blog

The purpose of this blog is to present solutions based on Design Patterns illustrated by the development of video games.

Entries in this blog

AWT GUI Facade (5): Load a level

In this article, I propose to use the Visitor Pattern to easily load a level. The GUI facade in the previous post is used to display the level. This pattern allows (among other things) to easily browse a data structure to extract information. In our case, it is an XML file created by “Tiled Map Editor” which is analyzed to load a level in memory. This post is part of the AWT GUI Facade series I start by creating a level in XML with Tiled: You have to choose in the left panel Tile layer format as “XML”. This produces an XML file that looks like this: <map version="1.0" orientation="orthogonal" renderorder="left-up" width="16" height="16" tilewidth="16" tileheight="16" nextobjectid="1"> <tileset firstgid="1" name="advancewars-tileset1" tilewidth="16" tileheight="16" tilecount="256"> <image source="advancewars-tileset1.png" width="256" height="256"/> </tileset> <tileset firstgid="257" name="advancewars-tileset2" tilewidth="16" tileheight="16" tilecount="256"> <image source="advancewars-tileset2.png" width="256" height="256"/> </tileset> <layer name="Ground" width="16" height="16"> <data> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> <tile gid="41"/> ... <tile gid="41"/> </data> </layer> <layer name="Objects" width="16" height="16"> <data> <tile gid="0"/> <tile gid="0"/> <tile gid="0"/> <tile gid="0"/> <tile gid="0"/> ... <tile gid="0"/> <tile gid="0"/> <tile gid="0"/> </data> </layer> </map> We first have a <main> tag with global properties, then two <tileset> tags that define the tile sets used (one for the background, the other for the objects), and finally two <layer> tags that define the tiles for two layers (one for the background, the other for the objects). To decode this file, I propose to use the XML parser included in the standard Java library. It is based on the Visitor pattern, and allows you to easily work on tags without having to worry about character decoding issues. Visitor Pattern The Visitor pattern can be presented as follows: The IElement interface represents the elements of the structure. In this example, there are two possible types of objects: ElementA and ElementB. The interface requires a method like accept() which takes as argument a visitor who implements the IVisitor interface. There can be several ways to visit the structure, and so many other methods similar to accept(). Parameters can also be used to influence the traversal. The IVisitor interface is implemented by the user who wants to traverse the structured data. In general, the interface methods correspond to the different types of elements that compose it: in this example, they are the ElementA and ElementB classes. It is quite possible to imagine other cases that may interest a user, such as being at the beginning or at the end of an element. In all cases, the accept() method calls the methods of the IVisitor interface according to the cases encountered during its course. Within these methods, the user is free to view and modify the data. Load a level To read and store the level information, I define a Level class that contains this information in attributes: public class Level { private ArrayList<String> tilesetImages = new ArrayList(); private int tileWidth; private int tileHeight; private int width; private int height; private int[][][] level; private int tilesetWidth; private int tilesetHeight; private int x; private int y; ... The tilesetImages attribute contains the file names of texture images; The tileWidth and tileHeight attributes contain the width and height of a tile (16 x 16 pixels in our example); The width and height attributes contain the width and height of the level (16 x 16 cells in our example); The level attribute contains for each cell coordinate: the (x, y) tile coordinates of the tile to draw, and the tile set to use. The following attributes are only used to decode the information in the file. To decode the file, I define a LevelLoader class that implements org.xml.sax.helpers.DefaultHandler. The LevelLoader class is the equivalent of the Visitor class in the diagram above, and DefaultHandler is the equivalent of IVisitor. I’m only interested in the startElement() method, equivalent to the visitElementA() and visitElementB() methods in the diagram above. This method is called whenever the parser encounters a new opening tag: public class LevelLoader extends DefaultHandler { public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equals("tileset")) { if (tilesetImages.isEmpty()) { tileWidth = Integer.parseInt(attributes.getValue("tilewidth")); tileHeight = Integer.parseInt(attributes.getValue("tileheight")); } } else if (qName.equals("image")) { if (tilesetImages.isEmpty()) { tilesetWidth = Integer.parseInt(attributes.getValue("width")) / tileWidth; tilesetHeight = Integer.parseInt(attributes.getValue("height")) / tileHeight; } tilesetImages.add(attributes.getValue("source")); } else if (qName.equals("layer")) { if (level == null) { width = Integer.parseInt(attributes.getValue("width")); height = Integer.parseInt(attributes.getValue("height")); level = new int[height][width][3]; } x = 0; y = 0; } else if (qName.equals("tile")) { int id = Integer.parseInt(attributes.getValue("gid")); if (id != 0) { if (id >= 257) { level[x][y][2] = 1; id -= 256; level[x][y][0] = id % tilesetWidth - 1; level[x][y][1] = id / tilesetWidth - 1; } else { level[x][y][0] = id % tilesetWidth - 1; level[x][y][1] = id / tilesetWidth; } } x ++; if (x >= width) { x = 0; y ++; if (y > height) { throw new SAXException("Erreur dans le fichier"); } } } } } The method is a long discussion based on the tag encountered, whose name is placed in the qName argument. For the “tileset” and “image” cases the information relating to a set of tiles is decoded. For the “layer” case, the level is initialized, and for “tile”, the information relating to a tile is stored. The x and y attributes are used to store the coordinates of the next tile to be decoded. Finally, I define a load() method in the Level class that uses the LevelLoader class to load the level: public boolean load(String fileName) { try { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxParser = spf.newSAXParser(); XMLReader xmlReader = saxParser.getXMLReader(); xmlReader.setContentHandler(new LevelLoader()); URL fileURL = this.getClass().getClassLoader().getResource(fileName); xmlReader.parse(fileURL.toString()); return true; } catch(Exception ex) { return false; } } The Level class also has accessors/mutators (getters/setters) not listed here. Display the level To display the level, I use the GUI facade from the previous post: Level level = new Level(); if (!level.load("advancewars-map1.tmx")) { JOptionPane.showMessageDialog(null, "Error when loading advancewars-map1.tmx", "Error", JOptionPane.ERROR_MESSAGE); return; } int scale = 2; Layer backgroundLayer = gui.createLayer(); backgroundLayer.setTileSize(level.getTileWidth(),level.getTileHeight()); backgroundLayer.setTexture(level.getTilesetImage(0)); backgroundLayer.setSpriteCount(level.getWidth()*level.getHeight()); for(int y=0;y<level.getHeight();y++) { for(int x=0;x<level.getWidth();x++) { int index = x + y * level.getWidth(); backgroundLayer.setSpriteLocation(index, new Rectangle(scale*x*level.getTileWidth(), scale*y*level.getTileHeight(), scale*level.getTileWidth(), scale*level.getTileHeight())); if (level.getTileset(x, y) == 0) { Rectangle tile = new Rectangle(level.getTile(x, y), new Dimension(1,1)); backgroundLayer.setSpriteTexture(index, tile); } } } Layer groundLayer = gui.createLayer(); groundLayer.setTileSize(level.getTileWidth(),level.getTileHeight()); groundLayer.setTexture(level.getTilesetImage(1)); groundLayer.setSpriteCount(level.getWidth()*level.getHeight()); for(int y=0;y<level.getHeight();y++) { for(int x=0;x<level.getWidth();x++) { int index = x + y * level.getWidth(); groundLayer.setSpriteLocation(index, new Rectangle(scale*x*level.getTileWidth(), scale*(y-1)*level.getTileHeight(), scale*level.getTileWidth(), scale*2*level.getTileHeight())); if (level.getTileset(x, y) == 1) { Rectangle tile = new Rectangle(level.getTile(x, y), new Dimension(1,2)); groundLayer.setSpriteTexture(index, tile); } } } gui.createWindow("Load a level with the Visitor Pattern", scale*level.getTileWidth()*level.getWidth(), scale*level.getTileHeight()*level.getHeight()); The code of this post can be downloaded here: Download Now! To compile: javac com/learngameprog/awtfacade05/Main.java
To run: java com.learngameprog.awtfacade05.Main   View the full article  

phylyp

phylyp

AWT GUI Facade (4): Draw with tiles

I continue the extension of the graphic facade (see previous article), with here the addition of a very classic form of drawing for video games: tiles. These make it possible to compose an image using small images called tiles. There are several types of tiles, I propose in this article the simplest case with rectangular tiles. This post is part of the AWT GUI Facade series. For the implementation, the approach that I propose to follow is similar to the one used for images: we use a factory (Factory Method Pattern) to instantiate layers that allow drawing with tiles. On the GUIFacade main interface side, I add the following two methods, one to create a layer, and the other to draw it: For layers (Layer interface), I propose to manage the tiles with one texture image by layer, and with tiles of a fixed size. In addition, the rendering of tiles can be memorized by the layer: in doing so, it is sufficient to indicate which tiles should be displayed in which places, and then the layer will display these tiles continuously. These renderings are called in this article sprites, so as not to confuse them with tiles (e.g., a tile is just a small picture, and a sprite is a small picture displayed at a specific location on the screen). At this stage of design, layers able to renders independently may seem unnecessary: we could continuously consult the game data for rendering. Later, when it will be necessary to make multi-threaded synchronization, independent layers are a blessing. In addition, for low-level implementations (such as OpenGL), it allows you to use vertex buffers, which greatly speeds up rendering. These properties can be obtained via an interface like the following one: The getTileWidth() and getTileHeight() methods return the size of the tiles (for example, 16 by 16 pixels); The getTextureWidth() and getTextureHeight() methods return the number of tiles in the texture image, in width and height; The setTileSize() method is used to set the size of tiles; The setTexture() method is used to define and load the texture image; The setSpriteCount() method is used to set the number of sprites managed by the layer; The setSpriteTexture() method is used to define the tile used by a sprite. The tile rectangle defines the used tile: they are coordinates in tiles, and not in pixels. For example, if the tile size is 16×16 pixels, and the tile rectangle is (3,4,1,2), then the tile location in pixels is (3 * 16,4 * 16,1 * 16,2 * 16) = (48,64,16,32). By convention, if the rectangle argument is null, then the sprite is disabled; The setSpriteLocation() method is used to define the location where the sprite is drawn. The rectangle is in pixels, and may be larger or smaller than the tiles used, to achieve a zoom effect. AWT Implementation For the implementation, we still use the AWT library, with the AWTLayer class (the methods of the Layer interface are not repeated): The tileWidth, tileHeight, textureWidth, and textureHeight attributes define the size of the tiles and the texture image; The texture attribute contains the texture image; The texture and location arrays contain all sprite information: tiles to use and render locations; The draw() method draws the entire layer. Most methods are accessors / mutators (getters / setters) whose implementation is very simple. The draw () method is less obvious: public void draw(Graphics graphics) { for (int i = 0; i < locations.length; i++) { if (textures[i] != null) { graphics.drawImage(texture, locations[i].x, locations[i].y, locations[i].x + locations[i].width, locations[i].y + locations[i].height, textures[i].x * tileWidth, textures[i].y * tileHeight, (textures[i].x+textures[i].width) * tileWidth, (textures[i].y+textures[i].height) * tileHeight, null); } } } All sprites are traversed (l.2), if a sprite is well defined (l.3), we draw an image (l.4-13). Lines 5 to 8 define the rendering location, in pixels: it is the coordinates of the upper left corner and the lower right corner of the rectangle. Lines 9 to 12 define a rectangle in the texture image: all coordinates are multiplied by tile size (tileWidth, tileHeight). Use case To illustrate this facade with tile drawing, I propose to consider two layers: one for the background, and the other for the buildings. In both cases, we consider the same tile size of 16 × 16 pixels, and the same area of rendering (the level of the game) of 17 × 17 tiles. In addition, a scale factor is used to zoom tiles. These parameters are placed in variables: int scale = 2; int tileWidth = 16; int tileHeight = 16; int levelWidth = 17; int levelHeight = 17; For the background layer, each sprite uses only one tile, and we always use the same one: Layer backgroundLayer = gui.createLayer(); backgroundLayer.setTileSize(tileWidth,tileHeight); backgroundLayer.setTexture("advancewars-tileset1.png"); backgroundLayer.setSpriteCount(levelWidth*levelHeight); for(int y=0;y<levelHeight;y++) { for(int x=0;x<levelWidth;x++) { int index = x + y * levelWidth; backgroundLayer.setSpriteLocation(index, new Rectangle(scale*x*tileWidth, scale*y*tileHeight, scale*tileWidth, scale*tileHeight)); backgroundLayer.setSpriteTexture(index, new Rectangle(new Point(7,0), new Dimension(1,1))); } } Lines 1-4 instantiate and initialize the layer. Then, for each cell of the level (l, 5-6), we associate a sprite (l, 7). To achieve this association, it is necessary to define a unique number for each cell of the level: to do this, the cells are numbered from left to right then from top to bottom. The rendering area of each sprite is defined in lines 8-9, which is a simple scaling: everything is multiplied by the tileWidth, tileHeight, and the global scale. Finally, the tile is selected in lines 10-11, with the 8th column of the first line of the texture Point (7,0) and the size of a tile Dimension (1,1) The following layer is used to display buildings, with the particularity that these have a height of two tiles in height: Layer groundLayer = gui.createLayer(); groundLayer.setTileSize(tileWidth,tileHeight); groundLayer.setTexture("advancewars-tileset2.png"); groundLayer.setSpriteCount(2); groundLayer.setSpriteLocation(0, new Rectangle(scale*8*tileWidth, scale*6*tileHeight, scale*tileWidth, scale*2*tileHeight) ); groundLayer.setSpriteTexture(0, new Rectangle(new Point(0,2), new Dimension(1,2))); groundLayer.setSpriteLocation(1, new Rectangle(scale*8*tileWidth, scale*7*tileHeight, scale*tileWidth, scale*2*tileHeight) ); groundLayer.setSpriteTexture(1, new Rectangle(new Point(0,4), new Dimension(1,2))); Lines 1-4 instantiate the layer and initialize its properties: tile size, texture image, and sprite numbers (2). Lines 6 through 11 define the properties of the first sprite (index 0): its render location is at cell (8,6) of the grid (l. 7), its render size equals a tile width and two heights of tiles (l. 8). Its tiles are at coordinates (0,2) in the texture and use a tile of width one and height two (l. 11). Lines 13 to 18 define the properties of the second sprite: it is drawn on the cell just below, and its tiles start at the coordinates (0,4) in the texture. If all goes well, the top of the second building sprite should cover the bottom of the first: Finally, in the game’s main loop, displaying layers is as easy as displaying an image: if (gui.beginPaint()) { gui.drawLayer(backgroundLayer); gui.drawLayer(groundLayer); gui.endPaint(); } The code of this article can be downloaded here: awtfacade04.zip Some improvements have been made compared to the version of the previous article: it uses a canvas and limits the number of images per second. These improvements are purely related to the problems of video games, and does not change the concepts presented above. To compile: javac com/learngameprog/awtfacade04/Main.java
To run: java com.learngameprog.awtfacade04.Main Next post in this series   View the full article  

phylyp

phylyp

 

AWT GUI Facade (3): Display an image

The facade seen in the previous article has only an interface with its methods. It is also possible to enrich a facade with additional interfaces, connected to each other by various means. To illustrate it, I propose to add image management. This addition also makes it possible to present another design pattern: the Factory Method Pattern. This post is part of the AWT GUI Facade series. In graphic libraries, it is generally not possible to directly access the raw data of an image, it is always necessary to go through an identifier or a class to apply some processing. Knowing this, a relevant approach is to encapsulate what makes it possible to process the image in a dedicated class, for example: Compared to the previous article, the following methods have been added to the GUIFacade interface: The createImage() method creates an Image from the name of a file. In doing so, we are using the Factory Method pattern, which allows us to abstract the creation of objects, and whose creation process depends on inheritance. In this case, the user of the createImage() method does not know which implementation of the Image interface will be used. This choice depends on the class that implements GUIFacade interface, whose details are not necessarily known to the user, since it is a facade.
Once again, the idea is to free the user of specific features, and let him focus on the problem he has to deal with, in our case the rendering of a 2D tile game. The drawImage() method is used to draw an image at (x, y) coordinates. Other methods dedicated to the creation and processing of images can be imagined, these are only examples among others. The Image interface is used to represent access to an image managed by the graphics library. Methods can be proposed to work with an image. In our example, the getWidth() and getHeight() methods return the width and height of the image. Other methods can be imagined, such as a draw() method that would draw the image, instead of the one within the GUIFacade interface. For the implementation of the facade, we still use the AWT library. For the Image interface implementation, an AWTImage class is proposed: public class AWTImage implements Image { private BufferedImage image; @Override public int getWidth() { if (image == null) { throw new RuntimeException("The image was not loaded"); } return image.getWidth(); } @Override public int getHeight() { if (image == null) { throw new RuntimeException("The image was not loaded"); } return image.getHeight(); } void loadImage(String fileName) { try { image = ImageIO.read(this.getClass().getClassLoader().getResource(fileName)); } catch (IOException ex) { throw new RuntimeException("Error when reading "+fileName); } } void draw(Graphics graphics,int x,int y) { graphics.drawImage(image, x, y, null); } } The AWTImage class holds an image reference to a java.awt.image.BufferedImage. getWidth() and getHeight() methods return the width and height of the image. The other two methods are not part of the facade, and are invisible to its users. They are used by the AWTGUIFacade class to load the image (loadImage() method) and display it in a java.awt.Graphics (draw() method): public class AWTGUIFacade implements GUIFacade { ... @Override public Image createImage(String fileName) { AWTImage image = new AWTImage(); image.loadImage(fileName); return image; } @Override public void drawImage(Image image, int x, int y) { if (!(image instanceof AWTImage)) throw new IllegalArgumentException("Invalid image type"); AWTImage awtImage = (AWTImage) image; awtImage.draw(graphics,x,y); } } This new version of the facade can be used in the following way to display a window with an image: public static void run(GUIFacade gui) { Image titleImage = gui.createImage("title.png"); gui.createWindow("AWT Graphic User Interface Facade", titleImage.getWidth(),titleImage.getHeight()); while(!gui.isClosingRequested()) { if (gui.beginPaint()) { gui.drawImage(titleImage,0,0); gui.endPaint(); } } gui.dispose(); } The image is created from the file “title.png” (1. 2), a window of the size of the image is also created (l.3), and as long as the game is not finished (l. 5), the drawing is started (1. 6), the picture is displayed (1. 7) and the drawing is ended (1. 8). The code of this post can be downloaded here: awtfacade03.zip To compile: javac com/learngameprog/awtfacade03/Main.java
To run: java com.learngameprog.awtfacade03.Main Next post in this series View the full article  

phylyp

phylyp

 

AWT GUI Facade (2): Draw

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. Each feature is managed by a batch of methods in a GUIFacade interface. This post is part of the AWT GUI Facade series. The GUIFacade interface can be represented in the following way: The first four methods handle the window: The createWindow() method builds the window with a given title; The setClosingRequested() method is used to define whether the game should be completed; The isClosingRequested() method lets you know if the game needs to be completed; The dispose() method destroys the window and all associated elements. The last three methods allow you to draw in the window: The beginPaint() method starts the drawing, and returns true if it is possible; The drawLine() method draws a line. This is an example of a drawing method, a whole collection can be imagined to draw rectangles, circles, etc .; The 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(). In addition, 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. These methods can be used 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 are used to 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. In the first one, the current buffer is obtained (1.37), then a java.awt.Graphics is created from it (1.40). In both situations, various reasons may cause issues, in which case false is returned. 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 window closing is requested by the user, the facade is notified that the game must be completed (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: awtfacade02.zip To compile: javac com/learngameprog/awtfacade02/Main.java
To run: java com.learngameprog.awtfacade02.Main Next post in this series View the full article  

phylyp

phylyp

 

AWT GUI Facade (1): display a window

In this series, I propose to discover the Facade Pattern. In short, this pattern defines an interface that allows a reduced and simplified use of a set of features. To illustrate, we design a facade of the AWT graphic library for a 2D tile game. This post is part of the AWT GUI Facade series. Let’s start with the simplest facade with a unique createWindow() method that allows the user to create a new window: Creating a window with this facade is very simple: we instantiate an implementation of the facade (l.1), and call the createWindow() method (l.2): GUIFacade gui = new AWTGUIFacade(); gui.createWindow("AWT GUI Facade"); In this example, I propose to implement a window creation with the AWT library: other implementations are possible without changing the GUIFacade interface and its use. An AWTWindow class that implements java.awt.Frame is defined to handle our window: The createWindow() method of the AWTGUIFacade class instantiates an AWTWindow (l .4), initializes it with a title (l.5), places it in the center of the screen (l.6) and makes it visible (l. 7): public class AWTGUIFacade implements GUIFacade { @Override public void createWindow(String title) { AWTWindow window = new AWTWindow(); window.init(title); window.setLocationRelativeTo(null); window.setVisible(true); } } The AWTWindow class has an init() method that initializes the window with a given title (l.3), a size of 640 by 480 pixels (l.4), prohibits resizing (l.5), and ensures that the window is destroyed when closed (L.6-11): public class AWTWindow extends Frame { public void init(String title) { setTitle(title); setSize(640, 480); setResizable(false); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent we) { dispose(); } }); } } The creation of the window is split between the createWindow() method of the AWTGUIFacade class and the init() method of the AWTWindow class. The main motivation here is to separate the more dynamic operations, such as the position of the window, from more static operations, such as the title or the fact that the window is not designed to be resized. There are many other possibilities depending on the possible extensions of the facade. With this facade, a user can easily create a window for a specific use, without having to know all the methods of a graphic library. Moreover, apart from the instantiation of a layout of the facade (i.e., new AWTGUIFacade()), all the following code no longer depends on the graphic library (the gui variable in the first example code is of type GUIFacade). This means that it is possible to switch from one implementation to another, and therefore from one graphic library to another. This property is very interesting when you want to cross-platform, or if the first choice of the graphic library was not good. In any case, we can switch from one library to another without having to rewrite the entire application! This article presents a very simple first approach, and presents the basics of the facade pattern. In the following articles, things will become more complex, and you will see what can be achieved from a pattern whose principle seems so simple! The code of this post can be downloaded here: awtface01.zip To compile: javac com/learngameprog/awtfacade01/Main.java
To run: java com.learngameprog.awtfacade01.Main Next post in this series View the full article  

phylyp

phylyp

Sign in to follow this  
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!