Fundamentals of Computer Science II (CSC-152 99S)


Animation

In this laboratory session, you will develop a number of applets that do simple animation, including an applet that places a bouncing ball on the screen and an applet that prints a message, part by part. The purposes of this lab session are to

Prerequisite skills:

Required Java files:

Required HTML files:


Discussion

Animations

Most animations are done within applets because it is often simpler to animate with applets. We will focus on applet-based animation because it is also somewhat simpler than application-based animation.

For animations to work correctly, you need to create what is called a thread of computation. In order to successfully do animation, you often need to do a number of independent and simulataneous computations. By using threads, you permit the computer to do multiple things at once (in effect, the computer can alternate between threads of computation). Importantly, you can tell a thread to pause temporarily (using sleep). By pausing and then redrawing, you can do both simple and complex animations.

In order to develop an animated applet, you'll need to indicate that your applet can be turned into a thread. You do this by noting that your class implements Runnable.

You will also need to create start and stop methods. As you might guess, start starts your applet running. There is a fairly standard structure for most start methods. You create a new thread from the current applet and you start that thread. Similarly, stop stops your applet (most often, by telling the thread to stop).

Here is a sketch of an applet that does animation (although this one doesn't do much because the paint method is empty). However, it does illustrate all of the key components of an animated applet. That is, one must create a thread. The applet runs by repeatedly going to sleep for a small amount of time, updating its state, and then redrawing itself.


import java.awt.*;
import java.applet.*;
import java.util.Random;

/**
 * A simple framework for an applet that does animation.
 * 
 * @author Samuel A. Rebelsky
 * @version 1.1 of March 1999
 */
public class AnimationApplet 
  extends Applet 
  implements Runnable {

  // +--------+--------------------------------------------------
  // | Fields |
  // +--------+

  /** The thread corresponding to the current applet.  */
  protected Thread thread;


  // +-------------------------+---------------------------------
  // | Standard Applet Methods |
  // +-------------------------+

  /**
   * Initialize the applet.  
   */
  public void init() {
  } // init()
  
  /**
   * Paint the current "animation frame" on the screen.
   */
  public void paint(Graphics g) {
  } // paint(Graphics)

  /**
   * Run the thread.
   */
  public void run() {
    // It appears that we keep going forever.  However, external
    // things (that is, the web browser) control when we stop.
    while (true) {
      try {
        thread.sleep(50);
      }
      catch (InterruptedException ie) {
        // Do nothing
      }
      // Update information for the animation
      updateInformation();
      // And repaint
      repaint();
    } // while
  } // run()

  /**
   * Start the thread.
   */
  public void start() {
    thread = new Thread(this);
    thread.setPriority(Thread.MIN_PRIORITY);
    thread.start();
  } // start()

  /**
   * Stop the thread.
   */
  public void stop() {
    thread.stop();
  } // stop()

  // +-------------------------+---------------------------------
  // | Standard Applet Methods |
  // +-------------------------+

   /**
    * Update information to support the next "frame" in the
    * animation.
    */
   public void updateInformation() {
   } // updateInformation()
} // AnimationApplet


The first key part of this applet is the start method. This creates a new thread corresponding to the current object (the animation applet) and starts the thread. As this suggests, Java does not automatically create threads for you; you need to create them yourself (and keep track of them).

The more interesting part is the run method. In this method, we repeatedly put the thread to sleep (basically, pause between frames). When it wakes up, we update local information and then repaint.

How does this provide animation? If we design the paint method to paint one frame of animation, then we now have a situation in which we get a frame every n milliseconds, which is what happens in animation.

Animating a Bouncing Ball

Here's how we might put this together to get a ``bouncing'' ball (or at least a bouncing circle). We need to keep track of the x and y positions of the ball. We can do that with two fields.

  /** The current x position of the center of the ball.  */
  protected int xpos;
  /** The current y position of the center of the ball.  */
  protected int ypos;

At each step, we need to change those positions before painting the ball (Java handles the erasing for us, so we just have to paint). How do we know how to change them? We'll create two fields to keep track of the horizontal and vertical velocities.

When do we change these velocities? When it hits a wall. You should think about how one might determine that.


import java.awt.*;
import java.applet.*;
import java.util.Random;

/**
 * A simple implementation of a bouncing ball.
 * <p>
 * Copyright (c) 1998 Samuel A. Rebelsky.  All rights reserved.
 *
 * @author Samuel A. Rebelsky
 * @version 1.1 of March 1999
 */
public class BallApplet 
  extends Applet 
  implements Runnable 
{

  // +------------+----------------------------------------------
  // | Attributes |
  // +------------+

  /** The current x position of the center of the ball.  */
  protected int xpos;

  /** The current y position of the center of the ball.  */
  protected int ypos;

  /** The current radius of the ball. */
  protected int radius;

  /** The current horizontal velocity of the ball. */
  protected int horizVelocity;

  /** The current vertical velocity of the ball. */
  protected int vertVelocity;

  /** The color of the ball. */
  protected Color color;

  /** The thread that corresponds to the curent applet. */
  protected Thread thread;


  // +-------------------------+---------------------------------
  // | Standard Applet Methods |
  // +-------------------------+

  /**
   * Initialize the applet.  Set up the color, position, velocity,
   * and such.
   */
  public void init() {
    // To make things interesting, we might initialize some of the values
    // randomly.  This means that we'll need a random number generator.
    Random generator = new Random();

    // Pick a "reasonable" radius
    radius = 10;

    // Start at the upper left-hand-corner
    xpos = 10;
    ypos = 10;
    
    // Start at a moderate velocity
    horizVelocity = 5;
    vertVelocity = 5;

    // Pick a nice color
    color = Color.blue;
  } // init()
  
  /**
   * Paint the ball on the screen.
   */
  public void paint(Graphics g) {
    g.setColor(color);
    g.fillOval(xpos-radius, ypos-radius, 2*radius, 2*radius);
  } // paint(Graphics)

  /**
   * Run the thread.
   */
  public void run() {
    // It appears that we keep going forever.  However, external
    // things (that is, the web browser) control when we stop.
    while (true) {
      try {
        thread.sleep(20);
      }
      catch (InterruptedException ie) {
        // Do nothing
      }
      // Update the position of the ball
      updatePosition();
      // And repaint
      repaint();
    } // while
  } // run()

  /**
   * Start the thread.
   */
  public void start() {
    thread = new Thread(this);
    thread.setPriority(Thread.MIN_PRIORITY);
    thread.start();
  } // start()

  /**
   * Stop the thread.
   */
  public void stop() {
    thread.stop();
  } // stop()


  // +--------------------+--------------------------------------
  // | Additional Methods |
  // +--------------------+

  /**
   * Update the position of the ball.
   */
  public void updatePosition() {
    // We'll need the dimensions of the current window to determine
    // when we reach a wall.
    Dimension dim = this.getSize();
    // Update the x and y position
    xpos = xpos + horizVelocity;
    ypos = ypos + vertVelocity;
    // If we've gone too far in one direction, reverse the ball
    if (xpos+radius > dim.width) {
      xpos = dim.width - (xpos + radius - dim.width);
      horizVelocity = -horizVelocity;
    }
    else if (xpos-radius < 0) {
      xpos = -(xpos - radius); 
      horizVelocity = -horizVelocity;
    }
    if (ypos+radius > dim.height) {
      ypos = dim.height - (ypos + radius - dim.height);
      vertVelocity = -vertVelocity;
    }
    else if (ypos-radius < 0) {
      ypos = -(ypos - radius);
      vertVelocity = -vertVelocity;
    }
  } // updatePosition()
} // BallApplet


An Animated Sign

Here's another way one might use animation. We'll gradually write a short welcome message on the screen. We use a counter (amt) to keep track of how much of the message to write.


import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;

/**
 * A simple applet that prints a string, piece by piece..
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of November 1998
 */
public class AnimatedWelcome 
  extends Applet 
  implements Runnable 
{
  /** The message to print. */
  String message;
  
  /** The color of the message. */
  Color color;

  /** The portion of the message to print. */
  int amt = 1;
  
  /** The thread for the applet. */
  Thread welcomeThread;
  
  /**
   * Set up the message to print.
   */
  public void init() {
    message = getParameter("Message");
    if (message == null) { message = "A Sample Message"; }
    color = Color.blue;
  } // init()
  
  /*
   * Stop the thread.
   */
  public void stop() {
    welcomeThread = null;
  }
  
  /**
   * Start up.
   */
  public void start() {
    if ((amt < message.length()) && (welcomeThread == null)) {
      welcomeThread = new Thread(this);
    }
    welcomeThread.start();
  } // start
  
  /*
   * Run the program (or the thread).
   */
  public void run() {
    //Remember which thread we are.
    Thread currentThread = Thread.currentThread();

    //This is the display loop.
    while ( (currentThread == welcomeThread) &&
            (amt <= message.length()) ) {
      //Display it.
      repaint();
      // Pause
      try {
        Thread.sleep(200); // 1/5 of a second
      } catch (InterruptedException e) { break; }
    } // while
  } // run
  
  /*
   * Paint part of the message.
   */
  public void paint(Graphics g) {
    g.setColor(color);
    g.setFont(new Font("Times", Font.BOLD, 16));
    g.drawString(message.substring(0,amt), 10, 16);
    if (amt < message.length()) 
      ++amt;
    else {
      // Change the color.
      if (color.equals(Color.blue))
        color = Color.red;
      else
        color = Color.blue;
      // Reset the amount
      amt = 0;
    }
  } // paint(Graphics)
} // class AnimatedWelcome


You may notice some flickering. How to stop flickering is left as an issue for another day.


Experiments

Name: ________________
ID:_______________

Experiment G2.2: A Simple Bouncing Ball

Possible ideas:

Experiment G2.3: Adding Gravity

How do you add gravity? Change the vertical velocity each time.

Experiment G2.4: Modularizing

Experiment G2.5: Improving Animation


Post-Laboratory Problems


Disclaimer Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.

This page may be found at http://www.math.grin.edu/~rebelsky/Courses/CS152/99S/Labs/animation.html

Source text last modified Fri Mar 19 10:07:30 1999.

This page generated on Fri Mar 19 10:13:47 1999 by SiteWeaver. Validate this page's HTML.

Contact our webmaster at rebelsky@math.grin.edu