One of the most important aspects of animation are images in
various
formats. From 2D to 3D software, they are often used
to simulate motion and
reduce calculations. Eons ago, little
flipbooks were the technology whereby
each page of the book
had a different drawing and the rapid flipping
created the
illusion that a character was moving.
It is such a simple principle now inherent in best selling
games and
block buster movies. Each image frame just gets
fancier and the 'flipper'
has ranged from projection reels
to computers in the past. Now, through the
magic of Java
programming, we can build our own animations on the
internet
for the entire world to see. I'll show you how.
Since we're going to be dealing with images from this point
forward, I
must explain another aspect of building user
friendly applets: the
MediaTracker class.
The MediaTracker class monitors the status of media types,
such as
images, sounds, etc. A common concern with applets
is that images are
sometimes referred to before they are
fully loaded, which has unpredictable
results.
MediaTracker can load data asynchronously (in the background)
or
synchronously (waits for data to load first). Image loading
is sped up by
threads which dedicate themselves to a group of
images identified by a
selected ID.
Since the Flicker class adopts many techniques used in Part
2,
I will only go over the changes relevant to image loading
and
rendering. This applet also introduces you to parameters which
give
applets their flexibility. I will explain these as well.
MediaTracker flickerTracker; Image[] flickerImages;
The applet requires the variables above: an instance of the
MediaTracker
class and an array of image objects.
String paramString; int flickerStart=0, flickerCount=0; int flickerDelay=0, flickerFrame=0;
paramString is a string object for storing each parameter value and
the
integers are numbers relevant to the image displaying process.
paramString = getParameter("FLICKERDELAY"); flickerDelay = Integer.parseInt(paramString);
This is code in the init method showing how parameters are
accessed.
Parameters are defined in web page HTML code, making them
variable
without recompiling applets to change some small feature.
getParameter returns the parameter name in the HTML applet tag by
its
name ("FLICKERDELAY") as type String. In the case above, a
number is
required for the animation delay so we use an Integer
object to convert the
parameter string and put it in our variable.
flickerImages = new Image[flickerCount];
Here, the parameter FLICKERCOUNT will tell the applet how many images
to animate and allocate the dynamic memory for the array of images.
flickerTracker = new MediaTracker(this);
The MediaTracker object must also be allocated for usage.
for(int i=0; i>flickerCount; i++) { flickerImages[i] = getImage(getCodeBase(), paramString + flickerStart + ".gif");
This is a loop which requests each image in the array be loaded from
the
specified file. getCodeBase is the URL location of the applet
and the image
filename is reset for the next file. eg.) image1.gif
flickerTracker.addImage(flickerImages[i],0); flickerStart++; }
In the same loop, each image object is added to the MediaTracker
object
as part of the group ID zero. The counter is incremented
to the next image
number.
flickerTracker.checkID(0,true); repaint(); }
Above, checkID asks the MediaTracker object to load the images
specified by the ID zero and returns immediately. This
asynchronous
loading which will occur in the background while
the applet continues its
respective stages. Note that repaint
is called which calls the applet's
default update method.
public void update(Graphics g) { paint(g); }
We will override the default update method because it erases
the
background before calling the paint method. Our images will
be
scaled to fit the entire applet window so we will just call the
paint method.
if(!flickerTracker.checkAll()) { g.drawString("Please wait, images loading...", 20,this.size().height/2); }
This is inside our customized paint method. The MediaTracker
object will
return false from checkAll() until all the images
are loaded so this
information is used to paint a message for
the user.
else { g.drawImage(flickerImages[flickerFrame],0,0, this.size().width,this.size().height,this); }
Now, when the images have all been loaded, drawImage will be
called to
draw the current image in the array at (0,0), scaled
to the applet width
and height.
repaint(); flickerFrame++; if(flickerFrame >= flickerCount) flickerFrame=0;
In Part
2, the applet methods are discussed along with the run
method so I've
listed just the animation actions above in the
run method.
Repaint calls update which calls paint which draws the image in
the
array. The image counter is incremented to the next image
number and reset
to the first if the last image is reached. This
cycles through the images
for the life of the applet.
You can reuse this applet with your own gif images numbered in
sequence,
(anim1.gif, anim2.gif, anim3.gif, etc...) Here is
example HTML for running
this applet using parameters for gif
files anim1.gif to anim3.gif with a
delay of 10 milliseconds
between frames:
<APPLET CODE="Flicker.class" WIDTH=50 HEIGHT=50>
<PARAM
NAME="FLICKERNAME" VALUE="anim">
<PARAM NAME="FLICKERSTART"
VALUE="1">
<PARAM NAME="FLICKERCOUNT" VALUE="3">
<PARAM
NAME="FLICKERDELAY" VALUE="10">
</APPLET>
Here is the applet code in its entirety:
// Flicker.java // by Garry Morse import java.awt.*; import java.applet.*; public class Flicker extends Applet implements Runnable { Thread flickerThread; MediaTracker flickerTracker; Image[] flickerImages; String paramString; int flickerStart=0, flickerCount=0; int flickerDelay=0, flickerFrame=0; public void init() { // get parameter for wait delay between images paramString = getParameter("FLICKERDELAY"); flickerDelay = Integer.parseInt(paramString); // get parameter for number of image to start with paramString = getParameter("FLICKERSTART"); flickerStart = Integer.parseInt(paramString); // get parameter for number of images to load paramString = getParameter("FLICKERCOUNT"); flickerCount = Integer.parseInt(paramString); // get parameter for image path and prefix name paramString = getParameter("FLICKERNAME"); // allocate memory for an array of image objects flickerImages = new Image[flickerCount]; // create a media tracker object for this applet flickerTracker = new MediaTracker(this); // loop for number of images for(int i=0; i<flickerCount; i++) { // load each graphic file into memory flickerImages[i] = getImage(getCodeBase(), paramString + flickerStart + ".gif"); // add image to list of monitored images with ID zero flickerTracker.addImage(flickerImages[i],0); // increment image number to next flickerStart++; } // load images into memory flickerTracker.checkID(0,true); repaint(); } public void start() { if(flickerThread==null) { flickerThread = new Thread(this); flickerThread.start(); } } public void stop() { if(flickerThread!=null) { flickerThread.stop(); flickerThread = null; } } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { // if images not loaded yet, inform user if(!flickerTracker.checkAll()) { g.drawString("Please wait, images loading...", 20,this.size().height/2); } // otherwise erase last image and draw next image in queue else { g.drawImage(flickerImages[flickerFrame],0,0, this.size().width,this.size().height,this); } } public void run() { while(true) { repaint(); // reloop through list of images to display flickerFrame++; if(flickerFrame >= flickerCount) flickerFrame=0; // try to shut down thread for milliseconds try { flickerThread.sleep(flickerDelay+1); } // if exception, output error message catch(InterruptedException e) { System.out.println("Thread would not go to sleep."); } } } }
Click here to see the Flicker animation applet in action!
The Java Game Programming Tutorial and all tutorials within are created by Garry Morse, Copyright 1997