When creating animated applets, it is important to double-buffer your animation to avoid flicker. Flicker can trigger seizures in certain individuals, and is caused because the human eye is fast enough to see the screen being drawn. If you are not prone to seizures, an example of an applet with bad flicker is provided. By double-buffering the animation, you can see that an applet with eliminated flicker appears to run much smoother.
There are several techniques for double-buffering, with the easiest already being built into Java. JApplets, by default, double-buffer, so extending it solves your problem (you should still avoid any blinking images in your Applet).
If, for some reason, you do not have the option of working with Swing components, you can still manually double-buffer. The algorithm behind double-buffering is:
drawImage(
) method to display that image. You may be thinking that this would produce the same flickering example, but drawing an image is much quicker than drawing all of the individual lines, arcs, and whatever else.
The real nemesis here is the public void update ( ) method. This is an inherited method that clears the screen by filling a huge rectangle
the same color as the background color, and then call paint.
public void update (Graphics h) {
Color c = this.getBackground();
h.setColor(c);
h.fillRect(0, 0, widthOfApplet, heightOfApplet);
c = this.getForeground();
h.setColor(c);
paint (h);
}
Most of the flicker is caused by this clearing of the screen, so we can simply override (redefine) the method as follows:
public void update (Graphics h) {
paint (h);
}
Realize now that it is necessary to 'clear the screen' in a similar fashion when drawing on the off-screen image.
import java.awt.*;
// A simple Ball class
class Ball {
double x, y, xVel, yVel, width, height;
final static double GRAVITY = 0.1;
final static int BALL_SIZE = 160;
Ball(int x, int y, int width, int height) {
this.width = width; this.height = height;
this.x = x; this.y = y;
xVel = 0.7; yVel = 0.5;
}
public void updateBall() {
if ((x >= width-BALL_SIZE) || (x <=
0)) { xVel = -xVel; }
if ((y >= height-BALL_SIZE) || (y <=
0)) { yVel = -yVel; }
x += xVel;
yVel += Ball.GRAVITY;
y += yVel;
}
}
public class BufferedBall extends java.applet.Applet implements Runnable {
Thread t;
Ball ball;
int width, height;
// Here is the offscreen image, and the related Graphics
object
Image image;
Graphics offscreen;
public void init() {
width = Integer.parseInt(getParameter
("width"));
height = Integer.parseInt(getParameter
("height"));
ball = new Ball (100, 50, width, height);
// Bring the offscreen image to life
image = this.createImage (width, height);
// Pull the Graphics object from it, so
we can draw later
offscreen = image.getGraphics ();
this.setBackground (Color.white);
t = new Thread (this);
t.start();
}
// Override update so it *does not* clear the screen
public void update (Graphics h) {
paint (h);
}
// In paint, we draw on the screen image first
public void paint (Graphics g) {
offscreen.setColor (Color.black);
offscreen.fillRect(0,0,width,height);
offscreen.setColor (Color.white);
offscreen.fillOval ((int)ball.x,
(int)ball.y, ball.BALL_SIZE, ball.BALL_SIZE);
// Now that image is
ready to go, we can paint it using drawImage
g.drawImage (image,
0, 0, width, height, this);
}
public void run() {
while (true) {
try {
ball.updateBall();
repaint();
Thread.sleep(35);
} catch (InterruptedException
e) { }
}
}
}
The next page will discuss testing Java.