Title: Animation
Course: CPS 261: Advanced Java Concepts ~ DL & MM MASTER

Animation

Multi-tasking opens up the doors to a new set of applications. This topic will be covered in much more depth later. However, its useful to show the potential in a GUI to provide Animated applicatons.

There are 2 basic ways to create threads.

Approach1: Extend the Thread class

Key Points:

- Extending the Thread class ... take a look at online documentation to see this class
- Override the run method - This is where the Thread lives for it's whole life time.
- To make it run, construct it and call it's start method
- The sleep method is a useful capability for us now because we are interested in periodically doing something. You pass the number milleseconds that you want your Thread to sleep.
- Note that you have to catch the InterruptedException(checked Exception)


import java.util.Date;


public class MyThread extends Thread{

    public void run()
    {
        while (true)
        {
            try
            {
                Thread.sleep(1000); // Sleep for 1 second
                Date d = new Date();
                System.out.println("Date =" + d);
            }
            catch (InterruptedException e)
            {
            }
        }
    }
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
        System.out.println("Exitting main thread");

    }

}


Approach2: Implement the Runnable Interface

- A more flexible approach to using Threads is to use the Runnable Interface.
- In Java you can only extend one class. So the above Thread approach limits itself in how it can be used.
- Any class can implement the Runnable interface which has one required method: run
- To make a Thread, construct the class which implements Runnable, then Construct a Thread using the Thread constructor that has the pattern
Thread (Runnable r)
- Then call the start method on the Thread constructed in the above step


import java.util.Date;


public class MyClass implements Runnable{

    public void run()
    {
        while (true)
        {
            try
            {
                Thread.sleep(1000); // Sleep for 1 second
                Date d = new Date();
                System.out.println("Date =" + d);
            }
            catch (InterruptedException e)
            {
            }
        }
    }
    public static void main(String[] args) {
        MyClass mc = new MyClass();
        Thread t = new Thread(mc); // This constructor needs any class which implements Runnable
        t.start();

        System.out.println("Exitting main thread");

    }

}


Threads in a JFrame

The above approaches can be used in a JFrame as follows::



import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.*;

public class Timer extends JFrame
        implements Runnable{


    public Timer(String name)
    {
        super(name);
        setSize(800, 400);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Thread thread = new Thread(this);
        thread.start();
    }
    public void run()
    {
        while(true)
        {
           try
           {
                Thread.sleep( 1000); // Sleep for 1 second
                repaint();
           }
           catch (InterruptedException e)
           {}
        }

    }

    public void paint(Graphics g)
    {
        super.paint(g);

        Date date = new Date();
        g.drawString(date.toString(), 50,50);
    }


    public static void main(String[] args) {
        Timer t = new Timer("What time is it??");
        System.out.println("Exitting main thread");
    } // end of main


}





Animations:

1. Create an animated Juggler. Again have your class implement Runnable. In the run method, sleep for a short time and call repaint(); You will display one of the 2 sequences below:

The following images are named j0.gif, j1.gif, j2.gif, j3.gif

The following images are named T0.gif, T1.gif, T2.gif .... T8.gif



import java.awt.Graphics;
import java.awt.Image;
import java.util.Date;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

public class Animation extends JFrame
        implements Runnable{
   // String[] fileNames = {"j0.gif", "j1.gif", "j2.gif", "j3.gif", "j4.gif"};
    String[] fileNames = {"T0.gif", "T1.gif", "T2.gif", "T3.gif",
            "T4.gif", "T5.gif", "T6.gif", "T7.gif", "T8.gif"};

    Image[] images;
    int index = 0;
    int next=0;

    public Animation(String name)
    {
        super(name);
        loadImages();
        setSize(800, 400);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Thread thread = new Thread(this);
        thread.start();
    }
    public void run()
    {
        while(true)
        {
           try
           {
// Try changing this number to something smaller, like 200 milliseconds
                Thread.sleep( 1000); // Sleep for 1 second
                repaint();
           }
           catch (InterruptedException e)
           {}
        }

    }

    public void paint(Graphics g)
    {
        super.paint(g);

        next = index % fileNames.length;
        index += 1;
        g.drawImage(images[next], 50,50,null);
        g.drawString(fileNames[next], 50, 250);
    }

    private void loadImages()
    {
        int len = fileNames.length;
        images = new Image[len];

        for (int i=0; i < fileNames.length; i++)
        {
            images[i] = loadImage(fileNames[i]);
        }
    }
    private Image loadImage(String fileName)
    {
      return new ImageIcon(fileName).getImage();
    }

    public static void main(String[] args) {
        Animation t = new Animation("Movie Time");
        System.out.println("Exitting main thread");
    } // end of main


}



Eliminating flicker

By now I'm sure some of you are asking how do I get rid of the flicker on the screen.

The problem is that we draw a screen, and then we clear the screen and draw the screen again. Your eye sometimes catches glipses of the screen when it is clear and some of the glipses occur when everything is drawn.

- Solution: Draw into a Memory Screen Image and then copy the Memory to the real screen.
- To do this, we need to allocate Memory big enough for the screen (by the way, the class for the Memory image with be "Image")
- Then we need to create a Graphics class associated with the screen
- We then need to copy the Image memory to the real screen
- Note that the technique we are using here is very general in that it can be used for any paint routine in any GUI application that we develop.



import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

public class Animation extends JFrame
        implements Runnable{
    String[] fileNames = {"j0.gif", "j1.gif", "j2.gif", "j3.gif", "j4.gif"};
   // String[] fileNames = {"T0.gif", "T1.gif", "T2.gif", "T3.gif",
    //        "T4.gif", "T5.gif", "T6.gif", "T7.gif", "T8.gif"};

    Image[] images;
    int index = 0;
    int next=0;
    Image offScreenImage = null;
    Dimension screenDimension = null;


    public Animation(String name)
    {
        super(name);
        loadImages();
        setSize(800, 400);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Thread thread = new Thread(this);
        thread.start();
    }
    public void paint(Graphics screen)
    {
        Dimension dimen = getSize();
        if (offScreenImage==null || !dimen.equals(screenDimension))
        {
            screenDimension = dimen;
            offScreenImage = createImage(dimen.width, dimen.height);
        }
        Graphics g = offScreenImage.getGraphics();

        next = index % fileNames.length;
        index += 1;
        g.setColor(Color.white);
        g.fillRect(0, 0, dimen.width, dimen.height);
        g.drawImage(images[next], 50,50,null);
        g.drawString(fileNames[next], 50, 250);

        screen.drawImage(offScreenImage, 0,0,this);
    }

    public void run()
    {
        while(true)
        {
           try
           {
                Thread.sleep( 1000); // Sleep for 1 second
                repaint();
           }
           catch (InterruptedException e)
           {}
        }

    }


    private void loadImages()
    {
        int len = fileNames.length;
        images = new Image[len];

        for (int i=0; i < fileNames.length; i++)
        {
            images[i] = loadImage(fileNames[i]);
        }
    }
    private Image loadImage(String fileName)
    {
      return new ImageIcon(fileName).getImage();
    }
    public static void main(String[] args) {
        Animation t = new Animation("Movie Time");
        System.out.println("Exitting main thread");
    } // end of main


}



Create a one person pong game where a ball bounces around in a box. The user has a paddle that must keep the ball from entering a hole on the end of the box. This needs to be drawn.
Picture of Pong Game

Box.java


package pong;

import java.awt.Point;
import java.util.Random;

public class Box {
    Point boxUpperRight ;
    Point boxUpperLeft  ;
    Point boxLowerRight ;
    Point boxLowerLeft  ;
    Point holeUpper     ;
    Point holeLower     ;
    Point ballLoc       ;
    Point paddleLoc     ;

    int paddleWidth ;
    int ballRadius = 10;
    private int ballVx, ballVy;
    private Random rand = new Random();

    int successCount=0;
    boolean bouncing=true;



    public Box(int box_top, int box_bottom, int box_left, int box_right)
    {
        boxUpperRight= new Point(box_right, box_top);
        boxUpperLeft = new Point(box_left, box_top);
        boxLowerRight= new Point(box_right, box_bottom);
        boxLowerLeft = new Point(box_left, box_bottom);
        holeUpper    = new Point(box_right, box_top +(box_bottom-box_top)/4);
        holeLower    = new Point(box_right, box_top +3*(box_bottom-box_top)/4);
        ballLoc      = new Point(box_left+ rand.nextInt(box_right - box_left),
                                box_top+ rand.nextInt(box_bottom - box_top));
        paddleLoc    = new Point(box_right,( holeUpper.y+holeLower.y)/2);
        paddleWidth  = (holeLower.y - holeUpper.y)/3;
        ballVx = (-10 + (int)(20*Math.random()));
        ballVy = -10 + (int)(20*Math.random());
    }
    public void setPaddleY(int y)
    {
        paddleLoc.y =y;
        if (paddleLoc.y - paddleWidth/2 < holeUpper.y)
            paddleLoc.y = holeUpper.y + paddleWidth/2;
        if (paddleLoc.y + paddleWidth/2 > holeLower.y)
            paddleLoc.y = holeLower.y - paddleWidth/2;

    }

    public void update()
    {
        if (!bouncing)return;

        ballLoc.x = ballLoc.x + ballVx;
        ballLoc.y = ballLoc.y + ballVy;
        if (ballLoc.x + ballRadius > boxUpperRight.x)
        {  //ball either bounces off right wall, hits paddle, or lands in the hole
            if (ballLoc.y <= holeUpper.y || ballLoc.y >= holeLower.y )
            {   // ball bounces off the right wall
                ballVx *= -1;
                ballLoc.x = boxUpperRight.x - ballRadius;
            }
            else if (ballLoc.y >= paddleLoc.y-paddleWidth/2 &&
                    ballLoc.y <= paddleLoc.y + paddleWidth/2)
            {   // ball bounces off paddle
                successCount +=1;  // In hole but bounces off paddle
                ballVx *= -1;
                ballLoc.x = boxUpperRight.x - ballRadius;
                System.out.println("In Hole and hits paddle");
            }
            else
            {
                // In hole and missed by paddle
                bouncing = false;
                System.out.println("In Hole and missed by paddle");
            }
        }
        if (ballLoc.x - ballRadius < boxUpperLeft.x)
        {   // Ball bounces off left wall
            ballVx *= -1;
            ballLoc.x = boxUpperLeft.x + ballRadius;
        }
        if (ballLoc.y + ballRadius > boxLowerRight.y)
        {   // Ball bounces off bottom wall
            ballVy *= -1;
            ballLoc.y = boxLowerRight.y - ballRadius;
        }
        if (ballLoc.y - ballRadius < boxUpperRight.y)
        {   // Ball bounces off top wall
            ballVy *= -1;
            ballLoc.y = boxUpperRight.y + ballRadius;
        }
    }



}



Pong.java


 package pong;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;

public class Pong extends JFrame
        implements Runnable{

    Box box = null;
    int PAD=20;
    Image offScreenImage = null;
    Dimension screenDimension = null;

    // ****   INNER CLASS
    class Mouser extends MouseAdapter
    {
        public void mouseMoved(MouseEvent e)
        {
            int y= e.getY();
            if (box != null)
            {
                box.setPaddleY(y);
            }
            repaint();
        }
        public void mousePressed(MouseEvent e)
        {
           box = null;
           repaint();
        }

    }


    // ****   END OF INNER CLASS

    public Pong(String name)
    {
        super(name);
        setSize(800, 400);
        setResizable(false);  // We don't want this window resized
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Thread thread = new Thread(this);
        thread.start();
        Mouser m = new Mouser();
        addMouseMotionListener(m);
        addMouseListener(m);
        setVisible(true);
    }
    public void run()
    {
        while(true)
        {
           try
           {
                Thread.sleep( 10); // Sleep for 0.1 second
                if (box !=null)
                    box.update();
                repaint();
           }
           catch (InterruptedException e)
           {}
        }

    }

    public void paint(Graphics screen)
    {
        Dimension d = getSize();
        if (offScreenImage==null || !d.equals(screenDimension))
        {
            screenDimension = d;
            offScreenImage = createImage(d.width, d.height);
        }
        Graphics g = offScreenImage.getGraphics();

        g.setColor(Color.white);
        g.fillRect(0,0, d.width, d.height);
        g.setColor(Color.black);

        Insets insets = getInsets();

        if (box == null)
            box = new Box(insets.top+PAD, d.height - insets.bottom-2*PAD,
                    insets.left+PAD, d.width-insets.right-2*PAD);
        if (!box.bouncing)
        {
            String str ="You missed, your success count="+
                box.successCount+ " Click Mouse to restart";
            g.drawString(str, 100, 100);
        }

        Point bur = box.boxUpperRight;
        Point bul = box.boxUpperLeft;
        Point blr = box.boxLowerRight;
        Point bll = box.boxLowerLeft;
        Point hu  = box.holeUpper;
        Point hl  = box.holeLower;
        g.drawLine(bll.x, bll.y, blr.x, blr.y); // lower line
        g.drawLine(bll.x, bll.y, bul.x, bul.y); // left side
        g.drawLine(bul.x,bul.y, bur.x, bur.y);  // top side
        g.drawLine(bur.x, bur.y, hu.x, hu.y);   // above hole on right
        g.drawLine(blr.x, blr.y, hl.x, hl.y);   // below hole on right

        Point pball = box.ballLoc;
        int r = box.ballRadius;
        g.fillOval(pball.x-r, pball.y-r, 2*r, 2*r);

        Point pPaddle = box.paddleLoc;
        int w = box.paddleWidth;
        g.setColor(Color.red);
        int x= pPaddle.x ;
        g.drawLine(x, pPaddle.y-w/2, x, pPaddle.y+w/2);

        screen.drawImage(offScreenImage, 0,0, this);
    }


    public static void main(String[] args) {
        Pong p = new Pong("Pong game");
    } // end of main



}



Last Updated: September 8, 2013 1:59 AM