|
Basics of Computer Animation
in Java |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
An Illustration
Timer object that sends
actionPerformed() messagesActionListener on the Timer
that updates state information each "tick"JComponent that renders itself using the
state information
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* A simple example of ad-hoc animation
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class AnimatedRectangle extends JComponent implements ActionListener
{
private float direction, height, width, x, y;
private Line2D.Float ground;
private Timer timer;
/**
* Default Constructor
*/
public AnimatedRectangle()
{
super();
width = 50.0f;
height = 50.0f;
x = 100.0f;
y = 100.0f;
direction = 1.0f;
ground = new Line2D.Float(0f,480f,480f,480f);
timer = new Timer(10, this);
timer.start();
}
/**
* Handle actionPerformed messages generated by the Timer
* (Required by ActionListener)
*
* @param evt The ActionEvent
*/
public void actionPerformed(ActionEvent evt)
{
if (y <= 100) direction = 1.0f;
else if (y+height >= 480) direction = -1.0f;
y += direction;
repaint();
}
/**
* Render this Painting
*
* @param g The rendering engine to use
*/
public void paint(Graphics g)
{
Graphics2D g2;
Rectangle2D.Float rectangle;
g2 = (Graphics2D)g;
g2.setColor(Color.BLACK);
g2.draw(ground);
g2.setColor(Color.RED);
rectangle = new Rectangle2D.Float(x, y, width, height);
g2.draw(rectangle);
}
}
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* A simple example of ad-hoc animation
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class AnimatedCurve extends JComponent implements ActionListener
{
private CubicCurve2D.Float curve;
private float[] x, y;
private Stroke curveStroke;
private Timer timer;
/**
* Default Constructor
*/
public AnimatedCurve()
{
super();
curveStroke = new BasicStroke(2.0f,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_MITER);
setBackground(Color.white);
x = new float[4];
y = new float[4];
x[0] = 0.0f; y[0] = 0.0f;
x[1] = 100.0f; y[1] = 100.0f;
x[2] = 300.0f; y[2] = 300.0f;
x[3] = 400.0f; y[3] = 400.0f;
curve = new CubicCurve2D.Float();
updateCurve();
timer = new Timer(100, this);
timer.start();
}
/**
* Handle actionPerformed messages generated by the Timer
* (Required by ActionListener)
*
* @param evt The ActionEvent
*/
public void actionPerformed(ActionEvent evt)
{
x[0] += 1.0f;
y[0] += 1.0f;
x[1] += 1.0f;
y[1] -= 1.0f;
x[2] -= 1.0f;
y[2] += 1.0f;
x[3] -= 1.0f;
y[3] -= 1.0f;
updateCurve();
repaint();
}
/**
* Render this Painting
*
* @param renderer The rendering engine to use
*/
public void paint(Graphics g)
{
Graphics2D g2;
g2 = (Graphics2D)g;
g2.setColor(Color.black);
if (curveStroke != null) g2.setStroke(curveStroke);
if (curve != null) g2.draw(curve);
}
/**
* Update the points in the curve
*/
private void updateCurve()
{
curve.setCurve(x[0],y[0],x[1],y[1],
x[2],y[2],x[3],y[3]);
}
}
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.io.*;
import java.util.Random;
import javax.imageio.*;
import javax.swing.*;
/**
* An example of animation with user interaction
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class WhackADuke extends JComponent
implements ActionListener, MouseListener
{
private Image head;
private int direction, hits, speed, steps, x, y;
private Random rng;
private Rectangle2D.Float base;
private Timer timer;
private final Color GOLD = new Color(0xc2,0xa1,0x4d);
private final Font SCORE_FONT = new Font(Font.MONOSPACED, Font.PLAIN, 20);
private final int HEAD_HEIGHT = 70;
private final int HEAD_WIDTH = 70;
private final int HIDDEN_STEPS = 10;
/**
* Default Constructor
*/
public WhackADuke()
{
// Construct a random number generator
rng = new Random();
// Read the Madison head
try
{
head = ImageIO.read(new File("madison.png"));
}
catch (IOException ioe)
{
head = null;
}
// Construct the base
base = new Rectangle2D.Float(0f, 400f, 500f, 100f);
// Initialize
direction = 0;
x = 0;
y = (int)base.getY();
speed = 2;
// Make this object a MouseListener on itself
addMouseListener(this);
// Construct and start the timer
timer = new Timer(10, this);
timer.start();
}
/**
* Handle actionPerformed messages (required by ActionListener)
*
* @param event The ActionEvent that generated the message
*/
public void actionPerformed(ActionEvent event)
{
if (direction == 0) // The head isn't moving
{
steps++;
if (steps > HIDDEN_STEPS)
{
x = rng.nextInt(400);
y = (int)base.getY();
steps = 0;
direction = -1;
}
}
else if (direction == -1) // The head is moving up
{
y += direction * speed;
if (y <= ((int)base.getY() - HEAD_HEIGHT))
{
direction = 1;
}
}
else if (direction == 1) // The head is moving down
{
y += direction * speed;
if (y >= (int)base.getY())
{
direction = 0;
}
}
repaint();
}
/**
* Handle mouseClicked messages (requried by MouseListener)
*
* @param event The MouseEvent that generated the message
*/
public void mouseClicked(MouseEvent event)
{
int mouseX, mouseY;
// Get the position of the mouse
mouseX = event.getX();
mouseY = event.getY();
// Check for a whack
if ((direction != 0) &&
(mouseX >= x) && (mouseX <= x+HEAD_WIDTH) &&
(mouseY >= y) && (mouseY<= y+HEAD_HEIGHT))
{
hits++;
}
}
/**
* Handle mouseEntered messages (requried by MouseListener)
*
* @param event The MouseEvent that generated the message
*/
public void mouseEntered(MouseEvent event)
{
}
/**
* Handle mouseExited messages (requried by MouseListener)
*
* @param event The MouseEvent that generated the message
*/
public void mouseExited(MouseEvent event)
{
}
/**
* Handle mousePressed messages (requried by MouseListener)
*
* @param event The MouseEvent that generated the message
*/
public void mousePressed(MouseEvent event)
{
}
/**
* Handle mouseReleased messages (requried by MouseListener)
*
* @param event The MouseEvent that generated the message
*/
public void mouseReleased(MouseEvent event)
{
}
/*
* Render this JComponent
*
* @param g The rendering engine to use
*/
public void paint(Graphics g)
{
float height, width;
Graphics2D g2;
Rectangle2D background;
g2 = (Graphics2D)g;
// Clear the screen
background = getBounds();
width = (float)background.getWidth();
height = (float)background.getHeight();
g2.setColor(Color.WHITE);
g2.fill(background);
// Render the head
g2.drawImage(head, x, y, null);
// Render the base
g2.setColor(GOLD);
g2.fill(base);
// Render the score
g2.setColor(Color.BLACK);
g2.setFont(SCORE_FONT);
g2.drawString("Score: "+hits, 300, 100);
}
}
Fish Classimport java.awt.*;
import java.awt.image.*;
import java.util.*;
/**
* A Fish that "swims" in an interesting way.
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class Fish
{
protected BufferedImage[] images;
protected int initialSpeed, maxX, maxY, speed, x, y;
protected int state, stateChange;
protected int ticks, ticksInState;
private static final int INITIAL_LOCATION = -320;
private static final Random rng = new Random();
/**
* Explicit Value Constructor
*
* @param threeImages The three Image objects for this Fish
* @param width The width of the fishtank
* @param height The height of the fishtank
* @param speed The normal speed
*/
public Fish(BufferedImage threeImages, int width, int height, int speed)
{
int imageHeight, imageWidth;
if (threeImages != null)
{
imageHeight = threeImages.getHeight(null);
imageWidth = threeImages.getWidth(null)/3;
images = new BufferedImage[3];
for (int i=0; i<3; i++)
{
images[i] = threeImages.getSubimage(i*imageWidth,0,
imageWidth,imageHeight);
}
}
maxX = width;
maxY = height;
x = rng.nextInt(maxX);
y = rng.nextInt(maxY);
this.speed = speed;
this.state = 0;
this.stateChange = 1;
this.ticksInState = 20 - 2*speed;
}
/**
* Paint this Fish
*
* @param g The rendering engine to use
*/
public void paint(Graphics g)
{
ticks += 1;
if (ticks > ticksInState)
{
ticks = 0;
state += stateChange;
if (state == 2) stateChange = -1;
else if (state == 0) stateChange = 1;
}
x += speed;
if (x > maxX)
{
x = INITIAL_LOCATION;
y = rng.nextInt(maxY);
}
if (images[state] != null) g.drawImage(images[state], x, y, null);
}
}
FishTank
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.util.Random;
import javax.imageio.*;
import javax.swing.*;
/**
* An example of animation
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class FishTank extends JComponent
implements ActionListener
{
private BufferedImage ocean;
private Fish[] fish;
private Timer timer;
private static final Random rng = new Random();
/**
* Default Constructor
*/
public FishTank()
{
BufferedImage threeImages;
try
{
ocean = ImageIO.read(new File("ocean.png"));
threeImages = ImageIO.read(new File("fish.png"));
}
catch (IOException ioe)
{
ocean = null;
threeImages = null;
}
fish = new Fish[5];
for (int i=0; i<5; i++)
fish[i] = new Fish(threeImages, 640, 480, rng.nextInt(3)+1);
timer = new Timer(10, this);
timer.start();
}
/**
* Handle actionPerformed messages (required by ActionListener)
*
* @param event The ActionEvent that generated the message
*/
public void actionPerformed(ActionEvent event)
{
repaint();
}
/*
* Render this JComponent
*
* @param g The rendering engine to use
*/
public void paint(Graphics g)
{
Graphics2D g2;
g2 = (Graphics2D)g;
if (ocean != null) g2.drawImage(ocean, 0, 0, null);
for (int i=0; i<fish.length; i++) fish[i].paint(g);
}
}