?? ch6.htm
字號:
// Get a random position<BR>
pos.x = Math.abs(rand.nextInt() %<BR>
background.getSize().width);
<BR>
pos.y = Math.abs(rand.nextInt() %<BR>
background.getSize().height);
<BR>
<BR>
// Iterate through sprites, checking
if position is empty<BR>
boolean collision = false;<BR>
for (int i = 0; i < size(); i++) {
<BR>
Rectangle testPos = ((Sprite)elementAt(i)).getPosition();
<BR>
if (pos.intersects(testPos))
{<BR>
collision = true;
<BR>
break;<BR>
}<BR>
}<BR>
empty = !collision;<BR>
}<BR>
return new Point(pos.x, pos.y);</FONT></TT>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">}</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
<TT><FONT FACE="Courier">getEmptyPosition</FONT></TT> is a method
whose importance might not be readily apparent to you right now;
it is used to find an empty physical position in which to place
a new sprite in the sprite list. This doesn't mean the position
of the sprite in the list; rather, it means its physical position
on the screen. This method is very useful when you want to randomly
place multiple sprites on the screen. By using <TT><FONT FACE="Courier">getEmptyPosition</FONT></TT>,
you eliminate the possibility of placing new sprites on top of
existing sprites. For example, in an adventure game you could
randomly place scenery objects such as trees using <TT><FONT FACE="Courier">getEmptyPosition</FONT></TT>
to make sure none of them overlap each other.
<P>
The <TT><FONT FACE="Courier">isPointInside</FONT></TT> method
in <TT><FONT FACE="Courier">SpriteVector</FONT></TT> is similar
to the version of <TT><FONT FACE="Courier">isPointInside</FONT></TT>
in <TT><FONT FACE="Courier">Sprite</FONT></TT>, except it goes
through the entire sprite list checking each sprite. Check out
the source code for it:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">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.isPointInside(pt))<BR>
return s;<BR>
}<BR>
return null;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
If the point passed in the parameter <TT><FONT FACE="Courier">pt</FONT></TT>
lies in a sprite, <TT><FONT FACE="Courier">isPointInside</FONT></TT>
returns the sprite. Notice that the sprite list is searched in
reverse, meaning that the last sprite is checked before the first.
The sprites are searched in this order for a very important reason:
Z-order. The sprites are stored in the sprite list sorted in ascending
Z-order, which specifies their depth on the screen. Therefore,
sprites near the beginning of the list are sometimes concealed
by sprites near the end of the list. If you want to check for
a point lying within a sprite, it only makes sense to check the
topmost sprites first-that is, the sprites with larger Z-order
values. If this sounds a little confusing, don't worry; you'll
learn more about Z-order later today when you get to the <TT><FONT FACE="Courier">add</FONT></TT>
method.
<P>
As in <TT><FONT FACE="Courier">Sprite</FONT></TT>, the <TT><FONT FACE="Courier">update</FONT></TT>
method is the key method in <TT><FONT FACE="Courier">SpriteVector</FONT></TT>
because it handles updating all the sprites. Listing 6.3 contains
the source code for <TT><FONT FACE="Courier">update</FONT></TT>.
<HR>
<BLOCKQUOTE>
<B>Listing 6.3. The </B><TT><B><FONT FACE="Courier">SpriteVector</FONT></B></TT><B>
class's </B><TT><B><FONT FACE="Courier">update</FONT></B></TT><B>
method.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public void update() {<BR>
// Iterate through sprites, updating each<BR>
Sprite s, sHit;<BR>
Rectangle lastPos;<BR>
for (int i = 0; i < size(); ) {<BR>
// Update the sprite<BR>
s = (Sprite)elementAt(i);<BR>
lastPos = new Rectangle(s.getPosition().x,
s.getPosition().y,<BR>
s.getPosition().width, s.getPosition().height);
<BR>
BitSet action = s.update();<BR>
<BR>
// Check for the SA_ADDSPRITE action<BR>
if (action.get(Sprite.SA_ADDSPRITE)) {
<BR>
// Add the sprite<BR>
Sprite sToAdd = s.addSprite(action);
<BR>
if (sToAdd != null) {<BR>
int iAdd = add(sToAdd);
<BR>
if (iAdd >=
0 && iAdd <= i)<BR>
i++;
<BR>
}<BR>
}<BR>
<BR>
// Check for the SA_RESTOREPOS action
<BR>
if (action.get(Sprite.SA_RESTOREPOS))
<BR>
s.setPosition(lastPos);<BR>
<BR>
// Check for the SA_KILL action<BR>
if (action.get(Sprite.SA_KILL)) {<BR>
removeElementAt(i);<BR>
continue;<BR>
}<BR>
<BR>
// Test for collision<BR>
int iHit = testCollision(s);<BR>
if (iHit >= 0)<BR>
if (collision(i, iHit))<BR>
s.setPosition(lastPos);
<BR>
i++;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">}</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
The <TT><FONT FACE="Courier">update</FONT></TT> method iterates
through the sprites, calling <TT><FONT FACE="Courier">Sprite</FONT></TT>'s
<TT><FONT FACE="Courier">update</FONT></TT> method on each one.
It then checks for the various sprite action flags returned by
the call to <TT><FONT FACE="Courier">update</FONT></TT>. If the
<TT><FONT FACE="Courier">SA_ADDSPRITE</FONT></TT> flag is set,
the <TT><FONT FACE="Courier">addSprite</FONT></TT> method is called
on the sprite and the returned sprite is added to the list. If
the <TT><FONT FACE="Courier">SA_RESTOREPOS</FONT></TT> flag is
set, the sprite position is set to the position of the sprite
prior to being updated. If the <TT><FONT FACE="Courier">SA_KILL</FONT></TT>
flag is set, the sprite is removed from the sprite list. Finally,
<TT><FONT FACE="Courier">testCollision</FONT></TT> is called to
see whether a collision has occurred between sprites. You get
the whole scoop on <TT><FONT FACE="Courier">testCollision</FONT></TT>
in this section. If a collision has occurred, the old position
of the collided sprite is restored and the <TT><FONT FACE="Courier">collision</FONT></TT>
method is called.
<P>
The <TT><FONT FACE="Courier">collision</FONT></TT> method is used
to handle collisions between two sprites:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">protected boolean collision(int i, int
iHit) {<BR>
// Swap velocities (bounce)<BR>
Sprite s = (Sprite)elementAt(i);<BR>
Sprite sHit = (Sprite)elementAt(iHit);<BR>
Point swap = s.getVelocity();<BR>
s.setVelocity(sHit.getVelocity());<BR>
sHit.setVelocity(swap);<BR>
return true;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
The <TT><FONT FACE="Courier">collision</FONT></TT> method is responsible
for handling any actions that result from a collision between
sprites. The action in this case is to simply swap the velocities
of the collided <TT><FONT FACE="Courier">Sprite</FONT></TT> objects,
which results in a bouncing effect. This method is where you provide
specific collision actions in derived sprites. For example, in
a space game, you might want alien sprites to explode upon collision
with a meteor sprite.
<P>
The <TT><FONT FACE="Courier">testCollision</FONT></TT> method
is used to test for collisions between a sprite and the rest of
the sprites in the sprite list:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">protected int testCollision(Sprite test)
{<BR>
// Check for collision with other sprites<BR>
Sprite s;<BR>
for (int i = 0; i < size(); i++)<BR>
{<BR>
s = (Sprite)elementAt(i);<BR>
if (s == test) // don't check
itself<BR>
continue;<BR>
if (test.testCollision(s))<BR>
return i;<BR>
}<BR>
return -1;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
The sprite to be tested is passed in the <TT><FONT FACE="Courier">test</FONT></TT>
parameter. The sprites are then iterated through and the <TT><FONT FACE="Courier">testCollision</FONT></TT>
method in <TT><FONT FACE="Courier">Sprite</FONT></TT> is called
for each. Notice that <TT><FONT FACE="Courier">testCollision</FONT></TT>
isn't called on the test sprite if the iteration refers to the
same sprite. To understand the significance of this code, consider
the effect of passing <TT><FONT FACE="Courier">testCollision</FONT></TT>
the same sprite on which the method is being called; you would
be checking to see whether a sprite was colliding with itself,
which would always return true. If a collision is detected, the
<TT><FONT FACE="Courier">Sprite</FONT></TT> object that has been
hit is returned from <TT><FONT FACE="Courier">testCollision</FONT></TT>.
<P>
The <TT><FONT FACE="Courier">draw</FONT></TT> method handles drawing
the background, as well as drawing all the sprites:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public void draw(Graphics g) {<BR>
// Draw the background<BR>
background.draw(g);<BR>
<BR>
// Iterate through sprites, drawing each<BR>
for (int i = 0; i < size(); i++)<BR>
((Sprite)elementAt(i)).draw(g);<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
The background is drawn with a simple call to the <TT><FONT FACE="Courier">draw</FONT></TT>
method of the <TT><FONT FACE="Courier">Background</FONT></TT>
object. The sprites are then drawn by iterating through the sprite
list and calling the <TT><FONT FACE="Courier">draw</FONT></TT>
method for each.
<P>
The <TT><FONT FACE="Courier">add</FONT></TT> method is probably
the trickiest method in the <TT><FONT FACE="Courier">SpriteVector</FONT></TT>
class. Listing 6.4 contains the source code for <TT><FONT FACE="Courier">add</FONT></TT>.
<HR>
<BLOCKQUOTE>
<B>Listing 6.4. The </B><TT><B><FONT FACE="Courier">SpriteVector</FONT></B></TT><B>
class's </B><TT><B><FONT FACE="Courier">add</FONT></B></TT><B>
method.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public int add(Sprite s) {<BR>
// Use a binary search to find the right location
to insert the<BR>
// new sprite (based on z-order)<BR>
int l = 0, r = size(), i = 0;<BR>
int z = s.getZOrder(),<BR>
zTest = z + 1;
<BR>
while (r > l) {<BR>
i = (l + r) / 2;<BR>
zTest = ((Sprite)elementAt(i)).
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -