Back to July Calendar 

Previous Entry Next Entry→

July 25, 2005

 

Normal text here.

 

How about some java animation: deliverable to you, gentle reader, in the comfort of your own viewing pod?

 

The basic double buffering scheme in pseudocode might look like this
draw picture and loop the following:

1. put picture in frame

2. draw next picture

3. pause for some time

end loop

How about a java animation?  I've got here the first animation example from the Black Art of Java Game Programming, a book I find thoroughly enjoyable to read: the title kind of says it all.  One performs various incantations and magic happens.  Wow!  I will pass on the author's apologies to Mondrian, but I think it's pretty nifty how when the gray rectangle moves, it reveals a yellow one hiding behind it! 

The code for this is shown below.  I've added some color coding to mimic how it appears in the Java Creator freeware I got for working on applet creation.  I recommend it, it works for me! 

The updateRectangle() function is what's called a state machine and dictates the motion of the gray rectangle in one of four directions, depending on wether it's crossed one or another threshold.

Note that the init(), start(), paint(), and stop() methods are extant in the same stages of the applets process as they were for the static case of yesterday.

 There is one new method: run(), which essentially implements in java the pseudocode for double buffering described above.  The repaint() method is inherited from the Applet mother class.  It clears the screen and calls paint()
Calling updateRectangle() has the obvious effect of changing the state so that when paint() is called, something different is painted.  Note that the fillRect() method for the lightGray rectangle has variable parameters for its position and that it is precisely these parameters that updateRectangle() fiddles with...with which it fiddles.  Finally, the sleep() method of the Thread class is called to pause for 100 milliseconds, as outlined in the pseudocode above. 

 

If you've been wondering, Runnable is an interface which requires that the run() method be called, but it is not inherited...which is why we need to supply it.

 

So the start() method calls the thread called animation and tells it to start executing the code in run() by calling animation.start().  Got that?  Hmm, may take some getting used to.  The path of execution is sort of split into two different threads when this happens, with the animation thread operating independently, except that stop() will stop it. 

 

In truth, we are not yet using proper double buffering, and that explains the flickering effect you may have noticed.  Tomorrow, maybe an honest to goodness double buffer.

 

import java.applet.*;
import java.awt.*;

public class Broadway extends Applet implements Runnable {

Thread animation;
int locx,locy; // location of rectangle
int width, height; // dimensions of rectangle
static final byte UP = 0; // direction of motion
static final byte DOWN = 1;
static final byte LEFT = 2;
static final byte RIGHT = 3;

byte state; // state the rect is in

static final int REFRESH_RATE = 100; // in ms

public void init() {

System.out.println(">> init <<");
setBackground(Color.black);
locx = 80;
locy = 100;
width = 110;
height = 90;
state = UP;

}

public void start() {

System.out.println(">> start <<");
animation = new Thread(this);
if (animation != null) {

   animation.start()
}

}


public void paint(Graphics g) {

System.out.println(">> paint <<");

g.setColor(Color.yellow);
g.fillRect(0,0,90,90);
g.fillRect(250,0,40,190);
g.fillRect(80,110,100,20); // hidden rectangle

g.setColor(Color.blue);
g.fillRect(80,200,220,90);
g.fillRect(100,10,90,80);

g.setColor(Color.lightGray);
g.fillRect(locx,locy,width,height);

g.setColor(Color.red);
g.fillRect(200,0,45,45);
g.fillRect(0,100,70,200);

g.setColor(Color.magenta);
g.fillRect(200,55,60,135);

}


void updateRectangle() {
   switch (state) {
     case DOWN:
       locy += 2;
       if (locy >= 110) {
          state = UP;
       }
       break;
     case UP:
       locy -= 2;
       if (locy <= 90) {
          state = RIGHT;
       }
       break;
     case RIGHT:
       locx += 2;
       if (locx >= 90) {
          state = LEFT;
       }
       break;
     case LEFT:
       locx -= 2;
       if (locx <= 70) {
          state = DOWN;
       }
       break;
   }
}

public void run() {

   while (true) {
      repaint();
      updateRectangle();
      try {
         Thread.sleep (REFRESH_RATE);
      } catch (Exception exc) { };
   }
}

public void stop() {
   System.out.println(">> stop <<");
   if (animation != null) {
      animation.stop();
      animation = null;
   }
}

}