Option 4: Override update and Have paint do Incremental Updating

1. Idea

Have repaint (which triggers update) avoid clearing the screen each time as follows:

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

Then, assuming objects don’t overlap, erase each object at its old location by drawing a solid rectangle in the background color, then draw at the new location.

2. Example Bounce.java: (Download Source)

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;

//----------------------------------------------------
/** Bounce circles around on the screen.
 *  Doesn't use double buffering so has problems
 *  with overlapping circles. Overrides update
 *  to avoid flicker problems.
 */

public class Bounce extends Applet
                    implements Runnable,
                               ActionListener {
  private Vector circles;
  private int width, height;
  private Button startButton, stopButton;
  private Thread animationThread = null;

  public void init() {
    setBackground(Color.white);
    width = getSize().width;
    height = getSize().height;
    circles = new Vector();
    startButton = new Button("Start a circle");
    startButton.addActionListener(this);
    add(startButton);
    stopButton = new Button("Stop all circles");
    stopButton.addActionListener(this);
    add(stopButton);
  }

  //----------------------------------------------------
  /** When the "start" button is pressed, start the
   *  animation thread if it is not already started.
   *  Either way, add a circle to the Vector of
   *  circles that are being bounced.
   * 
   *  When the "stop" button is pressed, stop
   *  the thread and clear the Vector of circles.
   */
  public void actionPerformed(ActionEvent event) {
    if (event.getSource() == startButton) {
      if (circles.size() == 0) {
        // Erase any circles from previous run.
        getGraphics().clearRect(0, 0, getSize().width,
                                      getSize().height);
        animationThread = new Thread(this);
        animationThread.start();
      }
      int radius = 25;
      int x = radius + randomInt(width - 2 * radius);
      int y = radius + randomInt(height - 2 * radius);
      int deltaX = 1 + randomInt(10);
      int deltaY = 1 + randomInt(10);
      circles.addElement(new MovingCircle(x, y, radius,
                                          deltaX,
                                          deltaY));
    } else if (event.getSource() == stopButton) {
      if (animationThread != null) {
        animationThread = null;
        circles.removeAllElements();
      }
    }
    repaint();
  }

  //----------------------------------------------------
  /** Each time around the loop, call paint and then
   *  take a short pause. The paint method will
   *  move the circles and draw them.
   */
  public void run() {
    Thread myThread = Thread.currentThread();
    // Really while animationThread not null
    while(animationThread==myThread) { 
      repaint();
      pause(100);
    }
  }

  //----------------------------------------------------
  /** Skip the usual screen-clearing step of update
   *  so that there is no "flicker" between each
   *  drawing step.
   */
  public void update(Graphics g) {
    paint(g);
  }

  //----------------------------------------------------
  /** Erase each circle's old position, move it,
   *  then draw it in new location.
   */
  public void paint(Graphics g) {
    MovingCircle circle;
    for(int i=0; i<circles.size(); i++) {
      circle = (MovingCircle)circles.elementAt(i);
      g.setColor(getBackground());
      circle.draw(g); // Old position
      circle.move(width, height);
      g.setColor(getForeground());
      circle.draw(g); // New position
    }
  }

  //----------------------------------------------------
  // Returns an int from 0 to max (inclusive),
  // yielding max + 1 possible values.

  private int randomInt(int max) {
    double x =
      Math.floor((double)(max + 1) * Math.random());
    return((int)(Math.round(x)));
  }

  //----------------------------------------------------
  // Sleep for the specified amount of time.

  private void pause(int milliseconds) {
    try {
      Thread.sleep((long)milliseconds);
    } catch(InterruptedException ie) {}
  }
}

3. MovingCircle.java: (Download Source)

/** An extension of SimpleCircle that can be moved
 *  around based on deltaX and deltaY values. Movement
 *  will continue in a given direction until the
 *  edge of the circle reaches a wall, in which case it
 *  will "bounce" and move the other direction.
 */

public class MovingCircle extends SimpleCircle {
  private int deltaX, deltaY;

  public MovingCircle(int x, int y, int radius,
                      int deltaX, int deltaY) {
    super(x, y, radius);
    this.deltaX = deltaX;
    this.deltaY = deltaY;
  }

  public void move(int windowWidth, int windowHeight) {
    setX(getX() + getDeltaX());
    setY(getY() + getDeltaY());
    bounce(windowWidth, windowHeight);
  }

  private void bounce(int windowWidth,
                      int windowHeight) {
    int x = getX(), y = getY(), radius = getRadius(),
        deltaX = getDeltaX(), deltaY = getDeltaY();
    if ((x - radius < 0) && (deltaX %lt; 0))
      setDeltaX(-deltaX);
    else if ((x + radius > windowWidth) && (deltaX > 0))
      setDeltaX(-deltaX);
    if ((y -radius < 0) && (deltaY < 0))
      setDeltaY(-deltaY);
    else if((y + radius > windowHeight) && (deltaY > 0))
      setDeltaY(-deltaY);
  }


  public int getDeltaX() {
    return(deltaX);
  }

  public void setDeltaX(int deltaX) {
    this.deltaX = deltaX;
  }

  public int getDeltaY() {
    return(deltaY);
  }

  public void setDeltaY(int deltaY) {
    this.deltaY = deltaY;
  }
}

    

4. Result: (Download Source)

Bouncing Circles

Sorry, you need Java for this.

© 1996-99 Marty Hall, 1999 Lawrence M. Brown