Managing the Bricks


Wraparound Ribbons

A Ribbon object manages a wraparound, movable image, which should be wider than the game panel. This width requirement is important for the amount of work needed to draw the image as it wraps around the JPanel.

A wide image means that its display on-screen requires, at most, two drawImage( ) calls (with associated calculations for the coordinates and image dimensions): one to draw the tail of the image on the left side and the other for its start on the right. If the image is narrower than the panel, then three drawImage( ) calls (or more) might be needed, with an increase in the number of calculations.

Furthermore, if the panel width is constant, as here, then some parts of the calculations need only be carried out once and can be reused after that.

The constructor of this class initializes the graphic, its moveSize value, two movement flags, and a position variable called xImHead:

     // globals     private BufferedImage im;     private int width;      // the width of the image (>= pWidth)     private int pWidth, pHeight;    // dimensions of display panel     private int moveSize;       // size of the image move (in pixels)     private boolean isMovingRight;  // movement flags     private boolean isMovingLeft;     private int xImHead;   // panel position of image's left side     public Ribbon(int w, int h, BufferedImage im, int moveSz)     {       pWidth = w; pHeight = h;       this.im = im;       width = im.getWidth( );    // no need to store the height       if (width < pWidth)         System.out.println("Ribbon width < panel width");       moveSize = moveSz;       isMovingRight = false;   // no movement at start       isMovingLeft = false;       xImHead = 0;     }

xImHead holds the x-coordinate in the panel where the left side of the image (its head) should be drawn.

The isMovingRight and isMovingLeft flags determine the direction of movement for the Ribbon image (or whether it is stationary) when its JPanel position is updated. The flags are set by the moveRight( ), moveLeft( ), and stayStill( ) methods:

     public void moveRight( )     // move the ribbon image to the right on the next update     { isMovingRight = true;       isMovingLeft = false;     }

update( ) adjusts the xImHead value depending on the movement flags. xImHead can range between -width to width (where width is the width of the image):

     public void update( )     { if (isMovingRight)         xImHead = (xImHead + moveSize) % width;       else if (isMovingLeft)         xImHead = (xImHead - moveSize) % width;     }

As xImHead varies, the drawing of the ribbon in the JPanel will usually be a combination of the image's tail followed by its head.

Drawing the Ribbon's Image

The display( ) method does the hard work of deciding where various bits of the image should be drawn in the JPanel.

One of the hard aspects of display( ) is that it utilizes two different coordinate systems: JPanel coordinates and image coordinates. This can be seen in the many calls to Graphics' 10-argument drawImage( ) method:

     boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,                                  int sx1, int sy1, int sx2, int sy2,                                        ImageObserver observer);

Figure 12-7 shows that the eight integers represent two regions: the destination JPanel and source image.

Figure 12-7. Drawing a region with drawImage( )


Fortunately, in JumpingJack, the regions are always the same height, starting at the top edge of the JPanel (y == 0) and extending to its bottom (y == pHeight). However, dx1 and dx2 vary in the JPanel, and sx1 and sx2 vary in the image.

The x-coordinates are derived from the current xImHead value, which ranges between width and -width as the image is shifted right or left across the JPanel. As the image moves right (or left), there will come a point when it'll be necessary to draw the head and tail of the image to cover the JPanel.

These considerations lead to display( ) consisting of five cases; each is detailed in the following sections:

     public void display(Graphics g)     {       if (xImHead == 0)   // draw im start at (0,0)         draw(g, im, 0, pWidth, 0, pWidth);       else if ((xImHead > 0) && (xImHead < pWidth)) {          // draw im tail at (0,0) and im start at (xImHead,0)         draw(g, im, 0, xImHead, width-xImHead, width);   // im tail         draw(g, im, xImHead, pWidth, 0, pWidth-xImHead);  // im start       }       else if (xImHead >= pWidth)   // only draw im tail at (0,0)         draw(g, im, 0, pWidth,                     width-xImHead, width-xImHead+pWidth);  // im tail       else if ((xImHead < 0) && (xImHead >= pWidth-width))         draw(g, im, 0, pWidth, -xImHead, pWidth-xImHead);  // im body       else if (xImHead < pWidth-width) {          // draw im tail at (0,0) and im start at (width+xImHead,0)         draw(g, im, 0, width+xImHead, -xImHead, width);  // im tail         draw(g, im, width+xImHead, pWidth,                     0, pWidth-width-xImHead);  // im start       }     } // end of display( )     private void draw(Graphics g, BufferedImage im,                           int scrX1, int scrX2, int imX1, int imX2)     /* The y-coords of the image always starts at 0 and ends at        pHeight (the height of the panel), so are hardwired. */     { g.drawImage(im, scrX1, 0, scrX2, pHeight,                        imX1, 0,  imX2, pHeight, null);     }

Case 1: Draw the image at JPanel (0,0)

The relevant code snippet from display( ):

     if (xImHead == 0)   // draw im start at (0,0)       draw(g, im, 0, pWidth, 0, pWidth);

Figure 12-8 illustrates the drawing operation.

Figure 12-8. Case 1 in Ribbon's display( )


Case 1 occurs at startup time, when the scene is first drawn, and reoccurs when Jack has run around the width of the image and xImHead is back at 0.

draw( ) is a simplified interface to drawImage( ), hiding the fixed y-coordinates (0 to pHeight). Its third and fourth arguments are the x-coordinates in the JPanel (the positions pointed to in the top gray box in Figure 12-8). The fifth and sixth arguments are the positions pointed to in the image ribbon (the box at the bottom of the figure).

Case 2: Image moving right, where xImHead is less than pWidth

Here's the code fragment from display( ) for this case:

     if ((xImHead > 0) && (xImHead < pWidth)) {        // draw im tail at (0,0) and im head at (xImHead,0)       draw(g, im, 0, xImHead, width-xImHead, width);   // im tail       draw(g, im, xImHead, pWidth, 0, pWidth-xImHead);  // im head     }

Figure 12-9 illustrates the drawing operations.

Figure 12-9. Case 2 in Ribbon's display( )


When the image moves right (caused by the sprite apparently moving left), the JPanel drawing will require two drawImage( ) calls: one for the tail of the image and the other for the head (which still begins at xImHead in the JPanel).

The tricky part is calculating the x-coordinate of the start of the image's tail and the x-coordinate of the end of the head.

Case 3: Image moving right, where xImHead is greater than or equal to pWidth

Here's the relevant piece of code:

     if (xImHead >= pWidth)   // only draw im tail at (0,0)       draw(g, im, 0, pWidth, width-xImHead, width-xImHead+pWidth);

Figure 12-10 shows the drawing operation.

Case 3 happens after Case 2 as the image moves even further to the right and xImHead TRavels beyond the right edge of the JPanel. This means only one drawImage( ) call is

Figure 12-10. Case 3 in Ribbon's display( )


necessary to draw the middle part of the image into the JPanel. The tricky x-coordinates are the start and end points for the image's middle.

Case 4: Image moving left, where xImHead is greater than or equal to (pWidth-width)

This is the relevant code snippet:

     if ((xImHead < 0) && (xImHead >= pWidth-width))         draw(g, im, 0, pWidth, -xImHead, pWidth-xImHead);  // im body

Figure 12-11 illustrates the drawing operation.

Figure 12-11. Case 4 in Ribbon's display( )


Case 4 occurs when the image is moving left, which happens when the sprite apparently travels to the right. xImHead will become negative since it's to the left of JPanel's origin. One drawImage( ) is needed for the middle of the image even though it is still greater than (pWidth - width).

Case 5. Image moving left, where xImHead is less than (pWidth-width)

Here's the code for this case:

     if (xImHead < pWidth-width) {        // draw im tail at (0,0) and im head at (width+xImHead,0)       draw(g, im, 0, width+xImHead, -xImHead, width);  // im tail       draw(g, im, width+xImHead, pWidth, 0, pWidth-width-xImHead);  // im head     }

Figure 12-12 shows the drawing operations.

Figure 12-12. Case 5 in Ribbon's display( )


Case 5 occurs after Case 4 when the image has moved further to the left and xImHead is smaller than (pWidth-width). This distance marks the point at which two drawImage( ) calls are required, one for the tail of the image and the other for its head.



Killer Game Programming in Java
Killer Game Programming in Java
ISBN: 0596007302
EAN: 2147483647
Year: 2006
Pages: 340

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net