|  
             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 
            
    |