Back to July Calendar 

Previous Entry Next Entry→

July 29, 2005

 

As mentioned before, the java animation flickers because it's not double buffering.  The call to repaint does the followin:

public void update(Graphics g) {
  g.setColor(getBackgroud());
  g.fillRect(0,0,width,height);
  g.setColor(getForeground());
  paint(g);
}

The upshot of this is that the entire screen is cover with background color and then a new foreground is painted.  It's the rapid alteration of background and foreground that causes the flicker.  The cure is double-buffering.  In OpenGL we had glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)  which when combined with the idle and redisplay functions, achieve double buffering.  In java, you declare an off-screen buffer as a graphics object

Graphics offscreen;
Image image;

Once these objects are declared, they are typically allocated in init() like so:

public void init() {

   // other stuff here

   image = createImage(width,height);

   offscreen = image.getGraphics();

}

Now the paint() routine should be changed to draw to the offscreen buffer before putting it on the screen with drawImage().

public void paint(Graphics g) {

   offscreen.setColor(Color.black);

   offscreen.fillRect(0,0,300,300); //clear the buffer

   offscreen.setColor(Color.yellow);

   offscreen.fillRect(0,0,90,90);

   offscreen.fillRect(250,0,40,190);

   // more of the same here

   offscreen.setColor(Color.magenta);

   g.drawImage(image,0,0,this); //put offscreen buffer on screen

}

Now override (just delete the first three lines of) the default update() so it doesn't clear the screen:

public void update(Graphics g) {
  paint(g);
}

and you're in flicker free business! 

 

What is you want to animate a bunch of different objects in different ways, (seemingly) simultaneously?  BAJGP builds a nice object-oriented foundation for this.  What we do is build a class for any object that could be a rectangle moving around on the screen.  We need to know where it is, how big it is, what color it is and how it moves.  Here's the basic template:

class MovingRect {
  int ulCornx, ulCorny; //coordinates of upper left corner

  int width, height; //dimensions of rectangle

  Color rectColor; //color of rectangle

  //Constructor

  public MovingRect(int ulCornx, int ulCorny,
                    int width, int height,
                    Color rectColor) {
     this.ulCornx = ulCornx;
     this.ulCorny = ulCorny;
     this.width = width;
     this.height = height;
     this.rectColor = rectColor;

  }
  //methods
  public void movement() {

    //just a placeholder for now

  }
 
  public void paint(Graphics g) {
    g.setColor(rectColor);

    g.fillRect(ulCornx,ulCorny,width,height);
  }
}

You may be wondering, "What's this all about?"  It's a word that means, in this context, "this particular object that the variable or method that follows the dot operator belongs to."  So, for instance, this.ulCornx above refers to variable ulCornx that belongs to the constructor MovingRect() for the class MovingRect.  We need the word to distinguish temporary local variables from the instance variables of the same name. 

 

Another important is use of this is in the context of overloaded constructors (or other methods,)  each with a different set of parameters, or "signature."   I'm not sure why this is useful, BAJGP claims it can save typing...

 

Let's suppose that we've defined the MovingRect class, more or less as above.  The way you allocate memory on the computer for such a thing in java (pretty much like C++) is by using the keyword new. For example,

MovingRect rek = new MovingRect(13,17,19,23);

will create a 19X23 pixel MovingRect with its upper left corner at (13,17).  Note that color will be...default?

 

To get the MovingRect moving and visible, use these commands:

rek.movement();
rek.pain();

So we rebuild Broadway with double buffering and an object-oriented, cleaner foundation:

import java.applet.*;
import java.awt.*;
// rebuilt Broadway with objects
public class Mondrian2 extends Applet {
  static final int NUM_RECTS = 9;
  movingRect rek[];
  public void init() {
     System.out.println(">> init <<");
     setBackground(Color.black);
     initRectangles();
  }
  public void initRectangles() {
    // allocate dancing rectangles
    rek = new MovingRect[NUM_RECTS];
    rek[0] = new MovingRect(0,0,90,90,Color.yellow);
    rek[1] = new MovingRect(250,0,40,190,Color.yellow);
    rek[2] = new MovingRect(200,55,60,135,Color.yellow);
    rek[3] = new MovingRect(80,200,220,90,Color.blue);
    rek[4] = new MovingRect(100,10,90,80,Color.blue);
    rek[5] = new MovingRect(80,100,110,90,Color.lightGray);
    rek[6] = new MovingRect(200,0,45,45,Color.red);
    rek[7] = new MovingRect(0,100,70,200,Color.red);
    rek[8] = new MovingRect(200,55,60,135,Color.magenta);
  }
  public void start() {
    System.out.println(">> start <<");
  }
  public void paint(Graphics g) {
    for (int i=0; i<NUM_RECTS; i++) {
       rek[i].paint(g); // paint each rectangle
    }
  }
  public void stop() {
    System.out.println(">> stop <<");
  }
}
/////////////////////////////////////////////////////////////////
class MovingRect {
    int ulCornx, ulCorny; //coordinates of upper left corner
    int width, height; // width and height of rectangle
    Color rectColor; // color of rectangle
    public MovingRect(int x,int y,int w,int h,Color c) {
      this.ulCornx = x;
      this.ulCorny = y;
      this.width = w;
      this.height = h;
      this.rectColor = c;
    }
    public void movement() {
       // placeholder
    }
    public void paint(Graphics g) {
      g.setColor(rectColor);
      g.fillRect(ulCornx,ulCorny,width,height);
    }
}

Even though it looks like we've gone back to a non-animated applet, the potential for motion is now, in fact, much greater.  Each rectangle can now easily be assigned it's own motion sequence, independent of the others