In the Chapter 8:
Java: Animations we presented examples of animations with Swing
components but using the standard drawing tools in the Graphics
class. Here we remake the DropApplet
animation example with the more capable Java 2D drawing classes.
As before, the Drop2DApplet
program below illustrates a simple simulation of a bouncing ball.
The applet creates a thread to direct the drawing of the frames
of the animation as the ball falls and bounces on the floor and
gradually comes to a rest. The interface consists of a subclass
of JPanel
called Drop2DApplet
and a button to initiate a new drop of the ball. Drop2DApplet
displays the ball and calculates its position.
The applet implements Runnable
and in the start()
method it creates a thread to which it passes a reference to itself.
The applet's run()
method first does some initialization and then enters a loop that
draws each frame of the animation. The loop begins with a 25 millisecond
pause using the static sleep()
method in the Thread
class. Then the Drop2DApplet
is told to paint the next frame.
If the drop is done, the process jumps from the loop and exits
the run()
method, thus killing this thread.
Note how the antialiasing makes the ball much more smoothly round
than with the AWT version.
|
import
javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* This applet implements Runnable and uses
a thread
* to create a simple dropped ball demonstration.
**/
public class Drop2DApplet extends JApplet
implements
Runnable, ActionListener{
// Will use thread reference as a flag
Thread fThread;
Drop2DPanel fDropPanel;
JButton fDropButton;
/** Build the interface. **/
public void init () {
Container content_pane = getContentPane
();
content_pane.setLayout (new BorderLayout
());
// Create an instance of DropPanel
fDropPanel = new Drop2DPanel ();
// Add the DropPanel to the content
pane.
content_pane.add ("Center",fDropPanel);
// Create a button and add it
fDropButton = new JButton ("Drop");
fDropButton.addActionListener
(this);
content_pane.add ("South",fDropButton);
} // init
/** Start when browser is loaded or button
pushed. **/
public void start () {
// If the thread reference not
null then a
// thread is already running.
Otherwise, create
// a thread and start it.
if (fThread == null){
fThread
= new Thread (this);
fThread.start
();
}
} // start
/** Applet's stop method used to stop thread.**/
public void stop () {
// Setting thread to null will
cause loop in
// run () to finish and kill
the thread.
fThread = null;
}
/** Button command. **/
public void actionPerformed (ActionEvent ae){
if (fDropPanel.isDone ()) start
();
}
/** The thread loops to draw each frame of
drop.**/
public void run () {
// Disable button during drop
fDropButton.setEnabled (false);
// Initialize the ball for the
drop.
fDropPanel.reset ();
// Loop through animation frames
while ( fThread != null){
// Sleep 25msecs
between frames
try{ Thread.sleep
(25);
} catch (InterruptedException
e) { }
// Repaint drop panel
for each new frame
fDropPanel.repaint
();
if (fDropPanel.isDone
()) fThread = null;
}
// Enable button for another drop
fDropButton.setEnabled (true);
} // run
} // class DropApplet
|
import
javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
/** This JPanel subclass animates the dropping of a ball.**/
public class Drop2DPanel extends JPanel{
// Parameters for the drop
double fY = 0.0, fVy = 0.0;
// Conversion factor from cm to pixels.
double fYConvert = 0.0;
double fXPixel = 0.0, fYPixel = 0.0;
double fRadius = 0.0, fDiam = 0.0;
// starting point for ball in cm
double fY0 = 1000.0;
// Frame dimensions.
double fFrameHt, fFrameWd;
// Flag for drop status
boolean fDropDone = false;
Ellipse2D fBall;
/** Reset parameters for a new drop. **/
void reset(){
fFrameHt = getHeight();
fFrameWd = getWidth();
fXPixel = getWidth()/2;
fY = fY0; fVy = 0;
// Conversion factor from cm to pixels
// Start the ball about 20% from the
top
// of the frame
fYConvert = fFrameHt / (1.2 * fY0);
// Choose a size for the ball relative
to the
// height in pixel units.
fRadius = (int)((0.1 * fY0) * fYConvert);
fDiam = 2 * fRadius;
fBall = new Ellipse2D.Double(fXPixel-fRadius,
fYPixel-fRadius,
fDiam,fDiam);
setBackground(Color.WHITE);
fDropDone=false;
} // reset
/** Draw the ball at its current position. **/
public void paintComponent (Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
// Antialiasing for smooth surfaces.
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Determine position after this time
increement
calcPosition ();
// Move the ball.
fBall.setFrame(fXPixel-fRadius,
fYPixel-fRadius,
fDiam,fDiam);
// Want a solid red ball
g2.setColor (Color.RED);
g2.fill(fBall);
// Now draw the ball.
g2.draw (fBall);
} // paintComponent
/** Calculate the ball position in the next frame.
**/
void calcPosition () {
// Increment by 10 millseconds per
frame
double dt = 0.025;
// Calculate position and velocity
at each step
fY = fY + fVy * dt - 490.* dt * dt;
fVy = fVy - 980.0
* dt;
// Convert to the pixel coordinates
fYPixel = fFrameHt - (int)(fY * fYConvert);
// Reverse direction when ball hits
bottom.
if ((fYPixel + fRadius) >= (fFrameHt-1))
{
fVy = Math.abs
(fVy);
// Subtract
friction loss
fVy -= 0.1*fVy;
// Stop when
speed at bottom drops below arbitrary limit
if (fVy <
15.0) {
fDropDone=true;
}
}
} // clacPosition
/** Provide a flag on drop status. **/
public boolean isDone () {
return fDropDone;
}
} // class DropPanel |
Latest update: Nov. 5, 2004
|