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); } }