📄 ch13.htm
字号:
+ i + j + ".gif");<BR>
tracker.addImage(image[i][j],
id);<BR>
}<BR>
}<BR>
<BR>
protected void setCollision() {<BR>
collision = new Rectangle(position.x +
3, position.y + 3,<BR>
position.width - 6, position.height
- 6);<BR>
}<BR>
<BR>
public BitSet update() {<BR>
// See if the scorpion escaped<BR>
BitSet action = super.update();<BR>
if (action.get(Sprite.SA_KILL))<BR>
ScorpionRoundup.lost++;<BR>
return action;<BR>
}<BR>
}</TT>
</BLOCKQUOTE>
<HR>
<P>
The <TT>Scorpion</TT> class uses a
two-dimensional array of images to show the animations of the
scorpion kicking its legs and wagging its tail in each direction.
Figure 13.3 shows the images used by the <TT>Scorpion</TT>
class.
<P>
<A HREF="f13-3.gif" ><B>Figure 13.3 : </B><I>The images used by the Scorpion class.</I></A>
<P>
The constructor for <TT>Scorpion</TT>
takes parameters specifying the direction and speed increment
for the scorpion, <TT>dir</TT> and
<TT>speedInc</TT>. The <TT>dir</TT>
parameter determines in which direction the scorpion travels,
as well as which side of the screen it starts from, and the parameter
can be set to either <TT>0</TT> (left)
or <TT>1</TT> (right). The <TT>speedInc</TT>
parameter specifies how much to increase the scorpion's speed
beyond its default speed. This parameter is how new scorpions
become faster as the difficulty level of the game increases.
<P>
The <TT>update</TT> method in <TT>Scorpion</TT>
is overridden to track when the scorpion makes it across the screen.
This works rather indirectly, so bear with me. Notice in the constructor
for <TT>Scorpion</TT> that the bounds
action is set to <TT>BA_DIE</TT>.
If you recall, the bounds actions determine what a sprite does
when it reaches a boundary (the other side of the applet window,
in this case). The <TT>BA_DIE</TT>
bounds action causes the <TT>SA_KILL</TT>
flag to be returned by the default sprite <TT>update</TT>
method, eventually resulting in the sprite being removed from
the sprite list. By looking for this flag in <TT>Scorpion</TT>'s
overridden <TT>update</TT> method,
you can tell when the scorpion makes it across the screen unscathed.
Pretty tricky, huh?
<P>
If the scorpion has made it across safely, the <TT>ScorpionRoundup.lost</TT>
variable is incremented. This variable is a public static member
of the <TT>ScorpionRoundup</TT> applet
class that can be accessed by other classes, such as <TT>Scorpion</TT>.
You'll learn more about it later in this lesson when you get into
the <TT>ScorpionRoundup</TT> class.
<P>
Scorpion Roundup uses a derived version of the <TT>SpriteVector</TT>
class called <TT>SRVector</TT>. Listing
13.2 contains the source code for the <TT>SRVector</TT>
class.
<HR>
<BLOCKQUOTE>
<B>Listing 13.2. The </B><TT><B><FONT FACE="Courier">SRVector</FONT></B></TT><B>
class.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT>public class SRVector extends SpriteVector
{<BR>
public SRVector(Background back) {<BR>
super(back);<BR>
}<BR>
<BR>
Sprite isPointInside(Point pt) {<BR>
// Iterate backward through the sprites,
testing each<BR>
for (int i = (size() - 1); i >= 0;
i--) {<BR>
Sprite s = (Sprite)elementAt(i);
<BR>
if ((s.getClass().getName().equals("Scorpion"))
&&<BR>
s.isPointInside(pt))
<BR>
return s;<BR>
}<BR>
return null;<BR>
}<BR>
<BR>
protected boolean collision(int i, int iHit) {<BR>
// Do nothing!<BR>
return false;<BR>
}<BR>
}</TT>
</BLOCKQUOTE>
<HR>
<P>
The <TT>SRVector</TT> class overrides
two methods in <TT>SpriteVector</TT>:
<TT>isPointInside</TT> and <TT>collision</TT>.
The overridden <TT>isPointInside</TT>
method is necessary to distinguish between the user clicking a
scorpion sprite and clicking the net sprite. Without overriding
this method, you would never be able to detect when a scorpion
is clicked, because the net sprite would always be in the way.
This is a result of the fact that the net sprite follows the mouse
around and has a higher Z-order than the scorpions (so it can
always be seen). The simple solution is to look only for sprites
of type <TT>Scorpion</TT> in the <TT>isPointInside</TT>
method.
<P>
Because the scorpions don't need to be able to collide with each
other or the net sprite, it makes sense to do nothing when a collision
occurs. This is carried out by simply returning <TT>false</TT>
from the <TT>collision</TT> method.
<P>
You've now seen the sprite classes used by <TT>ScorpionRoundup</TT>.
It's time to check out the applet class.
<H3><A NAME="TheScorpionRoundupClass"><B>The </B><TT><B><FONT SIZE=4 FACE="Courier">ScorpionRoundup</FONT></B></TT><B><FONT SIZE=4>
Class</FONT></B></A></H3>
<P>
The <TT>ScorpionRoundup</TT> class
takes care of all the high-level animation and sound issues, as
well as handling user input. First take a look at the member variables
defined in the <TT>ScorpionRoundup</TT>
class:
<BLOCKQUOTE>
<TT>private Image offImage,
back, netImage;<BR>
private AudioClip music, netHit,
netMiss, applause;<BR>
private Graphics offGrfx;<BR>
private Thread animate;
<BR>
private MediaTracker tracker;<BR>
private SRVector srv;<BR>
private Sprite net;
<BR>
private int delay
= 83; // 12 fps<BR>
private Font infoFont
= new Font("Helvetica",<BR>
Font.PLAIN,
14);<BR>
private FontMetrics infoMetrics;<BR>
private Random rand
= new<BR>
Random(System.currentTimeMillis());
<BR>
private boolean musicOn
= true;<BR>
private static int level, caught;<BR>
public static int lost;</TT>
</BLOCKQUOTE>
<P>
You might be curious about a few of these member variables. The
four <TT>AudioClip</TT> member variables
hold audio clips for the music and sound effects used in the game.
The <TT>musicOn</TT> member variable
is a boolean variable that determines whether the music is on
or off. The <TT>level</TT>, <TT>caught</TT>,
and <TT>lost</TT> member variables
are used to store the state of the game: <TT>level</TT>
is the current difficulty level, <TT>caught</TT>
is how many scorpions have been caught, and <TT>lost</TT>
is how many scorpions have escaped.
<P>
The <TT>init</TT> method in <TT>ScorpionRoundup</TT>
is pretty straightforward-it loads and initializes all the images
and sounds used by the game:
<BLOCKQUOTE>
<TT>public void init() {<BR>
// Load and track the images<BR>
tracker = new MediaTracker(this);<BR>
back = getImage(getCodeBase(), "Res/Back.gif");
<BR>
tracker.addImage(back, 0);<BR>
netImage = getImage(getCodeBase(), "Res/Net.gif");
<BR>
tracker.addImage(netImage, 0);<BR>
Scorpion.initResources(this, tracker, 0);<BR>
<BR>
// Load the audio clips<BR>
music = getAudioClip(getCodeBase(), "Res/Music.au");
<BR>
netHit = getAudioClip(getCodeBase(), "Res/NetHit.au");
<BR>
netMiss = getAudioClip(getCodeBase(), "Res/NetMiss.au");
<BR>
applause = getAudioClip(getCodeBase(), "Res/Applause.au");
<BR>
}</TT>
</BLOCKQUOTE>
<P>
The <TT>stop</TT> method has been
pretty standard in all the applets you've seen thus far. However,
in <TT>ScorpionRoundup</TT> it has
an extra line of code that stops looping the music audio clip:
<BLOCKQUOTE>
<TT>public void stop() {<BR>
if (animate != null) {<BR>
animate.stop();<BR>
animate = null;<BR>
}<BR>
music.stop();<BR>
}</TT>
</BLOCKQUOTE>
<P>
The extra line of code, <TT>music.stop()</TT>,
is important because it ensures that the music is stopped when
the thread is stopped. Without this simple method call, the music
would continue to play even after a user has left the Web page
containing the game.
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Warning</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Be sure to always stop all looped sounds when the applet thread is stopped. You do this simply by calling the <TT>stop</TT> method on the <TT>AudioClip</TT> object from within the applet's <TT>stop</TT> method, as you just saw in <TT>ScorpionRoundup</TT>.
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<P>
The <TT>run</TT> method in <TT>ScorpionRoundup</TT>
calls the <TT>newGame</TT> method,
which you'll learn about in a moment. Listing 13.3 contains the
source code for the <TT>run</TT> method.
<HR>
<BLOCKQUOTE>
<B>Listing 13.3. The </B><TT><B><FONT FACE="Courier">ScorpionRoundup</FONT></B></TT><B>
class's </B><TT><B><FONT FACE="Courier">run</FONT></B></TT><B>
method.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT>public void run() {<BR>
try {<BR>
tracker.waitForID(0);<BR>
}<BR>
catch (InterruptedException e) {<BR>
return;<BR>
}<BR>
<BR>
// Set up a new game<BR>
newGame();<BR>
<BR>
// Update everything<BR>
long t = System.currentTimeMillis();<BR>
while (Thread.currentThread() == animate) {<BR>
srv.update();<BR>
repaint();<BR>
try {<BR>
t += delay;<BR>
Thread.sleep(Math.max(0, t
- System.currentTimeMillis()));<BR>
}<BR>
catch (InterruptedException e) {<BR>
break;<BR>
}<BR>
}<BR>
}</TT>
</BLOCKQUOTE>
<HR>
<P>
After setting up a new game, the <TT>run</TT>
method enters the main update loop where it updates the sprite
list and forces a repaint. Speaking of updating, the <TT>update</TT>
method does a few new things in <TT>ScorpionRoundup</TT>;
check out Listing 13.4.
<HR>
<BLOCKQUOTE>
<B>Listing 13.4. The </B><TT><B><FONT FACE="Courier">ScorpionRoundup</FONT></B></TT><B>
class's </B><TT><B><FONT FACE="Courier">update</FONT></B></TT><B>
method.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT>public void update(Graphics g) {<BR>
// Create the offscreen graphics context<BR>
if (offGrfx == null) {<BR>
offImage = createImage(size().width, size().height);
<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -