📄 ch6.htm
字号:
vel.y = -vel.y;<BR>
}<BR>
if (bounce)<BR>
setVelocity(vel);<BR>
}<BR>
// Die?<BR>
else if (boundsAction == Sprite.BA_DIE) {<BR>
if ((pos.x + position.width) < bounds.x
||<BR>
pos.x > bounds.width ||
<BR>
(pos.y + position.height)
< bounds.y ||<BR>
pos.y > bounds.height)
{<BR>
action.set(Sprite.SA_KILL);
<BR>
return action;<BR>
}<BR>
}<BR>
// Stop (default)<BR>
else {<BR>
if (pos.x < bounds.x ||
<BR>
pos.x > (bounds.x + bounds.width
- position.width)) {<BR>
pos.x = Math.max(bounds.x,
Math.min(pos.x,<BR>
bounds.x + bounds.width
- position.width));<BR>
setVelocity(new Point(0, 0));
<BR>
}<BR>
if (pos.y < bounds.y ||
<BR>
pos.y > (bounds.y + bounds.height
- position.height)) {<BR>
pos.y = Math.max(bounds.y,
Math.min(pos.y,<BR>
bounds.y + bounds.height
- position.height));<BR>
setVelocity(new Point(0, 0));
<BR>
}<BR>
}<BR>
setPosition(pos);<BR>
<BR>
return action;</FONT></TT>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">}</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
The <TT><FONT FACE="Courier">update</FONT></TT> method handles
the task of updating the animation frame and position of the sprite.
<TT><FONT FACE="Courier">update</FONT></TT> begins by creating
an empty set of action flags, which are stored in a <TT><FONT FACE="Courier">BitSet</FONT></TT>
object. The animation frame is then updated with a call to <TT><FONT FACE="Courier">incFrame</FONT></TT>.
The position of the sprite is updated by translating the position
rectangle based on the velocity. You can think of the position
rectangle as sliding a distance determined by the velocity.
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Note</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
The <TT><FONT FACE="Courier">BitSet</FONT></TT> class is included in the standard Java package <TT><FONT FACE="Courier">java.util</FONT></TT> and provides a means of maintaining a set of boolean flags or bit fields.
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<P>
The rest of the code in <TT><FONT FACE="Courier">update</FONT></TT>
is devoted to handling the various bounds actions. The first bounds
action flag, <TT><FONT FACE="Courier">BA_WRAP</FONT></TT>, causes
the sprite to wrap around to the other side of the bounds rectangle.
This flag is useful in an Asteroids type game, in which the asteroids
float off one side of the screen and back from the other. The
<TT><FONT FACE="Courier">BA_BOUncE</FONT></TT> flag causes the
sprite to bounce if it encounters a boundary. This flag is useful
in a Breakout or Pong type game, in which a ball bounces off the
edges of the screen. The <TT><FONT FACE="Courier">BA_DIE</FONT></TT>
flag causes the sprite to die if it encounters a boundary. This
flag is useful for sprites such as bullets, which you often want
destroyed when they travel beyond the edges of the screen. Finally,
the default flag, <TT><FONT FACE="Courier">BA_STOP</FONT></TT>,
causes the sprite to stop when it encounters a boundary.
<P>
Notice that <TT><FONT FACE="Courier">update</FONT></TT> finishes
by returning a set of sprite action flags, <TT><FONT FACE="Courier">action</FONT></TT>.
Derived sprite classes can return different sprite action values
to trigger different actions. Judging by its size, it's not hard
to figure out that the <TT><FONT FACE="Courier">update</FONT></TT>
method is itself the bulk of the code in the <TT><FONT FACE="Courier">Sprite</FONT></TT>
class. This is logical though, because the <TT><FONT FACE="Courier">update</FONT></TT>
method is where all the action takes place; <TT><FONT FACE="Courier">update</FONT></TT>
handles all the details of updating the animation frame and position
of the sprite, along with carrying out different bounds actions.
<P>
Another important method in the <TT><FONT FACE="Courier">Sprite</FONT></TT>
class is <TT><FONT FACE="Courier">draw</FONT></TT>, whose source
code is as follows:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public void draw(Graphics g) {<BR>
// Draw the current frame<BR>
if (!hidden)<BR>
g.drawImage(image[frame], position.x,
position.y, component);<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
After wading through the <TT><FONT FACE="Courier">update</FONT></TT>
method, the <TT><FONT FACE="Courier">draw</FONT></TT> method looks
like a piece of cake! It simply uses the <TT><FONT FACE="Courier">drawImage</FONT></TT>
method to draw the current sprite frame image to the <TT><FONT FACE="Courier">Graphics</FONT></TT>
object that is passed in. Notice that the <TT><FONT FACE="Courier">drawImage</FONT></TT>
method requires the image, XY position, and component (<TT><FONT FACE="Courier">ImageObserver</FONT></TT>)
to carry this out.
<P>
The <TT><FONT FACE="Courier">addSprite</FONT></TT> method is used
to add sprites to the sprite list:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">protected Sprite addSprite(BitSet action)
{<BR>
return null;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
The sprite list contains all the sprites and is maintained by
the <TT><FONT FACE="Courier">SpriteVector</FONT></TT> class, which
you'll learn about a little later today. The reason for having
the <TT><FONT FACE="Courier">addSprite</FONT></TT> method is that
a sprite occasionally needs to create and add another sprite to
the sprite list. However, there is a big problem in that an individual
sprite doesn't know anything about the sprite list. To get around
this problem, you use sprite actions. Sprite actions work like
this: A sprite notifies the sprite list that it wants to add a
sprite by setting the <TT><FONT FACE="Courier">SA_ADDSPRITE</FONT></TT>
action flag in the set of action flags returned by the <TT><FONT FACE="Courier">update</FONT></TT>
method. The sprite list, in turn, calls the <TT><FONT FACE="Courier">addSprite</FONT></TT>
method for the sprite and adds the new sprite to the list. I know
this sounds like a convoluted way to handle sprite creation, but
it actually works quite well and fits in with the object-oriented
design of the sprite classes. The remaining question, then, is
why does this implementation of <TT><FONT FACE="Courier">addSprite</FONT></TT>
return <TT><FONT FACE="Courier">null</FONT></TT>? The answer is
that it is up to derived sprites to provide a specific implementation
for <TT><FONT FACE="Courier">addSprite</FONT></TT>. Knowing this,
you could make <TT><FONT FACE="Courier">addSprite</FONT></TT>
abstract, but then you would be forced to derive a new sprite
class any time you want to create a sprite.
<P>
The last method in <TT><FONT FACE="Courier">Sprite</FONT></TT>
is <TT><FONT FACE="Courier">testCollision</FONT></TT>, which is
used to check for collisions between sprites:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">protected boolean testCollision(Sprite
test) {<BR>
// Check for collision with another sprite<BR>
if (test != this)<BR>
return collision.intersects(test.getCollision());
<BR>
return false;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
The sprite to test for collision is passed in the <TT><FONT FACE="Courier">test</FONT></TT>
parameter. The test simply involves checking to see whether the
collision rectangles intersect. If so, <TT><FONT FACE="Courier">testCollision</FONT></TT>
returns <TT><FONT FACE="Courier">true</FONT></TT>. <TT><FONT FACE="Courier">testCollision</FONT></TT>
isn't all that useful within the context of a single sprite, but
it is very handy when you put together the <TT><FONT FACE="Courier">SpriteVector</FONT></TT>
class, which you are going to do next.
<H3><A NAME="TheSpriteVectorClass"><B>The </B><TT><B><FONT SIZE=4 FACE="Courier">SpriteVector</FONT></B></TT><B><FONT SIZE=4>
Class</FONT></B></A></H3>
<P>
At this point, you have a <TT><FONT FACE="Courier">Sprite</FONT></TT>
class with some pretty impressive features, but you don't really
have any way to manage it. Of course, you could go ahead and create
an applet with some <TT><FONT FACE="Courier">Sprite</FONT></TT>
objects, but how would they be able to interact with each other?
The answer to this question is the <TT><FONT FACE="Courier">SpriteVector</FONT></TT>
class, which handles all the details of maintaining a list of
sprites and the handling of interactions between them.
<P>
The <TT><FONT FACE="Courier">SpriteVector</FONT></TT> class is
derived from the <TT><FONT FACE="Courier">Vector</FONT></TT> class,
which is a standard class provided in the <TT><FONT FACE="Courier">java.util</FONT></TT>
package. The <TT><FONT FACE="Courier">Vector</FONT></TT> class
models a growable array of objects. In this case, the <TT><FONT FACE="Courier">SpriteVector</FONT></TT>
class is used as a container for a growable array of <TT><FONT FACE="Courier">Sprite</FONT></TT>
objects.
<P>
The <TT><FONT FACE="Courier">SpriteVector</FONT></TT> class has
only one member variable, <TT><FONT FACE="Courier">background</FONT></TT>,
which is a <TT><FONT FACE="Courier">Background</FONT></TT> object:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">protected Background background;</FONT></TT>
</BLOCKQUOTE>
<P>
This <TT><FONT FACE="Courier">Background</FONT></TT> object represents
the background upon which the sprites appear. It is initialized
in the constructor for <TT><FONT FACE="Courier">SpriteVector</FONT></TT>,
like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public SpriteVector(Background back)
{<BR>
super(50, 10);<BR>
background = back;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
The constructor for <TT><FONT FACE="Courier">SpriteVector</FONT></TT>
simply takes a <TT><FONT FACE="Courier">Background</FONT></TT>
object as its only parameter. You'll learn about the <TT><FONT FACE="Courier">Background</FONT></TT>
class a little later today. Notice that the constructor for <TT><FONT FACE="Courier">SpriteVector</FONT></TT>
calls the <TT><FONT FACE="Courier">Vector</FONT></TT> parent class
constructor and sets the default storage capacity (<TT><FONT FACE="Courier">50</FONT></TT>)
and amount to increment the storage capacity (<TT><FONT FACE="Courier">10</FONT></TT>)
if the vector needs to grow.
<P>
<TT><FONT FACE="Courier">SpriteVector</FONT></TT> contains the
following two access methods for getting and setting the <TT><FONT FACE="Courier">background</FONT></TT>
member variable:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public Background getBackground() {<BR>
return background;<BR>
}<BR>
<BR>
public void setBackground(Background back) {<BR>
background = back;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
These methods are useful in games in which the background changes
based on the level of the game. To change the background, you
simply call <TT><FONT FACE="Courier">setBackground</FONT></TT>
and pass in the new <TT><FONT FACE="Courier">Background</FONT></TT>
object.
<P>
The <TT><FONT FACE="Courier">getEmptyPosition</FONT></TT> method
is used by the <TT><FONT FACE="Courier">SpriteVector</FONT></TT>
class to help position new sprites. Listing 6.2 contains the source
code for <TT><FONT FACE="Courier">getEmptyPosition</FONT></TT>.
<HR>
<BLOCKQUOTE>
<B>Listing 6.2. The </B><TT><B><FONT FACE="Courier">SpriteVector</FONT></B></TT><B>
class's </B><TT><B><FONT FACE="Courier">getEmptyPosition</FONT></B></TT><B>
method.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public Point getEmptyPosition(Dimension
sSize) {<BR>
Rectangle pos = new Rectangle(0, 0, sSize.width, sSize.height);
<BR>
Random rand = new Random(System.currentTimeMillis());
<BR>
boolean empty = false;<BR>
int numTries
= 0;<BR>
<BR>
// Look for an empty position<BR>
while (!empty && numTries++ < 50) {<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -