📄 ch7.htm
字号:
</BLOCKQUOTE>
<P>
In <TT><FONT FACE="Courier">setVelocity</FONT></TT>, <TT><FONT FACE="Courier">velocity</FONT></TT>
is first assigned its new value. The direction is then altered
based on the new velocity by way of a few comparisons and equations.
If the function of these equations isn't obvious to you at first,
just think about what task they are handling. The task is to obtain
a direction in the range <TT><FONT FACE="Courier">0</FONT></TT>
to <TT><FONT FACE="Courier">7</FONT></TT> from a given velocity.
Because no single equation can do this, I worked out a fairly
concise way of calculating the direction based on the velocity.
There's nothing magical about it; it's just a matter of closely
analyzing the different values.
<P>
You now have a fully functional directional sprite class that
can be reused in any applet from now on. Speaking of reusing the
<TT><FONT FACE="Courier">DirectionalSprite</FONT></TT> class,
let's start working on the tarantula simulator.
<H2><A NAME="DesigningSimTarantula"><B><FONT SIZE=5 COLOR=#FF0000>Designing
Sim Tarantula</FONT></B></A></H2>
<P>
Before jumping into the Java code of any applet, it's important
to decide to some degree what you want the applet to do. This
is very important because it gives you a clear goal and a strategy
toward implementing the various classes that make up a complete
applet. By using this approach, you save a lot of time rewriting
code, and you end up with a cleaner set of classes the first time
around.
<P>
Writing Java games requires a similar design approach. In the
design phase of a Java game, you must determine what sprites the
game needs, as well as how they interact with each other. The
only potential difference in designing a game is that there are
aspects of games that have to be played and then tweaked based
on feel. This trial and error approach is hard to avoid in some
cases because the subtleties of games are often the most fun.
<P>
Having said all that, let's take a stab at designing a simple
tarantula simulator, Sim Tarantula. Although it's not technically
a game, Sim Tarantula contains nearly all of the components of
a game-just about everything except user interaction.
<P>
First, what objects does a tarantula simulator contain? By defining
the objects used in the applet, you are indirectly defining the
applet itself. Well, no doubt it will have tarantulas, and preferably
some kind of background. Because tarantulas often live in the
desert, it only makes sense to use a desert background. Additionally,
tarantulas clearly have a front and a back, so it makes sense
to model them as directional sprites. This is important because
you want a tarantula to always face the direction in which it
is walking; otherwise, it will look like the tarantula is sliding
across the desert floor rather than walking.
<P>
Next, you might wonder where tarantulas come from. Of course,
eggs! Rather than create fully grown tarantulas, it would be much
more interesting to have them hatch from eggs and grow into larger
tarantulas. This is a perfect place to display a frame animation
of a tarantula hatching from an egg and growing up into an adult
tarantula.
<P>
Then what? At this point, the full-grown tarantulas can walk around,
explore things, and talk amongst themselves if they like. They
can even lay more eggs, which eventually results in more tarantulas.
But sooner or later, they start getting old. And like all creatures,
at some point they must die. Sounds like another cool place for
an animation. A frame animation showing a tarantula getting more
and more frail until it just withers away should do the trick.
<P>
You now have enough information to make a pretty neat little tarantula
simulator. At this point, it makes sense to break down the design
into sprites so that you'll have an idea of what classes need
to be written. Based on what you have so far, Sim Tarantula requires
the following sprites:
<UL>
<LI>Spiderling
<LI>Tarantula
<LI>Spidercide
</UL>
<P>
A spiderling is a baby tarantula, and the spiderling sprite basically
models the birth of a tarantula from the egg. This sprite is really
just a frame-animated sprite that is used to show the birth of
a tarantula. The tarantula sprite models a fully grown tarantula.
It is a frame-animated directional sprite and can walk around
freely and create new spiderlings. The spidercide sprite models
the death of a tarantula. It is a frame-animated sprite that shows
a tarantula growing weaker and weaker until it finally disappears.
<P>
Overall, these sprites probably sound pretty reasonable to you.
However, you haven't addressed one thing, and that is the issue
of how the sprites are created and destroyed. The only sprite
out of these three that the applet ever needs to create directly
is the spiderling sprite. This is because the other two sprites
should be created automatically. For example, the tarantula sprite
should be created automatically when the spiderling finishes its
animation. Similarly, the spidercide sprite should be created
automatically when the tarantula is ready to die.
<P>
That covers creating the sprites, but how about destroying them?
In this case, the applet isn't responsible for destroying any
of the sprites. The spiderling sprite should kill itself whenever
it finishes its animation and creates the tarantula sprite. Similarly,
the tarantula sprite should kill itself when it creates the spidercide
sprite. And last but not least, the spidercide sprite should kill
itself when it finishes its animation. Just in case you've had
trouble following any of this, check out Figure 7.3, which shows
the life cycle of the Sim Tarantula sprites.
<P>
<A HREF="f7-3.gif" ><B>Figure 7.3 : </B><I>The life cycle of the sprites in Sim Tarantula.</I></A>
<P>
Now you understand when each sprite is created and destroyed,
but how in the world do sprites create and destroy each other
in the first place? The answer is <I>sprite actions</I>. When
you use sprite actions, you have full control over creating and
destroying sprites from within a sprite. You even have the option
of creating custom actions for derived sprites that can make them
do anything you want. Sprite actions are probably the most powerful
aspect of using sprites, and you've already written support for
them into the sprite classes in the last lesson.
<P>
A <I>sprite action</I> is a mechanism that allows sprites to interact
with each other. For example, using sprite actions, a sprite can
create new sprites or kill existing sprites.
<P>
By now, you'll probably agree that the design of Sim Tarantula
is far enough along to move on to the applet. I couldn't agree
more!
<H2><A NAME="SampleAppletSimTarantula"><B><FONT SIZE=5 COLOR=#FF0000>Sample
Applet: Sim Tarantula</FONT></B></A></H2>
<P>
The Sim Tarantula applet contains most of the elements of a complete
Java game, including extensive support for derived sprites. Figure
7.4 shows the Sim Tarantula applet in action.
<P>
<A HREF="f7-4.gif" ><B>Figure 7.4 : </B><I>The Sim Tarantula sample applet.</I></A>
<P>
Sim Tarantula first places a number of spiderlings that eventually
grow into tarantulas. These tarantulas then roam around dropping
new spiderlings until they die. This process continues until all
of the tarantulas die. Because the creation of spiderlings and
the death of tarantulas occur randomly, the applet has the potential
of running indefinitely. On the other hand, all the tarantulas
could potentially die off, leaving an empty landscape.
<P>
Now that you've seen Sim Tarantula in action, let's look under
the hood and figure out how everything works.
<H3><A NAME="TheSpriteClasses"><B>The Sprite Classes</B></A></H3>
<P>
The core of the Sim Tarantula applet is the extended sprite classes.
The first of these classes is the <TT><FONT FACE="Courier">Spiderling</FONT></TT>
class, which handles displaying an animation of a tarantula hatching
from an egg. The <TT><FONT FACE="Courier">Spiderling</FONT></TT>
class shows an animation and then creates a new tarantula and
kills itself. The <TT><FONT FACE="Courier">Spiderling</FONT></TT>
class has a single constructor, whose code follows:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public Spiderling(Component comp, Point
pos) {<BR>
super(comp, image, 0, 1, 20, pos, new Point(0, 0),
40,<BR>
Sprite.BA_DIE);<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
Notice that, unlike the <TT><FONT FACE="Courier">Sprite</FONT></TT>
class, the constructor for <TT><FONT FACE="Courier">Spiderling</FONT></TT>
only takes a couple of parameters. This simplification is handy
because it enables you to create spiderlings in the applet without
having to supply a bunch of information. This is a common technique
that you will use on game-specific sprites throughout the rest
of the book.
<P>
The <TT><FONT FACE="Courier">initResources</FONT></TT> method
is used to initialize the resources used by the spiderling:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public static void initResources(Applet
app, MediaTracker tracker,<BR>
int id) {<BR>
for (int i = 0; i < 6; i++) {<BR>
image[i] = app.getImage(app.getCodeBase(),
"Res/Spling" +<BR>
i + ".gif");<BR>
tracker.addImage(image[i], id);<BR>
}<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
Resources initialized by <TT><FONT FACE="Courier">initResources</FONT></TT>
could include anything from images to sound and music. In this
case, the only resources used by <TT><FONT FACE="Courier">Spiderling</FONT></TT>
are images. It's important that <TT><FONT FACE="Courier">initResources</FONT></TT>
is defined as static. This means that <TT><FONT FACE="Courier">initResources</FONT></TT>
applies to all instances of the <TT><FONT FACE="Courier">Spiderling</FONT></TT>
class, which results in all <TT><FONT FACE="Courier">Spiderling</FONT></TT>
objects referencing the same images. Furthermore, this makes the
loading of resources smoother because you can load the resources
at the beginning of the applet before you even create any <TT><FONT FACE="Courier">Spiderling</FONT></TT>
objects. This is another tactic that will be used frequently throughout
the rest of the book.
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Warning</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Because the <TT><FONT FACE="Courier">Spiderling</FONT></TT> class is completely dependent on its resources (images), the <TT><FONT FACE="Courier">initResources</FONT></TT> method must be called before creating or using any <TT><FONT
FACE="Courier">Spiderling</FONT></TT> objects. The same rule applies to all other sprites you develop that use the <TT><FONT FACE="Courier">initResources</FONT></TT> method to initialize their resources.
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<P>
The overridden <TT><FONT FACE="Courier">update</FONT></TT> method
in <TT><FONT FACE="Courier">Spiderling</FONT></TT> takes care
of incrementing the spiderling frame animation:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">public BitSet update() {<BR>
BitSet action = new BitSet();<BR>
<BR>
// Die?<BR>
if (frame >= 5) {<BR>
action.set(Sprite.SA_KILL);<BR>
action.set(Sprite.SA_ADDSPRITE);<BR>
action.set(Tarantula.SA_ADDTARANTULA);
<BR>
return action;<BR>
}<BR>
<BR>
// Increment the frame<BR>
incFrame();<BR>
<BR>
return action;<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
An important thing to notice is how <TT><FONT FACE="Courier">update</FONT></TT>
kills the <TT><FONT FACE="Courier">Spiderling</FONT></TT> object
and creates a new <TT><FONT FACE="Courier">Tarantula</FONT></TT>
object. This is carried out simply by checking the current animation
frame and returning the correct sprite action. The <TT><FONT FACE="Courier">update</FONT></TT>
method makes use of a custom sprite action, <TT><FONT FACE="Courier">SA_ADDTARANTULA</FONT></TT>,
which is defined in the <TT><FONT FACE="Courier">Tarantula</FONT></TT>
class. You learn about the <TT><FONT FACE="Courier">Tarantula</FONT></TT>
class in a moment.
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Note</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Notice that even though the <TT><FONT FACE="Courier">SA_ADDTARANTULA</FONT></TT> sprite action is used to add tarantula sprites, the standard sprite action <TT><FONT FACE="Courier">SA_ADDSPRITE</FONT></TT> must also be used in conjunction with it. This is
true because the <TT><FONT FACE="Courier">SA_ADDSPRITE</FONT></TT> action signals that a sprite is to be added, and the <TT><FONT FACE="Courier">SA_ADDTARANTULA</FONT></TT> action specifies the specific type of sprite (a tarantula sprite).
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<P>
The <TT><FONT FACE="Courier">addSprite</FONT></TT> method is the
other overridden method in <TT><FONT FACE="Courier">Spiderling</FONT></TT>
and handles adding a new <TT><FONT FACE="Courier">Tarantula</FONT></TT>
object when the spiderling dies:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">protected Sprite addSprite(BitSet action)
{<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -