JavaRanch Home    
 
This page:         last edited 09 October 2007         What's Changed?         Edit
PROTECTED

Code Barn Hello Anim Thread B   

You must turn on Java for applets to work.

IMPORTANT: This is the third in a series of four applet examples for animation. After this, see HelloAnimThreadC?. Make sure you understand this example and the first two-- HelloAnimThreadFirst? and HelloAnimThreadA? -- before moving on.

ALSO IMPORTANT: This is not the ideal way to animate. This example overrides the update() method, which helps improve flicker, but since update() normally does the "erasing" of the background, you will see a trail of rectangles -- each new rectangle will be drawn, but the previous one won't be erased. To fix this animation completely, and make it flicker-free and smooth, we will put everything together in HelloAnimThreadC? which adds double-buffering to the code below. Be sure that you understand the effect of overriding update() before you move on to the final example.

Explanation of HelloAnimThreadB? applet

Number of classes: 1 (HelloAnimThreadB?.class)

What it does: Animates a small rectangle across the screen, bouncing when it hits the edges of the applet space. Leaves a trail everywhere the rectangle is drawn, since we have overriden the method which normally erases the background.

How to do animation in Java:

You have two choices for animation:

1) Display a series of different images (as you often do with gif animations)

OR

2) Display just a single image, but keep updating that image's location on the screen

Of course, you can combine the two methods, as we did with the moving cows in the Roundup game... there are four different "looks" (different .gif files) for each cow, but the cows also move across the screen.

In this animation series of applets, we draw a rectangle on the screen using a method of the Graphics class, rather than drawing a .gif image.

Animation Overview for this applet:

In a permanent loop, update / change the x and y coordinates of a rectangle and then repaint it in the new location. To the user, this will appear as a moving rectangle... but it will also leave a trail.

Step-by-Step:

  1. Define instance variables for the rectangles size and x,y screen coordinates (which represent the top left corner of the rectangle), and the right and bottom of the applet (max width and max height). Also define instance variables for the rectangles x and y velocity. This determines how many pixels (and in which direction) the rectangle will move with each loop.
  2. Implement a run() method (did we mention that this applet implements the runnable interface? You'll see why in a minute...). This run method contains the infinite loop (while (true)) that drives the animation.
  3. In the run method, update the x and y instance variables so the rectangle will be drawn at a new location The x and y will also be checked for the rectangle hitting the edges of the applet. If the rectangle hits an edge, the velocity will be reversed for that dimension (i.e. if the rectangle hits the top, then the y velocity will be reversed so the rectangle will start traveling down again -- y will be incremented positively instead of negatively)
  4. At the end of the run method, call repaint() (a method the applet can respond to). Repaint will ultimately lead to the applet's paint() method being called. Don't call paint directly!
  5. Override the update() method. Update is scheduled when you call repaint(). (You don't call update directly). Update normally just erases the background by filling it with the current background color. To prevent this erasing step, you can override update(), but you must still include the call to paint() which update() normally does. Otherwise, paint() won't be called when you call repaint(), since repaint() triggers update() which calls paint(). Since we have overriden update() to stop it from doing the erasing, you will see a trail of rectangles. Obviously, this is not the final result we want -- but overriding update() is the first step to improving our animation. In the last example (C) we'll take care of the erasing ourselves.
  6. Override the paint() method, and give it something to do: Draw the rectangle in a new position, using the instance variables for x, y and the size (height and width) of the rectangle.
  7. Be sure to create and start a Thread object at the end of the init method.



CODE


// Simple Animation -- third in a series of four --- series continues through HelloAnimThreadC
// A rectangle bounces off the edges of the applet, forever. 
// Flickering will be completely fixed in the final HelloAnimThreadC

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

    private int maxWidth , maxHeight ; // animation boundary
    private int currX , currY ; // left,top (x,y) of the animating rectangle
    private int currXVelocity , currYVelocity ; // pixels per move
    private int imageHeight , imageWidth ; // size of animating rectangle
    
    public void init() 
    {
        setBackground( Color.yellow );
        maxWidth = 200 ;
        maxHeight = 200 ;
        imageHeight = 25 ;
        imageWidth = 40 ;
        currXVelocity = 3 ;
        currYVelocity = 3 ;
    
        // set up random positions to start the images
        currX = (int)( ( Math.random() * 80 ) + 1 );
        currY = (int)( ( Math.random() * 80 ) + 10 );
    
        Thread animator = new Threadthis );
        animator.start();
    } //close init
    
    public void paint( Graphics g ) 
    {
        g.drawRect( currX , currY , imageWidth , imageHeight );
    } // close paint
    
    // override update to prevent it from erasing the background
    public void update( Graphics g ) 
    {
        paint( g ); // we still have to call paint
    }
    
    public void run() 
    {
        // update location/velocity of the rectangle 
        while ( true ) 
        {
            try 
            {
                Thread.sleep( 50 );
            } 
            catch ( InterruptedException e ) 
            {
            }
    
            currX = currX + currXVelocity ;
            currY = currY + currYVelocity ;
    
            if ( ( currX + imageWidth ) >= maxWidth )
            {
                currXVelocity = -3 ; 
            }
            else if ( currX <= 0 )
            {
                currXVelocity = 3 ;
            }
    
            if ( ( currY + imageHeight ) >= maxHeight ) 
            {
                currYVelocity = -3 ;
            }
            else if ( currY <= 0 ) 
            {
                currYVelocity = 3 ;
            }
    
            repaint(); 
        } // close while loop 
    } // close run
// close class 


CodeBarnApplets

JavaRanchContact us — Copyright © 1998-2014 Paul Wheaton