GRAPHICS FOR THE HILO GAME



In this section of the tutorial, we are going to add graphics to the Hilo game we created earlier. This time we are going to add graphics to our Hilo game to display the cards as graphical cards rather than as text. The card images are going to be gif files. This section of the tutorial is more advanced than previous sections, and it assumes some knowledge of graphics programming. If you are a novice to graphics programming, then the material in this section might not be clearly understood. If you are a novice, this section will help you understand how to load gif or jpeg files and display them. For the advanced user, this section will be helpful in learning how to set up clipping regions and double-buffered graphics.

THE PROGRAM

Click on hilo-graphical to see it work. Click on code to see the code for the applet.

THE UPDATE() METHOD

Recall that the update() method is invoked as a result of invoking the repaint() method. As illustrated in the previous example, we override the update() method with our own.

THE update() CODE


1:  public void update ( Graphics g) {
2: 
3:         // Creating offscreen buffer image
4:         if ( offScrImg == null )
5:             offScrImg = createImage( size().width, size().height );
6: 
7:         // Creating offscreen graphics context
8:         Graphics og = offScrImg.getGraphics();
9: 
10:        if (painting) {
11:            // Set up clipping region
12:            og.clipRect(5, 175, (5+(iwidth*2)+10+iwidth), (iheight));
13:            g.clipRect(5, 175, (5+(iwidth*2)+10+iwidth), (iheight));
14: 
15:            // Clear the screen to blue
16:            og.setColor(Color.blue);
17:            og.fillRect(0, 0, size().width, size().height);
18: 
19:            GetImages();  // Get the images for the cards 
20: 
21:            // if you have an image for card1 draw it
22:            if (card1_image != null)
23:                og.drawImage(card1_image, 5, 175, iwidth, iheight, this);
24: 
25:            // if you have an image for card2 draw it
26:            if (card2_image != null)
27:              og.drawImage(card2_image, (5+((iwidth *2)+10)), 175, iwidth, 
28:								iheight, this);
29: 
30:			// Only draw the third card if you are supposed to
31:            if (paint_third)
32:            {
33:              if (og.drawImage(card3_image, (5+iwidth+5), 175, iwidth, 
34:					iheight, this))
35:                {
36:                    paint_third = false;
37:                }
38:            }
39:            // Copy offscreen buffer to display
40:            g.drawImage(offScrImg, 0, 0, this);
41:        }
42:    }

ANALYSIS OF THE CODE

First, let's look at lines 4 and 5. In line 4, the reference to the offscreen buffer is checked to see if it is null. If it is null, a new offscreen buffer is created with the same size and height as the current size of the applet. We are going to use this offscreen buffer to draw the image into memory before we copy it to the screen buffer. This procedure is called double-buffering. The idea is that you draw the object into memory and then perform a copy operation to draw the image on the screen. This way the drawing will be drawn a little faster. If you are going to do any complex graphics in Java, a double-buffered scheme is highly recommended. Now line 8's getGraphics() call simply requests a graphics context for the offscreen image so that we can have the drawing methods and have a place to draw.

Line 10 (if (painting)) { makes sure that this is not the first execution of the update() method (because we have no cards to display when the application first starts up). You might wonder why we do this check in update() when update() is only called after a repaint() call. The reason is that if you look at our paint() method, you will notice that it simply calls the update() method. Remember that the paint() method is called when the image is first displayed, and we do not want to display any cards until the user presses the deal button.

In line 12 and line 13, we implement another powerful graphical technique of setting up a clipping region. This technique will speed up the graphical displaying of the cards even more. We already know that the only new drawing that we will be doing is on the bottom of the screen where we display the cards. The rest of the window remains static. Since we know this fact, we can set up a clipping region around the three cards. This region will cause the clearing of the screen and the other graphic calls to apply only to this area of the screen. This procedure cuts out all of the unnecessary redrawing of the static images.

If you notice, we have to set up clipping regions both for the offscreen graphics context and the onscreen graphics context. This is done by applying the clipRect(x, y, width, height) method to both graphics context (og and g).

Lines 16 and 17 simply clear the screen to blue.

Line 19 calls the GetImages() method which loads the graphical cards from their location on disk.

GetImages()


    void GetImages () {
        // Get images for cards1 and 2
        // Get image for card3 if it is needed

        card1 = getImage(getCodeBase(), "cards/"+game.deck.card1_file);
        card2 = getImage(getCodeBase(), "cards/"+game.deck.card2_file);

        if (game.card3_file != null)
            card3 = getImage(getCodeBase(), "cards/"+game.deck.card3_file);
    }


As you can see, the call to the getImage() method is the call that loads the gif or jpeg file from disk. The parameters to getImage() are the URL and the file name. In this instance, we use the getCodeBase() call to get the URL. This method simply returns a URL relative to the applet's current location. The next parameter specifies that the file name is located in the current applet location under the directory "cards". The getImage() method returns an Image object.

Next, we'll look at line 23 og.drawImage(card1_image, 5, 175, iwidth, iheight, this);

This line is where the actual drawing of the gif file takes place. The call specifies the image to draw, the x and y coordinates, the width and the height, and a reference to a default ImageObserver object. This object asynchronously observes the object while it's being drawn, noting whether the image is finished drawing or not along with some other details of the drawing.

Finally, let's discuss line 42 g.drawImage(offScrImg, 0, 0, this);

This method will draw the completed offscreen image to the display graphics context. Notice that this version of drawImage() no longer has parameters for width and height. The other drawImage() method used earlier can be used to scale objects, while this one draws the object to its saved dimensions.

To call the update() method we call the repaint() method when we want to display the first two cards and then again when we want to display the third card.

CONCLUSION

This section provided a fast and furious overview of some advanced graphical techniques implemented in Java. Hopefully the example provided enough information so that experienced graphics programmers are prepared to efficiently implement graphics programs in Java by using the standard AWT libraries.