MAIN
ABOUT
COURSES
ADVERTISE
SPONSORS
LINK TO US
CONTACT US

FREE ONLINE COURSES FOR THE INTERNET COMMUNITY
 

Introduction

Chapter 1: The basics in Java

Chapter 2: Your first applet

Chapter 3: Threads and animation

Chapter 4: Images and parameters

Chapter 5: Mouse messages

Links to applets and source code

FAQ

Basic Course in Special Effects and Game Programming in Java

by Aníbal Wainstein

Chapter 3
Threads and animation

Last updated 1999-05-24


CONTENTS

3.0 The theory behind program threads 

Multiple program threads were introduced to PC programming with the release of Windows 95. Operating systems such as UNIX have had this for a long time. A thread works like a "little worker" working with the program code. If you have several programs running in your system, then there is at least one thread for each program. Some programs may have several threads, for instance a web browser that supports Java. To be able to have animated Java applets we are forced to use two threads. First there is the program or main thread, which is the Java Virtual Machines own thread. This is used for the so-called event system, which handles user input such as mouse pointer movements and keyboard events. In the last chapter we had only worked with the main thread. The problem with animations is that they often requires that you have a thread that is constantly working with the animation code. We cannot use the main thread because then the applet's other functions will stop working, the applet hangs. A program that hangs, usually does that because the main thread gets stuck in an program loop and cannot get free, or that it collides with another thread and stops. Assume that two web designers are working with a homepage. If they do not decide who is going to do what, then they will probably end up writing each other work. This leads to agression, the cooperation breaks down and with that also the project. In the same way you must be careful when using threads.

3.0.1 An applet's start() and stop() methods (The Runnable interface) 

Applets have no natural support for threads. You add thread support by adding the Runnable interface :

import java.applet.*;  

import java.awt.*;   
public class threadtest extends Applet implements Runnable { 

}   

In our empty threadtest applet we have added "implements Runnable". With the declaration "implements" you can add several new functions to a class. It is used to increase the class' abilities. "Runnable" will give access to three new methods: start(), stop() and run(). In an applet that supports threads the methods init() and paint() are first called by the Java Virtual Machine's main thread, the third method called by this thread is the start() method.
In the start() method you can initialize and start your animation thread.

public Thread programthread = null
public void start() 

{

    if (animationthread == null) 

    {

         animationthread = new Thread(this,"animationthread");

         animationthread.start();

    }

}
The variable "animationthread" is a global variable within the applet and you must declare it yourself. What the start() method does first is that it checks that the thread is not already created (sometimes some web browsers can execute the start() method several times). The next step is to create the thread itself. It demands a reference to your applet (this) and a name that could be anything (we call it "animationthread" for the sake of simplicity). After this you start the thread with its own start() method.
When the homepage visitor changes the page or shut down the web browser, then the stop() methods is immediately called by the applet's main thread: 

public void stop() 

{

    if ((animationthread != null) && animationthread.isAlive())

        animationthread.stop();

    animationthread = null;

}
For security reasons you should make sure that the thread is created and that it is running with the method isAlive(). If it is then the thread is stopped with it's own stop() method and we set the variable "animationthread" to the value null.

You will probably always just cut and paste these two methods into your programs in the future. They are general methods that all the special effects or game applets have.

3.0.2 The run() method 

In contrast to the start() and stop() methods, the run() method will always be different from applet to applet. This methods is always called by your self-created thread. This means that you have a thread that works with the applets other methods (the main thread) and a thread that works with the run() method (your own thread that you initialize in the start() method). By "holding" the thread in the run() method with a loop, you can exploit it to do work that will not hang the JVM: 

public void run()

{

    while (true)

    {

       

    }

}
In the example above we are holding the thread within the loop forever (or until the applet's stop() method is called). The problem with the applet that we made in chapter 2.0.4, was that it was overwritten by other status messages from the web browser. Threads can help us here, because they constantly manage to update a message on the status window, and by doing so, overwriting other web browser messages. Let us rewrite the applet: 
import java.applet.*;

import java.awt.*;



public class threadtest extends Applet implements Runnable {



public Thread animationthread = null;



public void start()

{

    if (animationthread == null)

    {

         animationthread = new Thread(this,"animationthread");

         animationthread.start();

    }

}



public void stop()

{

    if ((animationthread != null) && animationthread.isAlive())

        animationthread.stop();

    animationthread = null;

}



public void run()

{

    while (true)

    {

        showStatus("Hello Sweden!");

    }

}

} 
Compile the applet and run it. You can also click here to see it. You will notice that the message "Hello Sweden!" is being displayed in the status window of your browser.

3.0.3 Exceptions, static classes and how to put a thread to sleep 

You may have notice that the applet in the last section was very processor intensive. Maybe it is not necessary to update "Hello Sweden!" every millisecond, it is enough doing that once per second or less. We must now put a delay in the run() method's loop. With the method sleep(), which exist in the Thread class, we can get the thread to "sleep" a number of milliseconds. This method is a so-called static method. A method of this type may be called without having to create an instance of the class (in other words, without having to create an object, based on that class). So you can simply write:

Thread.sleep(1000);               

Now the thread will sleep for exactly 1000 milliseconds (which is exactly one second). Static classes can be useful sometimes if you happen to have a very useful function in a class that you use a lot. Then it can be a good idea top make the function static and to be able to save some memory by not having to allocate memory for a new object. The memory that is required for a static method is allocated first when the Java VM encounters the class where the static method is. All the future objects created and based on that class will always use the same memory cell where the static method exist.

There is something that you must consider. Static methods cannot work with non-static variables in the same class. However, this is not true if the variables are also static. Why, do you ask? Because you have not allocated memory for these variables (created a new object). They simply do not exist when the static function is called! The effect of using static variables is that if you have several objects based on the same class, then they will all share the same variable. That means that if a static variable is manipulated in one object then the content of the other statical variables in the other objects are also changed.

Now you probably think that you can use this method freely and put it in your loop. No, I am sorry, we are not quite there yet. The problem with the sleep() method is that it throws an exception that you must catch. Exceptions are used in Java by methods to tell to the class calling them that something went wrong. In this case another thread may interrupt the sleeping process. You must know what happened and therefore the function throws an exception. This is of more bother than use, because if you do not catch the exception, then the applet is stopped, or as in some cases, the compiler will simply not accept it. First, you place the method in a try-loop and then you catch its exception in a catch statement: 

try  

{  

    Thread.sleep(1000);  

}  

catch (InterruptedException e)  

{  

    System.out.println("Something interrupted the sleep() method")

}   

The catch loop requires an argument which must be an exception class. The interruption class is named InterruptedException and it is thrown by sleep(). There are many types of exceptions, and all are subclasses to the Exception class. Other well known exceptions are NullPointerException (appears when you have tried to use a null reference), ArrayIndexOutOfBoundsException (appears when you have specified a negative or too high index on an array) and IOException (an error occurred when reading a file). 
To make our "effect" more interesting, we remake the run() method in the last section:

public void run()

{

    while (true)

    {

        showStatus("Long");

        try {Thread.sleep(1000);} 

        catch(InterruptedException e) {}

        showStatus("Live");

        try {Thread.sleep(1000);}

        catch(InterruptedException e) {}

        showStatus("Java!");

        try {Thread.sleep(1000);} 

        catch(InterruptedException e) {}

    }

}
In order to make the code more easy to read, I have written the pause code on two lines and removed the println() message, because it is not very interesting in this case to know if another thread interrupts the waiting process. Can you guess what will happen now? First, the message "Long" is written to the status window, then the thread will wait one second and then "Live" will be written, after another second, the string "Java!" is written. One second later, the program will start over again.

Click here and you will see. 

Now you will also notice that your computer is not working so hard compared with the other example.

3.1 Other examples with showStatus() 

Status window effects are often used among web designers today. Thanks to our new knowledge in threads, we can begin making some status window animations.

3.1.1 Your first scroller (statusscroller) 

I remember that the first applet I programmed was an applet called URLScroll. It was one of the most common effects at that time, and they usually were a text message that moved from right to left or upwards. We reviewed in section 1.4.1 how to get a substring from a larger string. Start by opening the old threadtest.java file and save a copy under another name ("statusscroller" for instance). Now we add an init() method where the message will be initialized:



public String message;



public void init()

{

    message="                              ";
message+=" ";
message+=" ";
message+=" ";
message+="Anibal Wainstein's Basic Course ";
message+="in Special Effects and Game development in Java(TM)";}
First we declare a global variable called "message". This will contain the actual message that we will write. We start by initializing the variable with a blankspace line and then by adding more subsequent blankspace lines. The message is added at the end of these lines. If you are confused by the way I am creating the message then you should know that:
message="message 1 ";

message+="message 2";
is the same thing as
message="message 1 " + "message 2";
The idea with our scroller is that we crop the string and show shorter and shorter bits on the status window until there is nothing left to show. Rewrite the run() method in the old applet:
public void run()

{

    while (true)

    {

        int L=message.length();

        for (int i=0; i<L; i++)

        {

            showStatus(message.substring(i,L));

            try {Thread.sleep(100);}

            catch(InterruptedException e) {}

        }

    }

}
Here, we start by first finding out the length of our string, with the help of the String class' length() method, the result is stored in the variable "L". The for-loop will count up from position 0 in the string, to the end of the message. Substring() can then take the part of the string that begins on this position and ends at the end of the string, that is the "L" position. We add a pause of one tenth of a second (100 milliseconds) after displaying the string. When the string has been cropped to the end, then the whole process start again, thanks to us having contained the code within the infinite while loop.
Take a look at the finished statusscroller by clicking here

 
3.1.2 A computer terminal in your status window (statusdatatext)  

Computer terminals in the beginning of the 80's were very sluggish and had a special way to write out sentences. Here, we shall try to simulate that effect, but on the status window and with only one sentence.

Do a copy of the previous Java file and rewrite the init() method to the following code:

public void init()

{

    message="Preparing to launch....";

    message+="6... 5... 4... 3... 2... 1... 0...";

}
We do not need blankspaces before the message, because the message will stand still on the status window. Write the following run() method: 
public void run()

{

    int L=message.length();

    boolean showcursor=false;



    //Write the message step by step with
//the blinking cursor.
    for (int i=0; i<L; i++)     {         if (showcursor) showStatus(message.substring(0,i)+"_");         else showStatus(message.substring(0,i));         showcursor=!showcursor;         try {Thread.sleep(100);}         catch(InterruptedException e) {}     }     //The message is written. Animate the cursor
//(keep it blinking).
    while (true)     {         if (showcursor) showStatus(message+"_");         else showStatus(message);         showcursor=!showcursor;         try {Thread.sleep(100);}         catch(InterruptedException e) {}     } }
The effect is in two steps and in two loops. The first thing we do before entering the loops is to initialize a variable that will be used for the cursor effect ("showcursor"). Inside the loops and using the following expression, this variable will be set to true when it is false and the other way around:
showcursor=!showcursor;
The keyword "!" means complement or opposite. So "showcursor" will be set to the opposite value of itself. When this variable is true, then the computer cursor will be displayed, which in our case happens to be an underscore character "_". It will be added to the message string before it is written to the status window. In the for loop we write the message step by step using the substring() method (plus the cursor) and add a pause after writing it.
The last while-loop in the run() method will see to that the thread keeps animating the cursor for an infinite time. It is needed to make the effect a bit more realistic.

Click here to see statusdatatext in action. 

Please note, that if you want to speed up the effects in this example and in the example in the last section, then decrease the value in the sleep() method. You will then have a more processor intensive applet. In more complicated effects that demands many calculations this could cause big problems, which we will give example of in later chapters.
 
3.1.3 Falling text (fallingtext)  

Another interesting effect is the falling test effect. The word "falling" is a bit missleading, the text moves from the right and builds up the sentence or message letter by letter. Now, do a copy of the applet code in the last section. We begin by initializing the message in the init() method:



public void init()

{

    message="Falling text is a cool effect!";

}
It was nothing strange with that so we go on to the code in the run() method:
public void run()

{

    int L=message.length();



    //Create a string with a blankspace for the animation.

    String blankspace="                                 ";

    blankspace=blankspace+blankspace+blankspace;



    //Set the variable "mL" to the blankspace's length.

    int mL=blankspace.length();



    //The first loop will count up to the message's

    //length minus one.

    for (int i=0; i<L-1; i++)

    {

        //The second loop will count up to the blankspace's

        //length with 8 step's at the time in order

        //to increase speed.

        for (int j=0; j<mL; j+=8)

        {

            //Show an increasing part of the message

            //with a decreasing part of the blankspace

            //and the last letter in the message string.

            showStatus(message.substring(0,i)

                +blankspace.substring(j)

                +message.substring(i,i+1));

            try {Thread.sleep(50);}

            catch(InterruptedException e) {}

        }

    }

    //The last loop will see to that the

    //message is not overwritten by other status

    //messages.

    while(true)

    {

        showStatus(message);

        try {Thread.sleep(100);}

        catch(InterruptedException e) {}

    }

}

This effect looks much more complicated than the ones we have done until now. What you may be frowning towards are the nested FOR loops. The first loop is used to build up the sentence. For every new letter that is added in the sentence you must animate the following letter. This is what the inner loop is used for. There you use the variable "j" to crop down the blankspace area between the sentence and the animated falling letter. Please note that to crop the blankspace area we have used an overloaded substring() method, which takes a substring from the j-position to the end of the string. When the animation is finished, then we make sure we hold the thread and keep it working in a loop. This loop will be writing out the finished message in the status window, so that it is not overwritten by other browser messages. The best way to know how the effect looks like, is to see it in work by clicking here
 
3.2 Simpler graphics animation

To this point, we have only worked with animations on status windows. Personally I think that these types of effects are irritating when I encounter them on the Internet. Now, we will look at graphics animation, which are the greatest strength with Java applets and what makes them so superior to other special effects technologies such as GIF89 animations, DHTML or JavaScript.

3.2.1 Thread synchronization

As I described to your in section 3.0, you must be very careful when using threads. We will look at an example of this now.
We have so far used the paint() method to draw on the applet screen. This method is automatically called by the program thread (the main thread) when the screen needs to be updated. Now that we have two threads and we will begin animating the applet screen, we must call the paint() method a certain number of times per second. The probability is large that the main thread and our self created thread collides with each other. To avoid this, we write the word synchronized after "public" in our overwritten paint() method: 

public synchronized void paint(Graphics g)

{

}
A thread may not enter the method if there already is a thread in there working with the code. This is actually everything we need to see to that our applet does not crash because of thread collisions.

3.2.2 How to stop gray flimmering in an applet by overwriting the update() method

If we are going to make animations with the paint() method, then we must also overwrite the update() method. This method is sometimes called by the web browser before updating the applet screen, in order to clear it (using gray color). This leads to gray flimmering when you animate with the paint() method. Therefore it must be put out of action with the following lines:

public synchronized void update(Graphics g)

{

    paint(g);

}  
Now the update() method will not clear the screen, but call our paint() method instead. As you see this method should also be synchronized.

3.2.3 A graphical text scroller

We will now make an applet that is similar than the one we made in section 3.1.1, but now the text will be scrolled in the applet screen.
Make a copy of the statusscroller.java file from section 3.1.1 and change the methods init() and run() to the following content: 

public void init()

{

    message="Basic Course in Special Effects and ";
message+="Game Development in Java(TM)"; } public void run() {     while (true)     {         update(getGraphics());         try {Thread.sleep(50);}         catch(InterruptedException e) {}     } }
There are no strange things in this init() method. However we have now remade our run() method so that it uses the new updating technique. We are now calling the update() method 20 times per second (using a pause of 50 milliseconds). As argument we send the Graphics object, which is connected to the applet screen and that you can get with the getGraphics() method. This method will in turn call the paint() method, which looks like this:
public int x=100;



public synchronized void paint(Graphics g)

{

    //Paint the screen black.

    g.setColor(Color.black);

    g.fillRect(0,0,100,20);



    //Draw the message using white paint starting

    //from position "x".

    g.setColor(Color.white);

    g.drawString(meddelande,x,12);



    //Check that the "x" position for the message

    //is not less than x=-400. If it is, then

    //set "x" to the position 100 (this will make the

    //text invisible).

    if (x<-400) x=100;



    //Decrease "x" with 1 so that the text moves

    //to the left.

    x--;

}

Please note that we have added the declaration of the new applet variable "x", over the declaration of "animationthread" and "message", which already existed in the code from the old example. This variable will store the text horizontal position (x-position), even after the paint() method has been called. This position will begin with 100 and end with -400. Because the applet screen being only 100 pixels wide, the text will not be visible at the beginning, but it will be moving slowly to the left until it disappears from sight. The variable "x" will then have the value -400 pixels and then it is time to start over again. This is what the "if" statement is used for.

The effect the variable "x" has on the scrolling text's position. 

You may be wondering why I added 12 pixels to the y-position of the string? As we said in section 2.1.4, the text is being drawn from the base line and upwards. Since it is a 12 dot font, I move the text by 12 pixels. Unfortunately this trick will not work with larger fonts, but we will look at a solution later. Look at the text scroller by clicking here.

You will probably notice that the text is flimmering a bit. In the upcoming chapter 4, we will look at how you can remove this using the well known double buffering technique.

 


Copyright © 1999-2005 Mandomartis Software Company