//
Animation.java Author: Dan Harvey Written:
Spring 2003
// Sample program for cs257 quarter project.
import
java.awt.*;
import
java.util.*;
import
java.applet.*;
import
java.awt.image.*;
import
java.awt.event.*;
// The
primary class for the applet.
public
class Animation extends Applet implements Runnable
{ private final static int APPLET_WIDTH = 300;
private final static int APPLET_HEIGHT =
300; // Applet size.
private final static int DELAY =
50; // Thread sleep time.
private final static int BUFFERS =
4; // # image buffers.
private final static int INTERVAL
=100; // Execution cycles per focus.
private final static int FOCUS = 20;
// # of repositioning steps.
//
Constants for controlling movement about the display.
public final static int DIRECTIONS = 8;
public final static int DX[] = { 0, 1, 1, 1, 0,-1,-1,-1};
public final static int DY[] = {-1, -1, 0,
1, 1, 1, 0, 1};
public final static int SOUTHEAST = 3;
Rectangle bounds; // Size of the Universe.
Rectangle focus; // Focus to be displayed.
int
direction; // Direction focus
is moving.
Image
viewBuffer[]; // Applet buffers
to display.
Graphics
view[]; // Graphics object
to write Applet buffers.
Universe
universe; // Where the
creatures live.
boolean
running; // determine if
animation is running.
int
time; // Number of
universe time steps;
Thread
thread; // Thread
controlling animation.
//
Method to initialize the applet.
public
void init()
{ addMouseListener (new ControlAnimation());
MediaTracker tracker = new
MediaTracker(this);
//
Instantiate the creature images.
String[] creatureFiles = {"img/north.gif",
"img/northeast.gif"
,
"img/east.gif", "img/southeast.gif",
"img/south.gif"
,
"img/southwest.gif", "img/west.gif",
"img/northwest.gif"};
Image[] creature = new
Image[creatureFiles.length];
for (int d=0; d<creatureFiles.length;
d++)
{
creature[d] = getImage(getCodeBase(), creatureFiles[d]);
prepareImage(creature[d], this);
tracker.addImage(creature[d], 0);
}
//
Instantiate the speech images.
String[] languageFiles =
{"img/a1.gif","img/a2.gif","img/a3.gif"
,"img/a4.gif","img/a5.gif","img/b1.gif","img/b2.gif"
,"img/b3.gif","img/b4.gif"};
Image[] language= new
Image[languageFiles.length];
for (int l=0; l<languageFiles.length;
l++)
{
language[l] = getImage(getCodeBase(), languageFiles[l]);
prepareImage(language[l], this);
tracker.addImage(language[l], 0);
}
//
Prepare the background image.
String backgroundFile =
"img/alpine2.jpg";
Image background = getImage(getCodeBase(),
backgroundFile);
prepareImage(background, this);
tracker.addImage(background, 0);
try { tracker.waitForAll(); }
catch (InterruptedException e) {}
bounds = new Rectangle( 0, 0,
2*APPLET_WIDTH, 2*APPLET_HEIGHT);
focus
= new Rectangle( 0, 0, APPLET_WIDTH, APPLET_HEIGHT);
direction = SOUTHEAST;
viewBuffer = new Image[BUFFERS];
view = new Graphics[BUFFERS];
for (int b=0; b<BUFFERS; b++)
{
viewBuffer[b] = createImage(bounds.width, bounds.height);
view[b] = viewBuffer[b].getGraphics();
}
universe = new Universe(new
Dimension(bounds.width,bounds.height),
background,
creature, language);
running
= true;
time
= 0;
resize(APPLET_WIDTH, APPLET_HEIGHT);
}
// End of init.
//
Paint the applet.
public void paint(Graphics page)
{
int buffer = time % BUFFERS;
if
(time < 0) time = BUFFERS;
if (time%INTERVAL<FOCUS)
{
focus.translate(DX[direction], DY[direction]);
while
(!focus.intersection(bounds).equals(focus))
{
focus.translate(-DX[direction], -DY[direction]);
direction =
(int)(Math.random()%DIRECTIONS);
focus.translate(DX[direction], DY[direction]);
}
}
universe.getView(view[buffer], focus,
this);
if (time++ >= BUFFERS)
page.drawImage(viewBuffer[(buffer+1)%BUFFERS],0, 0,
bounds.width,bounds.height,this);
}
//
Method to prevent clear the page before painting.
public void update(Graphics page) {
paint(page); }
//
Method to start animation.
public void start()
{
if (thread == null)
{
thread = new Thread(this);
thread.start();
} }
//
Method to stop animation.
public void stop() {
thread = null; }
//
Actual animation thread runs in this method.
public void run()
{ while(thread!=null)
{
try
{ thread.sleep(DELAY);
if
(running) repaint();
}
catch(InterruptedException e) {
stop(); }
} }
//
Action listener to stop and start the animation.
private class ControlAnimation implements
MouseListener
{
public void mouseClicked( MouseEvent event)
{
Point point = event.getPoint();
point.translate(focus.x, focus.y);
universe.eventHappened(point);
}
public void mouseEntered( MouseEvent
event) {}
public
void mouseExited( MouseEvent event) {}
public void mousePressed( MouseEvent
event) {running=false;}
public void mouseReleased( MouseEvent
event){running=true;}
} // End of ControlAnimation Class
} // End of Animation class.
//
Class to control what happens in the universe of creatures.
class
Universe
{ public final int FIGURES = 64;
public final int MIN_FIGURES = 48;
public final int MAX_FIGURES = 80;
public final int SPEAKER = 0, SIZE_CHANGER
= 1, SPEED_CHANGER = 2;
public final int SPECIES = 3;
Dimension bounds; //
Universe bounds.
Image background; // The
graphics background.
ArrayList creatures; // Create
an array of figures.
public Universe(Dimension b, Image bg,
Image[] files, Image[] language)
{
bounds = b;
background = bg;
creatures = new ArrayList(MAX_FIGURES);
int figsPerRow =
(int)Math.sqrt(FIGURES);
int width = bounds.width/figsPerRow;
int height = bounds.height/figsPerRow;
int row = 0, column = 0, pixelX, pixelY;
for (int i=0; i<FIGURES; i++)
{
pixelX = row * width;
pixelY = column * height;
creatures.add(createFigure(files,
language,
new
Point(pixelX, pixelY)));
if (++column == figsPerRow)
{column=0; row++;}
}
}
// End of Universe constructor.
//
Randomly draw the image on different places on the display.
public void getView(Graphics view,
Rectangle r, Applet applet)
{
Figure figure, newFigure, otherCreature;
Point point;
Dimension size;
view.drawImage(background,-r.x,-r.y,bounds.width,bounds.height,applet);
for (int creature=0;
creature<creatures.size(); creature++)
{
figure = (Figure)(creatures.get(creature));
figure.move(bounds, true);
if
(r.intersects(figure.getPosition()))
{
for (int other = 0; other<creatures.size(); other++)
{
if (creature!=other)
{ otherCreature = (Figure)(creatures.get(other));
if
(figure.intersect(otherCreature))
{ figure.move(bounds, false);
if (figure.react()) creatures.remove(creature);
}
} }
figure.draw(view, r, applet);
}
}
//
Create a new creature when the population is too low.
if (creatures.size() < MIN_FIGURES)
{
int creature = (int)(Math.random()*creatures.size());
figure =
(Figure)(creatures.get(creature));
point =
figure.getPosition().getLocation();
size
= figure.getPosition().getSize();
point.translate(-size.width, -size.height);
if (point.x >=0 && point.y
>=0)
{
newFigure = (Figure)createFigure(point);
creatures.add(newFigure);
newFigure.speak();
}
}
}
// End of getView() method.
//
Method to create a creature at the mouse click spot on the display.
public void eventHappened(Point point)
{
Figure figure = (Figure)createFigure(point);
for (int creature=0;
creature<creatures.size(); creature++)
{
Figure other = (Figure)creatures.get(creature);
if (figure.intersect(other))
{
other.speak();
return;
}
}
if (creatures.size() <
MAX_FIGURES) creatures.add(figure);
}
//
Methods to make a new figure.
public Figure createFigure(Point p) {return
createFigure(null,null,p);}
public Figure createFigure(Image[] images,
Image[] language, Point p)
{
int type = (int)(Math.random()*SPECIES);
Figure figure = null;
switch (type)
{
case SPEAKER:
figure = (Figure)(new
Speaker(images,language,p));
break;
case SPEED_CHANGER:
figure = (Figure)(new
SpeedChanger(images,language,p));
break;
case SIZE_CHANGER:
figure = (Figure)(new
SizeChanger(images,language,p));
break;
}
return figure;
}
} // End of Universe class.
//
Class to represent the figures.
abstract
class Figure
{ public final static int SPEED = 5;
public final static int WIDTH = 50,
HEIGHT = 50;
public final static int MESSAGE_WIDTH =
100, MESSAGE_HEIGHT = 100;
public final static int SPEAK_LENGTH = 5000;
static Image[] images; // The images
of this figure.
static Image[] phrases; // The images
for figure language.
int
direction; // The current
travel direction.
int
speed; // Current speed
of travel.
Rectangle position; // The position of the image in the
universe.
int
speakMessage; // Which message
to speak.
long
speakTime; // When the
speaking started.
int
intersections; // Count of
intersections.
//
Constructor.
public Figure(Image[] images, Image[]
phrases, Point p)
{
if (images!=null) this.images = images;
// Figure and phrase images.
if (phrases!=null) this.phrases = phrases;
direction = (int)(Math.random()*Animation.DIRECTIONS);
speed = SPEED;
position = new Rectangle(p, new Dimension(WIDTH,HEIGHT));
speakMessage = 0; // Message to speak.
speakTime = 0; // Initialize
when speaking started.
intersections = 0; // Count the intersections.
}
//
Public Method to determine if two figures intersect.
public boolean intersect(Figure figure)
{
if (position.intersects(figure.position))
{
intersections++; return
true; }
return false;
}
//
Public Method to get position of figure.
public Rectangle getPosition() { return
position; }
//
Method to speak by displaying a message.
public
void speak()
{
speakTime = System.currentTimeMillis();
speakMessage
= (int)(Math.random()*phrases.length);
}
//
Method to move the figure.
public void move(Dimension range, boolean
normal)
{
if (!normal)
direction =
(direction+Animation.DIRECTIONS/2)%Animation.DIRECTIONS;
move(range);
}
//
Abstract method to determine how a creature reacts
// upon intersection.
public abstract boolean react();
//
Method to move the figure.
private void move(Dimension range)
{
position.x += speed*Animation.DX[direction];
position.y +=
speed*Animation.DY[direction];
if (position.x<0) position.x = 0;
else if (position.y<0) position.y = 0;
else if
(position.x>range.width-position.width)
position.x =
range.width-position.width;
else if
(position.y>range.height-position.height)
position.y =
range.height-position.height;
else return;
// Hit
end of range in one of the directions. Change direction.
direction =
(int)(Math.random()*Animation.DIRECTIONS);
}
// End of move method.
//
Method to draw a figure.
public void draw(Graphics page, Rectangle
r, Applet applet)
{
page.drawImage(images[direction], position.x-r.x, position.y-r.y,
position.width,
position.height, applet);
if (System.currentTimeMillis()-speakTime
<= SPEAK_LENGTH)
{
int phraseX = position.x - MESSAGE_WIDTH/2 - r.x;
int
phraseY = position.y - MESSAGE_HEIGHT - r.y;
int
phraseWidth = MESSAGE_WIDTH;
int
phraseHeight = MESSAGE_HEIGHT;
page.drawImage(phrases[speakMessage],
phraseX, phraseY,
phraseWidth,
phraseHeight, applet);
}
} //
End of draw method.
} // End of Figure class.
//
Polymorphic classes with different reactions.
//
Creature that changes speed on intersection.
class
SpeedChanger extends Figure
{ public static final int MAX_SPEED = 30;
public static final int MIN_SPEED = 1;
int speedDelta;
//
Constructor.
public SpeedChanger(Image[] images, Image[]
phrases, Point p)
{
super(images, phrases, p);
speedDelta = (int)(Math.random()*2);
if (speedDelta==0) speedDelta = - 1;
}
// Method
to change the speed of travel.
public boolean react()
{
direction = (direction+1)%Animation.DIRECTIONS;
speed
+= speedDelta;
if (speed>MAX_SPEED) return true;
if (speed<MIN_SPEED) return true;
return false;
}
} // End of SpeedChanger class.
//
Creature that changes size on intersection.
class
SizeChanger extends Figure
{ public static final int MIN_SIZE = 10;
public static final int MAX_SIZE = 100;
int sizeDelta;
//
Constructor.
public SizeChanger(Image[] images, Image[]
phrases, Point p)
{
super(images, phrases, p);
sizeDelta = (int)(Math.random()*2);
if (sizeDelta==0) sizeDelta = - 1;
}
//
Method to change the size creature.
public boolean react()
{
direction = (direction+3)%Animation.DIRECTIONS;
position.width += sizeDelta;
position.height += sizeDelta;
if (position.width>MAX_SIZE) return
true;
if (position.width<MIN_SIZE) return
true;
return false;
}
} // End of SizeChanger class.
//
Creature that speaks on intersection.
class
Speaker extends Figure
{ public static final int MAX_INTERSECTIONS =
50;
//
Constructor.
public Speaker(Image[] images, Image[]
phrases, Point p)
{
super(images, phrases, p); }
//
Method to start the speaking.
public boolean react()
{ speak();
direction =
(int)(Math.random()*Animation.DIRECTIONS);
if (intersections > MAX_INTERSECTIONS)
return true;
else return false;
}
} // End of Speaker class.