Java 1.2 Unleashed

Previous chapterNext chapterContents


- 22 -

Creating Animations


This chapter shows you how to include animation sequences in your window programs. It identifies the basic elements of implementing an animation and then describes approaches to improving the quality of an animation's display by selectively repainting parts of a window and using the MediaTracker class to support the loading of the images used in an animation. When you finish this chapter, you'll be able to include animation in your window programs.

Animation Basics

While including animation sequences in your Java programs may at first appear to be complicated, it is, in fact, rather easy once you learn the basics. Animations are nothing more than the rapid display of still images such that the pattern of image display causes the appearance of movement for the objects contained in the image. To create an animation, you need to produce the sequence of objects that are to be displayed and then write a Java program that will display that sequence at a particular display rate.

For many, the hardest part of developing an animation is producing the images that are to be displayed. This part requires drawing skills and is completely separate from Java programming. Don't fret if you are unable to easily draw these animation sequences. Chances are that you're better at it than most. The important point of this chapter is to learn how to display, in the form of an animation, the sequences that you do come up with.

Many animations display their image sequences in a looping fashion. A looping animation gives the appearance that it is much longer than it actually is and it can run indefinitely. Looping animations also require fewer image frames. If your animation displays 10 to 20 image frames per second and you want it to run for a minute, you will need 600 to 1,200 images. That's a lot of work for a one-minute animation. It is much easier to develop a small but varied looping animation and have it loop several times during the course of a minute.

The major parameter of an animation, besides the type and quality of the images it displays, is the number of image frames it displays per-second. This is typically a fixed number between 5 and 25. The more frames per-second that are displayed, the smoother the animation appears to be. For example, television is 30 frames per-second. The frames-per-second parameter translates into a frame delay parameter that is used to determine how long a program should wait before it displays the next image frame. This is typically measured in milliseconds. For example, frames-per-second rates of 5, 10, and 20 translate into frame delays of 200, 100, and 50 milliseconds.

A common approach to implementing an animation is to create a program thread that runs in an infinite loop and displays the frames of the animation sequence one-at-a-time, waiting frame-delay milliseconds between each frame's display.

A Simple Animation

In order to get a better understanding of the basics of the animation process, you can develop a simple, character-based animation. The source code of the SimpleAnimationApp program is shown in Listing 22.1.

LISTING 22.1. THE SOURCE CODE OF THE SimpleAnimationApp PROGRAM.

import java.awt.*;

import java.awt.event.*;

public class SimpleAnimationApp extends Frame implements Runnable {

 Thread animation;

 // Set the frame delay

 int frameDelay = 100;

 // The objects to be displayed

 String frames[] = {"*","**","***","****","*****","****","***","**","*"};

 int numFrames = frames.length;

 int currentFrame = 0;

 long lastDisplay = 0;

 int screenWidth = 200;

 int screenHeight = 200;

 public static void main(String args[]) {

  SimpleAnimationApp app = new SimpleAnimationApp();

 }

 public SimpleAnimationApp() {

  super("Simple Animation");

  setup();

  setSize(screenWidth,screenHeight);

  addWindowListener(new WindowEventHandler());

  show();

  animation = new Thread(this);

  animation.start();

 }

 void setup() {

  setupMenuBar();

  setFont(new Font("default",Font.BOLD,18));

 }

 void setupMenuBar() {

  MenuBar menuBar = new MenuBar();

  Menu fileMenu = new Menu("File");

  MenuItem fileExit = new MenuItem("Exit");

  fileExit.addActionListener(new MenuItemHandler()); 

  fileMenu.add(fileExit);

  menuBar.add(fileMenu);

  setMenuBar(menuBar);

 }

 public void paint(Graphics g) {

  g.drawString(frames[currentFrame],60,60);

 }

 public void run() {

  // The animation loop

  do {

   long time = System.currentTimeMillis();

   if(time - lastDisplay > frameDelay) {

    repaint();

    try {

     Thread.sleep(frameDelay);

    }catch(InterruptedException ex){

    }

    ++currentFrame;

    currentFrame %= numFrames;

    lastDisplay = time;

   }

  } while (true);

 }

 class MenuItemHandler implements ActionListener, ItemListener {

  public void actionPerformed(ActionEvent ev){

   String s=ev.getActionCommand();

   if(s=="Exit"){

    System.exit(0);

   }

  }

  public void itemStateChanged(ItemEvent e){

  }

}

 class WindowEventHandler extends WindowAdapter {

  public void windowClosing(WindowEvent e){

   System.exit(0);

  }

 }

}

Compile and run SimpleAnimationApp. Your program's display should look like the one shown in Figure 22.1.

FIGURE 22.1. A simple animation.

A string of asterisks is modulated to give the appearance of movement.

While this short animation is by no means in line for any awards, it does illustrate all the basic elements of more complex and entertaining animations.

The SimpleAnimationApp class declares the animation Thread, the frameDelay variable, the array of frames[] used to implement the animation's display, the numFrames variable, the currentFrame variable, the time of the lastDisplay of a frame, and the standard menu bar and window size variables.

The setup of the SimpleAnimationApp program is fairly standard, with the exception of the creation of the animation thread at the end of the class constructor and the invocation of the animation thread's start() method.

The paint() method contains a single statement that is used to display a string of asterisks on the console window.

The run()method implements the animation loop. It checks the current system time and the time of the last image display to see if it is time to display a new frame. It uses the currentTimeMillis() method of the System class to read the current time in milli- seconds. If it is time to display another frame, the run() method invokes the repaint() method to display the current frame and then tries to sleep for frameDelay milliseconds. It updates the currentFrame using modular arithmetic and changes the time of lastDisplay.

A Graphics Animation

Because the SimpleAnimationApp program provides all the basic elements required of an animation, we can easily modify the animation to support graphics. Figures 22.2 through 22.5 provide four stick figures I drew using the Windows Paint program. These crude figures can be used to create an animation of a stick figure that attempts to fly or exercise.

FIGURE 22.2. stickman1.gif.

FIGURE 22.3. stickman2.gif.

FIGURE 22.4. stickman3.gif.

FIGURE 22.5. stickman4.gif.

You may easily substitute your own figures for the ones used in this example.

The source code of the GraphicAnimationApp program is shown in Listing 22.2.

LISTING 22.2. THE SOURCE CODE OF THE GraphicAnimationApp PROGRAM.

import java.awt.*;

import java.awt.event.*;

public class GraphicAnimationApp extends Frame implements Runnable {

 Thread animation;

 int frameDelay = 100;

 Image frames[];

 int numFrames;

 int currentFrame = 0;

 long lastDisplay = 0;

 int screenWidth = 400;

 int screenHeight = 400;

 public static void main(String args[]) {

  GraphicAnimationApp app = new GraphicAnimationApp();

 }

 public GraphicAnimationApp() {

  super("Graphic Animation");

  setup();

  setSize(screenWidth,screenHeight);

  addWindowListener(new WindowEventHandler());

  show();

  animation = new Thread(this);

  animation.start();

 }

 void setup() {

  setupMenuBar();

  setFont(new Font("default",Font.BOLD,18));

Toolkit toolkit = getToolkit();

  frames = new Image[4];

  // Load the animation frames

  frames[0] = toolkit.getImage("stickman1.gif");

  frames[1] = toolkit.getImage("stickman2.gif");

  frames[2] = toolkit.getImage("stickman3.gif");

  frames[3] = toolkit.getImage("stickman4.gif");

  numFrames = frames.length;

 }

 void setupMenuBar() {

  MenuBar menuBar = new MenuBar();

  Menu fileMenu = new Menu("File");

  MenuItem fileExit = new MenuItem("Exit");

  fileExit.addActionListener(new MenuItemHandler()); 

  fileMenu.add(fileExit);

  menuBar.add(fileMenu);

  setMenuBar(menuBar);

 }

 public void paint(Graphics g) {

  g.drawImage(frames[currentFrame],125,80,this);

 }

 public void run() {

  // The animation loop

  do {

   long time = System.currentTimeMillis();

   if(time - lastDisplay > frameDelay) {

    repaint();

    try {

     Thread.sleep(frameDelay);

    }catch(InterruptedException ex){

    }

    ++currentFrame;

    currentFrame %= numFrames;

    lastDisplay = time;

   }

  } while (true);

 }

 class MenuItemHandler implements ActionListener, ItemListener {

  public void actionPerformed(ActionEvent ev){

   String s=ev.getActionCommand();

   if(s=="Exit"){

    System.exit(0);

   }

  }

  public void itemStateChanged(ItemEvent e){

  }

 }

 class WindowEventHandler extends WindowAdapter {

  public void windowClosing(WindowEvent e){

   System.exit(0);

  }

 }

}

When you run GraphicAnimationApp, your display should look like the one shown in Figure 22.6.

FIGURE 22.6. The Graphic- AnimationApp program display.

Unless you have a really fast computer and video card, your program display probably has some very noticeable flickering. Don't worry about that problem now. You'll learn about ways to improve the quality of an animation's display in the following section. For now, just focus on how you modified the SimpleAnimationApp program to support graphic-based animation.

The GraphicAnimationApp program is similar to the SimpleAnimationApp program. These are the differences between the two programs.

These simple changes were all that was needed to convert the program from a simple text-based animation to a graphics-based animation.

Improving Animation Display Qualities

The GraphicAnimationApp program has some serious deficiencies in the way that it displays animation images. The first and probably the most noticeable problem is that it tries to start displaying the images before they are completely loaded. This is an easy problem to solve using the MediaTracker class.

The MediaTracker class provides the capability to manage the loading of image files. You use the addImage()method to add an image to the list of images being tracked. After adding an image to a MediaTracker object, you can check on the image or all images managed by the MediaTracker object using the access methods provided by the MediaTracker class.

The other major problem with the animation's display is that the entire screen is repainted with each new frame, which causes a significant amount of flickering. This image flickering can be mitigated by limiting the area of the window that is updated with each new image. The repaint() and update() methods of the component class provide this capability.

You are already familiar with limited screen repainting from using the repaint() method in Chapter 7, "Working with the Canvas." The update() method provides the capability to update a Graphics object without first clearing the current image. This allows successive images to be displayed as marginal increments to the currently displayed image.

Another option to improving an animation's display quality is to change the frame delay. By decreasing the number of frames-per-second being displayed, you are able to lower the rate at which flickering occurs. However, you do this at the expense of the overall quality of your animation, because higher frame display rates tend to smooth out any gaps between successive images.

An Updated Graphics Animation

The GraphicUpdateApp program shows how to use the MediaTracker class, together with limited repainting and frame-delay adjustments, to improve the quality of the GraphicAnimationApp program. Its source code is shown in Listing 22.3.

LISTING 22.3. THE SOURCE CODE OF THE GraphicUpdateApp PROGRAM.

import java.awt.*;

import java.awt.event.*;

public class GraphicUpdateApp extends Frame implements Runnable {

 Thread animation;

 int frameDelay = 200;

 Image frames[];

 int numFrames;

 int currentFrame = 0;

 long lastDisplay = 0;

 boolean fullDisplay = false;

 MediaTracker tracker;

 int screenWidth = 400;

 int screenHeight = 400;

 public static void main(String args[]) {

  GraphicUpdateApp app = new GraphicUpdateApp();

 }

 public GraphicUpdateApp() {

  super("Updated Graphic Animation");

  setup();

  pack();

  setSize(screenWidth,screenHeight);

  addWindowListener(new WindowEventHandler());

  show();

  animation = new Thread(this);

  animation.start();

 }

 void setup() {

  setupMenuBar();

  setFont(new Font("default",Font.BOLD,18));

  Toolkit toolkit = getToolkit();

  frames = new Image[4];

  // Load animation frames

  frames[0] = toolkit.getImage("stickman1.gif");

  frames[1] = toolkit.getImage("stickman2.gif");

  frames[2] = toolkit.getImage("stickman3.gif");

  frames[3] = toolkit.getImage("stickman4.gif");

  numFrames = frames.length; 

  tracker = new MediaTracker(this);

  // Use the MediaTracker object to manage the frames

  for(int i=0;i<numFrames;++i) tracker.addImage(frames[i],i);

 }

 void setupMenuBar() {

  MenuBar menuBar = new MenuBar();

  Menu fileMenu = new Menu("File");

  MenuItem fileExit = new MenuItem("Exit");

  fileExit.addActionListener(new MenuItemHandler()); 

  fileMenu.add(fileExit);

  menuBar.add(fileMenu);

  setMenuBar(menuBar);

 }

 public void paint(Graphics g) {

  if(allLoaded())

   g.drawImage(frames[currentFrame],125,80,this);

  else{

   String stars = "*";

   for(int i=0;i<currentFrame;++i) stars += "*";

   g.drawString(stars,60,60);

  }

 }

 boolean allLoaded() {

  for(int i=0;i<numFrames;++i) {

   if(tracker.statusID(i,true) != MediaTracker.COMPLETE) return false;

  }

  return true;

 }

 public void run() {

  // The animation loop

  do {

   long time = System.currentTimeMillis();

   if(time - lastDisplay > frameDelay) {

    if(allLoaded()) {

     if(fullDisplay) repaint (115,160,160,90); 

     else{

      fullDisplay = true;

      repaint();

     }

    }else repaint();

    try {

     Thread.sleep(frameDelay);

    }catch(InterruptedException ex){

    }

  ++currentFrame;

    currentFrame %= numFrames;

    lastDisplay = time;

   }

  } while (true);

 }

 class MenuItemHandler implements ActionListener, ItemListener {

  public void actionPerformed(ActionEvent ev){

   String s=ev.getActionCommand();

   if(s=="Exit"){

    System.exit(0);

   }

  }

  public void itemStateChanged(ItemEvent e){

  }

}

 class WindowEventHandler extends WindowAdapter {

  public void windowClosing(WindowEvent e){

   System.exit(0);

  }

 }

}

When you run GraphicUpdateApp, it will display an animated string of asterisks while the image files are being loaded. After that, it will immediately display the image animation. This reduces the unsightly flickering caused when an image is displayed while it is being loaded.

Notice how GraphicUpdateApp implements the limited area repainting. You can run your mouse over the image display to determine the boundaries of the repaint area.

You should also notice that GraphicUpdateApp displays images at a slower rate. The frame-delay rate was increased from 100 microseconds to 200 microseconds, decreasing the frame display rate by a factor of 2.

The changes made to GraphicAnimationApp by GraphicUpdateApp consist of the declaration of the fullDisplay and tracker variables and modifications to the setup(), paint(), and run() methods. In addition, the allLoaded() method was created. The following summarizes the changes that were made.

The Animation API

Although it wasn't included in the JMF version 1.0, Sun intends to incorporate an Animation API in future versions of the JMF. The Animation API will provide a standard set of animation capabilities and will consist of two packages, referred to as Sprite and Scripting. The Sprite package will provide classes and methods for working with 2D images. The Scripting package will allow animations to be defined as scripted images. A scripting engine and animation player will be provided. The player will be capable of synchronizing animation effects with other JMF players. When it becomes available, the Animation API will provide a platform-independent solution for high-end business presentations, computer-aided training, games, and entertainment software.

Summary

This chapter showed how to include animation sequences in your window programs. It identified the basic elements of implementing an animation and described approaches to improving the quality of an animation's display. It showed you how to selectively repaint parts of a window and how to use the MediaTracker class to support the loading of the images used in an animation. Chapter 23, "Integrating Speech and Telephony Capabilities," completes Part VI, "Multimedia Programming," by discussing the Speech and Telephony APIs.


Previous chapterNext chapterContents

© Copyright, Macmillan Computer Publishing. All rights reserved.