Home | Gaming | Programming | Play Online | Contact | Keyword Query
Games++ Games & Game Programming

GAMES++
Games++ Home
Games++ Gaming
Games++ Programming
Beta Testing Games
Free Online Games
Hints & Cheats

BROWSER UTILITIES
E-mail This Page
Add to Favorites

SITE SEARCH

Web Games++

AFFILIATES
Cheat Codes
Trickster Wiki
Game Ratings
Gameboy Cheats
PlayStation Cheats
BlackBerry Games
Photoshop Tutorials
Illustrator Tutorials
ImageReady Tutorials

ADVERTISEMENT

ADVERTISEMENT

Part 4: What About Double Buffering?

Java Game Programming Tutorial

Getting Started

To program in the Java language, you will need to install the free Java Development Kit (JDK) on your system.

You can find the latest version of the JDK, documentation, and online tutorials here at the JDK Download Site.

You will also require a Java-enabled web browser to view the tutorial examples.

My aim is not to teach every aspect of Java programming but to help those with a bit of programming knowledge with graphics and game programming issues in Java relating to web pages and the internet. I hope you find it helpful.

Download javaGameProgrammingTutorialClasses.zip to receive the following classes:

  • DBuffer.class
  • Flicker.class
  • FXCycle.class
  • FXImage.class
  • Hello.class
  • Lines.class

Part 4: What About Double Buffering?

Now we have images loaded and doing something the next topic on the agenda is flicker. Although simplistic animations or slideshows run fairly well, as more drawing is done to the viewing window in advanced applets, one can begin to see the screen refresh causing a flicker.

I would say this is the bane of the animator or game developer because many solutions exist on a variety of platforms to speed up rendering. Either built into the hardware or programmed in the software, the most common solution is double buffering.

Double buffering is the process of storing a copy of the screen in a section of memory and doing all drawing to this canvas as if it were the screen. Since all drawing is being done off the screen, the only drawing onscreen is the actual copying of the buffer to the screen which can be timed to avoid the refresh.

Some systems or environments have automatic buffering which means little or no programming. My experience in low level DOS has taught me this can mean extensive coding for speedy animation. Thankfully, in Java it is really only a matter of redirection from what we've doing so far.

These lessons are compressed to be building blocks for bigger and better applets. Otherwise, there might be ten versions of Hello world (yikes!) and I refuse to insult your intelligence this way. At this juncture, I must cram more information about sprite images relevant to the example.

A good thing about gif files is that you can make transparent gifs quite easily using many paint programs and image utilities. This fact allows gif images which aren't rectangular to keep their fine figure and blend in with whatever background we like.

In Part 3, an image array was created to load in each sketch or picture. This is alright for a slideshow of variable images but bad in the case of small identical images. Bad because each image requires an established HTTP connection which can be slow. A good solution is to store your images side by side like so:

Only a single image connection is required for this image strip which can be drawn easily with a slight adjustment. Since the previous tutorial sections cover applet structure and graphics techniques, I will just elaborate on new or changed code.

  Image offscreenImage;
  Graphics offscreenGraphics;

These two objects are added to the applet to implement double buffering. The first is the familiar image object which serves as the image buffer itself. The second is a graphics context or handle for reference to the image buffer.

  Image imageBackground; 
  Image spriteStrip;

These are just less important images for drawing. The first is a background image to make a backdrop for the sprite. The next is the sprite image strip. I've decided to omit explaining the init method. Image loading was discussed in Part 3.

    offscreenImage = createImage(this.size().width,
                                 this.size().height);
    offscreenGraphics = offscreenImage.getGraphics();

This conditional initialization code is added to the paint method. The method createImage is used to create an offscreen buffer image the size of the applet window. The image method getGraphics is used to get a graphics context. This is excellent because we can draw to the double buffer exactly as if it were the screen.

    spriteWidth = spriteStrip.getWidth(this) / spriteCount;
    spriteHeight = spriteStrip.getHeight(this);

Here, the variable spriteCount is used to calculate the independent image width, assuming each is the same width. The height is the same as the strip height because it is a row of images.

    offscreenGraphics.drawImage(imageBackground,0,0,this);

This is the same method used in Part 3 but note how our own graphics context is used. We could draw lines or circles, etc. In this case, our background image is copied into the offscreen buffer. Redrawing the background every frame is rather inefficient but I'll go over a better design later. For this purpose, it's okay.

    offscreenGraphics.clipRect(spriteX,spriteY,
                              spriteWidth,spriteHeight);

This code above makes a clipping window in our offscreen buffer in the position of our sprite image. We want it to be the size of only a single image frame. This way, the picture we want is drawn and not the entire image strip.

    offscreenGraphics.drawImage(spriteStrip,
                                spriteX+(-spriteIndex * spriteWidth),
                                spriteY,this);

Drawing the sprite is the same as drawing any image. Note above that the only difference is the x coordinate. We are shifting the strip of images left to get the image we want. If we didn't create a clipping rectangle first, the image would just move left. The image strip is like a large banner being carried back and forth behind a small window. Considering the image download speed increases, this is worthwhile.

    g.drawImage(offscreenImage,0,0,this);

And finally, here is the only call to the screen graphics context which draws the updated offscreen buffer just like any normal image.

Here's the HTML parameters:

<APPLET CODE="DBuffer.class" WIDTH=104 HEIGHT=64>
<PARAM NAME="SPRITEDELAY" VALUE="200">
<PARAM NAME="SPRITEX" VALUE="45">
<PARAM NAME="SPRITEY" VALUE="20">
<PARAM NAME="SPRITECOUNT" VALUE="4">
<PARAM NAME="SPRITENAME" VALUE="guy">
<PARAM NAME="BACKGROUNDNAME" VALUE="skullbg">
</APPLET>

The example applet parameters allow you to insert your own background image and animated character at an (x,y) position within the applet window. If you alter the applet, you can make your character walk around in front of a logo by changing spriteX and spriteY in the run method infinite loop. Below is the entire Java code. Enjoy!

// DBuffer.java
// by Garry Morse
 
import java.awt.*;
import java.applet.*;
 
public class DBuffer extends Applet implements Runnable {
 
  Thread spriteThread;
  MediaTracker tracker;
 
  Image offscreenImage;
  Graphics offscreenGraphics;
 
  Image imageBackground;
  Image spriteStrip;
 
  int spriteDelay=0, spriteCount=0, spriteIndex=0;
  int spriteWidth=0,spriteHeight=0;
  int spriteX=0,spriteY=0;
 
  String paramString;
 
  public void init() {
 
    // create a media tracker object for this applet
    tracker = new MediaTracker(this);
 
    // get parameter for wait spriteDelay between images
    paramString = getParameter("SPRITEDELAY");
    spriteDelay = Integer.parseInt(paramString);
 
    // get parameter for number of separate images in strip
    paramString = getParameter("SPRITECOUNT");
    spriteCount = Integer.parseInt(paramString);
 
    // get parameter for x position of sprite
    paramString = getParameter("SPRITEX");
    spriteX = Integer.parseInt(paramString);
 
    // get parameter for y position of sprite
    paramString = getParameter("SPRITEY");
    spriteY = Integer.parseInt(paramString);
 
    // get parameter for sprite image file and load strip
    paramString = getParameter("SPRITENAME");
    spriteStrip = getImage(getCodeBase(),paramString + ".gif");
 
    // get parameter for background image file and load image
    paramString = getParameter("BACKGROUNDNAME");
    imageBackground = getImage(getCodeBase(),paramString + ".gif");
 
    // add images to monitored images with IDs
    tracker.addImage(spriteStrip,0);
    tracker.addImage(imageBackground,1);
 
    // load images into memory
    tracker.checkID(0,true);
    tracker.checkID(1,true);
 
    repaint();
  }
 
  public void start() {
    if(spriteThread==null) {
      spriteThread = new Thread(this);
      spriteThread.start();
    }
  }
 
  public void stop() {
    if(spriteThread!=null) {
      spriteThread.stop();
      spriteThread = null;
    }
  }
 
  public void update(Graphics g) {
    paint(g);
  }
 
  public void paint(Graphics g) {
 
    // if images not loaded yet, inform user
    if(!tracker.checkAll()) {
      g.drawString("loading images...",
                   20,this.size().height/2);
    }
    else {
 
      if(offscreenImage==null) {
        // set up double buffer
        offscreenImage = createImage(this.size().width,
                                     this.size().height);
        offscreenGraphics = offscreenImage.getGraphics();
      }
 
      // get dimensions of sprite
      spriteWidth = spriteStrip.getWidth(this) / spriteCount;
      spriteHeight = spriteStrip.getHeight(this);
 
      // draw background and sprite in offscreen buffer
      offscreenGraphics.drawImage(imageBackground,0,0,this);
      offscreenGraphics.clipRect(spriteX,spriteY,
                                 spriteWidth,spriteHeight);
      offscreenGraphics.drawImage(spriteStrip,
                                  spriteX+(-spriteIndex * spriteWidth),
                                  spriteY,this);
 
      // copy offscreen buffer to screen
      g.drawImage(offscreenImage,0,0,this);
    }
 
  }
 
  public void run() {
    while(true) {
      
      repaint();
 
      // reloop through list of images to display
      if(++spriteIndex >= spriteCount) spriteIndex=0;
 
      // try to shut down spriteThread for milliseconds
      try {
        spriteThread.sleep(spriteDelay+1);
      }
      catch(InterruptedException e) { }
 
    }
  }
 
}
Copyright © 1998-2007, Games++ All rights reserved. | Privacy Policy