And then we’ll update Land to look like this:private void Land { State = CharState.Grounded; SetAnim"land"; } This means that in our character definition file guy.zmx, we need to creat
Trang 1Putting Scripting into Practice
Let’s look at how this scripting language actually works in a situation we could be using for
Zombie Smashers XNA
We’ve mapped out all of the important keyframes in the animation second (for secondary
attack) and connected them with gotos in Figure 6-9 We’ve left out frame numbers, opting to
use some attractive lines instead
Figure 6-9 Gotos in “second”
It’s all one animation, but it’s split into two rows that just happen to coincide with the
regular shoot and shoot up animations
At the start of the animation, we’ll check whether Up was pressed If it was, we’ll jump to
the start of the shoot up segment At the ends of both the regular shoot and shoot up
anima-tions, we’ll check for input to send us to the start of the shooting animations If we don’t get
input, we’ll move to the end of the animation and finally idle
Let’s look at a more complicated example for the animation attack, shown in the same
pseudo-format in Figure 6-10
Now we’re getting into the good stuff! We have got a four-hit combo: spanner-whack,
spanner-whack, spanner-whack, flying kick The player just needs to keep mashing those
buttons Of course, if the player doesn’t keep mashing the buttons, each individual attack has
a few back-to-idle frames, so we have a totally legit combo now
The fun part is at the end If the player does an uppercut (Down on the left analog + Y),
we’ll jump to the last row, launching skyward with a nice spanner-uppercut and finishing up in
the fly animation
See how powerful this stuff is? We can really go nuts with these combos—air juggles, complex
ground combos, far-reaching mega slams We’ve just opened up a really fun and exciting part
of this whole design racket!
Trang 2Figure 6-10 Complex stuff: the “attack” animation
Odds and Ends: Cleanup
Now that we have our script system in place (and gotten all hot and bothered in the process),
we can clean up some placeholder animation code in Character—namely jumping and landing Our character does look a little stiff-legged on the landing, doesn’t he?
In Update(), we’re going to change the key input part to this:
#region Key input
if (animName == "idle" || animName == "run")
Trang 3And then we’ll update Land() to look like this:
private void Land()
{
State = CharState.Grounded;
SetAnim("land");
}
This means that in our character definition file (guy.zmx), we need to create a jump
anima-tion, which will have our guy crouching and end in a joymove, setjump 600, and setanim fly,
and a land animation, which will end in a setanim idle We’ve basically added an extra
anima-tion between idle/running and flying through the air These animaanima-tions are then responsible
for progressing the character’s motion
Conclusion
We’ve made some terrific headway into our game We created our game solution, moved in all
of the right classes, loaded our content, loaded our data, and set up what is shaping up to be a
very complicated, very expressive Character class, complete with simple movement and
colli-sion detection
Next, we mapped out our scripting language, modified our character editor to allow script
editing, and implemented script parsing and running from our Character class We looked at
how we can use our ultra-simple scripting language to add a lot of depth and expressiveness to
our characters We’ll be building on this quite a bit as we flesh out our game
Our next order of business is going to involve lots of particles: sparks, muzzle flashes,
explosions, and more We’ll call it particle mayhem!
Trang 4■ ■ ■
Particle Mayhem
Bring Out the Smashing!
Designing particle systems is probably one of the most exciting aspects of independent game
development, yet it also happens to be an area where a lot of aspiring indie developers fall flat
This is another programmer art issue While large teams with big budgets can rely on tools to
better facilitate art direction for particle systems, independent developers must either work the
entire thing out for themselves or try to collaborate with an artist to really nail the feel of it
Furthermore, many developers often take a side road and never come back once they hit
parti-cles After building a basic system, it’s easy to get caught up in adding features to the particle
system and creating an editor, because particles are just so pretty If possible, get someone else
to build the particle system for you and integrate it into the game, so that you have time to work
on more pressing matters However, this book is written with the one-person team in mind, so
we’ll get it done
It’s always nice to have a bit of history under your belt when tackling something new We’re
about to unleash some shiny, explosive particle mayhem, so to prepare ourselves, we’ll take a
brief look at the quintessential rocket contrail, starting in 1993
A Brief History of Rocket Contrails in
First-Person Shooters
Doom introduced rockets without contrails Still, it had rockets, which we all thought was amazing
From the first inception of the rocket, players have been blowing each other up without
actu-ally hitting any rockets! But a rocket is nothing without a sweet trail of smoke and fire spewing
out, attracting everyone’s attention to the destruction that lies ahead and the person who created
it, as you see in Figure 7-1
Marathon (a Doom-like title that was a Mac exclusive for quite awhile) had rockets with
billboarded smoke contrails Unfortunately, something didn’t quite sit right about the contrails
They were drawn “attached” to the rockets, such that each smoke billboard was rendered a
fixed distance from the rocket This created an illusion that the player wasn’t firing a rocket, but
instead launching a giant tube consisting mostly of fluffy gray stuff with a rocket-like protrusion
at the business end
Trang 5Figure 7-1 Rocket contrail
Quake did it right, albeit cheaply Because the technology was already so taxing on the systems of the day, the Quake developers settled for giant point particles, rather than billboarded quads Each rocket left a trail of yellowish particles and gray particles; the yellow ones slowly fell, while the gray ones slowly rose Still, we all thought it was amazing, and it looked right according to some basic level of physics
Half-Life took another step backward in the name of progress The technique is similar to sword slash effects A contrail is made of a solid polygon with vertex pairs added at each point where a Quake rocket would have dropped some particles The vertex pairs are rotated so that the viewer will get the widest view of each quad section It seemed like a good idea, but in a number of cases, the technique just didn’t look right
Now that technology has caught up with the ubiquitous rocket contrail, it seems the industry has settled on billboarded quads However, for those who look to the future, volumetric rendering could allow for some very gorgeous smoke trails Coupled with a nice haze and fire effect, rockets of the future will look more realistic than ever However, for our purposes (and for much of the industry), volumetric clouds are a bit of overkill
In a nutshell, the modern rocket contrail is made up of billboarded quads, dropped from a fired rocket at regular intervals These quads change colors, fade out, and die after a short life span This modern rocket contrail just looks right More important though, it is realistic enough without creating a huge performance hit
Why is it important to pore over details like this? Much like many other aspects of game development, it is all too easy to get bogged down trying to make good-looking code rather than a good-looking game It’s important to be able to code, build, and run, and to be able to say not only, “this is doing what it’s supposed to,” but also “hey, this looks great!”
Setting Up a Particle System
We’ll start of by setting up the programmatic structure for our particles, and then we’ll make some mayhem We personally think that the first task is the boring part and the second is the fun part, so the attention to detail on each will reflect that Half the fun of particle systems is spent tweaking them to make explosions, splatters, and general effects look both on the money and dramatic enough to draw the player in for some more
A Base Class
We’ll start by defining a base Particle class Particles have fairly limited functionality: they can
be constructed, updated, and rendered They have locations and trajectories, short life spans, and a few other flags that we can play with
Trang 6The Update() function will decrease the particle life (killing it if necessary) and move the
particle along its trajectory The trajectory works as it did in the Character class, acting as a
consistent velocity to multiply by elapsed time and add onto the current location Update() has
a few parameters that we’ll explain later
public class Particle
{
protected Vector2 location;
protected Vector2 trajectory;
protected float frame;
protected float r, g, b, a;
protected float size;
protected float rotation;
protected int flag;
protected int owner;
public bool Exists;
public bool Background;
public Vector2 GameLocation
Trang 7public virtual void Draw(SpriteBatch sprite, Texture2D spritesTex)
• owner is typically used to indicate the index of the character that is responsible for this particle
• flag is commonly used for special data, such as which image index a particle uses
• background is used to determine whether the particle is drawn behind all characters or in front of them
To get started, we need some imagery We like to use sprite sheets full of all the neous particle imagery we’ll need Let’s begin by making some fluffy white blobs, as shown in Figure 7-2
miscella-Figure 7-2 Fluffy white blobs (the particle sprite sheet)
Trang 8We’ll create our Smoke class to extend the Particle base class.
class Smoke : Particle
if (trajectory.Y < -10.0f) trajectory.Y += gameTime * 500.0f;
if (trajectory.X < -10.0f) trajectory.X += gameTime * 150.0f;
if (trajectory.X > 10.0f) trajectory.X -= gameTime * 150.0f;
Trang 9The constructor is straightforward enough The Draw() and Update() methods show a bit
of life, and ironically enough, take it away!
The Update() method adds a bit of definition to the smoke particle by decelerating its trajectory as it nears death; that is, it will cause smoke to slow down as it fades out, giving it a more natural look (which is what this is all about, no?)
The Draw() method does a few things It determines the source rectangle based on our flag field Then it calculates a scalar, frameAlpha, as a linear function of frame—the particle will quickly fade in and slowly fade out The sprite.Draw() call has a bit of substance to it The color
is calculated such that the RGB values steadily decrease, while the alpha value changes with frameAlpha Also, the size steadily increases
Particle Management
Now we need a class to manage all of these particles Here’s an area where we skimped a bit Traditionally, particle systems are atomic entities, where one system governs its child particles, and each system has its own life cycle We just used a big array, without an emitter-child hier-archy, where particles can act as particle emitters This turns out to be very beneficial, because certain particles can act as particle emitters For example, a spark-like particle that shoots through the air and explodes can be represented as a particle that explodes and emits particles For now, however, we will focus on the more basic particle examples of blood, smoke, and fire.class ParticleManager
{
Particle[] particles = new Particle[1024];
SpriteBatch sprite;
Trang 10public ParticleManager(SpriteBatch sprite)
Trang 11To see our smoke in action, we’ll just set up our game so that smoke flies off of our hero’s head, and then get to practical cases later In the Character.Update() method, add the following:
#region Particle Test
for (int i = 0; i < 4; i++)
Let’s do some setup in Game1 At the class level, add this:
Texture2D spritesTex;
ParticleManager pManager;
In LoadContent(), we now can create our ParticleManager with spriteBatch and load our sprites texture:
Trang 12protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
pManager = new ParticleManager(spriteBatch);
pManager.UpdateParticles(frameTime, map, character);
And finally, draw in (where else?) Draw():
pManager.DrawParticles(spritesTex, true);
character[0].Draw(spriteBatch);
pManager.DrawParticles(spritesTex, false);
Our hot-headed guy in action is shown in Figure 7-3 The picture doesn’t do justice to the
code in action, so give it a try
Figure 7-3 Smoke in action
Trang 13The parameters we used in AddParticle() were decided after a bit of tweaking Not enough randomness would make the smoke look unnatural; too much randomness would make it look unnatural for other reasons See for yourself the ravages of bad randomization in Figure 7-4.
Figure 7-4 Bad amounts of randomness: too little (left) and too much (right)
Additive Blending: Fire
Additive blending is a cheap way to make flashy (literally) effects, and it’s actually tionally cheaper than plain-old alpha blending The idea of additive blending is that color
computa-values are added to what’s currently on our backbuffer (The backbuffer is what we draw on
each time through the Draw() loop.) When we use vanilla alpha blending, we paint onto the backbuffer with the intensity of the alpha value, so drawing black with an alpha value of 0.5 will darken things a bit Additive blending, on the other hand, adds only color values Additive blending is great for bright, flashy effects—fire, electricity, muzzle flashes, lens flares, and laser blasts, to name a few
We’ll need to make a few structural changes to our ParticleManager and Particle classes
to efficiently render additive particles We want to draw all alpha-blended particles as one batch, and then additive particles as another, simply because we cannot mix the two methods
Trang 14private bool additive;
public bool Additive
{
get { return additive; }
protected set { additive = value; }
}
In ParticleManager, we modify the Draw() method so it does two draw loops: one for
alpha-blended particles and one for additive particles This is much faster than putting each
sprite in its own batch, especially on the Xbox 360 This is due to how the SpriteBatch works
and, in fact, how rendering in general works It is more efficient to push as many graphics as
possible through the pipeline at once, rather than pushing a small amount at fast intervals You
can liken it to trying to drink a thick milkshake through a straw—a thin straw isn’t going to work
as well as a wider straw
Now we’ll make a Fire class, which can be fairly simple Over time, the color of flames will
transition from white to yellow to red to black Because it will be rendered additively, black is
synonymous with clear The size may diminish slightly over time, and the flame wisps will
rotate as well It’s simple in theory, but the settings and how the particles are morphed over
time matter Here’s the code:
class Fire : Particle
Trang 15Rectangle sRect = new Rectangle(flag * 64, 64, 64, 64);
float bright = frame * 5.0f;