F ONT EXAMPLE: DISPLAYING TEXT IN THEGAME WINDOW This example explains the steps to display the string “Score Tracking and Game Stats” in the top-left corner of the game window.. Alterna
Trang 1SineCycle()traces a floating-point value through a sine wave’s cycle over time.The function is only executed once per frame but is used to update each Y value for allvertices in the grid Add this function to your game class:
float SineCycle(GameTime gameTime){
// less than full cycle of sine wave retrieves between 0 and 1.
// full cycle for sine wave is 2*PI.
if (cycleIncrement < 1)
cycleIncrement += 0.0000005f * (float)gameTime.ElapsedGameTime.Milliseconds;
// adjust when sine wave cycle complete
it rises and falls over time
SetWaterHeight() receives the sum of the texture’s V coordinate plus thepoint in the sine wave over time This sine wave equation returns a Y value for the co-ordinate that corresponds with the V coordinate:
Height = Amplitude = sin(WaveCountPerCycle * PointInCycle * 2π)Add theSetWaterHeight()method to the game class:
float SetWaterHeight(float cycleTime){
const float FREQUENCY = 6.0f; // wave count per cycle
const float AMPLITUDE = 1.0f/15.0f; // wave height
// generates height based on V coord and sine equation
return (AMPLITUDE * (float)Math.Sin(FREQUENCY * cycleTime
* 2.0f * (float)Math.PI) - 0.4f);
}
Trang 2The X, Y, Z information is the same for both the stationary image layer and the
moving image layer Since the stationary layer is drawn first, the Y value that changes
with the sine wave over time can be set for this layer and the changes will apply to
both image layers Adding this code to reset the X, Y, and Z coordinates inside the
nested for-loop forUpdateMovingSurface()will create a dynamically changing
Y value that simulates the wave for both layers over time:
float X = surface1[col + row * NUM_COLS].Position.X;
float Y = SetWaterHeight(V + SineCycle(gameTime));
float Z = surface1[col + row * NUM_COLS].Position.Z;
surface0[col + row * NUM_COLS].Position = new Vector3(X, Y, Z);
surface1[col + row * NUM_COLS].Position = new Vector3(X, Y, Z);
When you run this program, it shows the moving dynamic texture and the waves
rippling through the object The effect is actually quite beautiful (see Figure 12-5)
You can try building this example, or you can download the completed example
from the Solutions folder on this book’s website
There are various ways to combine images for creating exciting graphics effects
Sprites are used to animate a series of image frames that are stored in an image file
Multitexturing can be used to blend two images together and provide more detail or
dynamic movement for the texture
Trang 3C HAPTER 12 REVIEW EXERCISES
To get the most from this chapter, try out these chapter review exercises
1. Try the step-by-step examples presented in this chapter, if you have notalready done so
2. For your solution to the SpriteBatch example, remove the code thatmanually resets theRenderStateproperties for theGraphicsDevice
in theDraw()method Then, add code to automatically restore the renderstates after theSpriteBatchobject is drawn Automatically restoring therender states can be done inDrawAnimatedHud()by replacing the
SpriteBatchobject’sBegin()instruction with code similar to this:
spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
SpriteSortMode.Immediate,SaveStateMode.SaveState);
Try running your code and notice that the output appears to be the same asbefore
3. Replace yourBegin()statement in Exercise 2 with an instruction similar
to the following statement and then run your project:
spriteBatch.Begin();
Notice how the ground and all other 3D objects disappear when the renderstates are not restored
4. With the solution for the 2D sprite and the original 2D sprite settings, call
DrawAnimatedHud()beforeDrawGround() Notice that you cannotsee the sprite unless the view is changed so the ground is not covering it
5. Create your own sprite with three or more frames In the same project,show the sprite as a 2DSpriteBatchobject Display your sprite in the3D world using a textured sprite
6. Use multitexturing to make it appear as if moving shadows cast from theclouds are traveling across the ground
Trang 4CHAPTER 13
Score Tracking and Game Stats
Trang 5ple, you might need to show statistics such as health, fuel level, the current mapname, or maybe even the opponents’ names In the end, your ability to present this in-formation boils down to having access to a font library that can overlay 2D text onyour game’s 2D or 3D environment As you would expect, XNA offers an excellentfont library for this purpose
The two examples in this chapter demonstrate how to write text and numeric put to the game window When you are finished (depending on which project youstart with), your output will be similar to the window display shown in Figure 13-1
Text and numeric data drawn in the game window
Trang 6F ONT EXAMPLE: DISPLAYING TEXT IN THE
GAME WINDOW
This example explains the steps to display the string “Score Tracking and Game
Stats” in the top-left corner of the game window You could use the same technique
to show your name, display your scores, or to show other important statistics—like
your shield strength or health level
This example can begin with either the Windows or Xbox 360 starter projects
from the BaseCode folder on the book’s website Alternatively, you could add this
font example code to a new project generated by Visual Studio’s project template
Loading the Font Type Data
Once you have chosen a starter project, you will use the XNA font class, so you will
need to add a reference to the font type description data, in the Solution Explorer
The font can be added by right-clicking the Content node, choosing Add and
select-ing New Item Once you have the Add New Item – Content dialog open, you can add
a font by choosing the Sprite Font template icon For this exercise, call your file
“MyFont.”
Once you click Add, this will generate an XML file called MyFont.spritefont,
which is then automatically placed in your project At this point you will see the
spritefont file referenced in the Solution Explorer (refer to Figure 13-2)
Trang 7If you view the contents of the spritefont file, you will see XML code that stores thefont properties You can adjust these elements to change the size of the font and thespacing between characters, and you can also set the font to be Regular, Bold, Italic,
or Bold Italic in the<Style>element
To reference a TrueType font on your PC or Xbox 360, you will need to adjust the
<FontName>element value in the spritefont file In this example we will load a rier New font, so to do this, you must replace the FontName element with:
<! Spacing is a float value, measured in pixels Modify this value
to change the amount of spacing in between characters. >
<Spacing>0</Spacing>
<! UseKerning controls the layout of the font If this value is
true, kerning information will be used when placing characters. >
<UseKerning>true</UseKerning>
<! Style controls the style of the font Valid entries are
"Regular", "Bold", "Italic", and "Bold, Italic", and are case
sensitive. >
<Style>Regular</Style>
Trang 8<! If you uncomment this line, the default character will be
substituted if you draw or measure text that contains characters
which were not included in the font. >
<! <DefaultCharacter>*</DefaultCharacter> >
<! CharacterRegions control what letters are available in the font.
Every character from Start to End will be built and made available
for drawing The default range is from 32, (ASCII space), to 126,
('~'), covering the basic Latin character set The characters are
ordered according to the Unicode standard See the documentation for
For now, aside from changing theFontName, you can leave the default spritefont
file settings as they were originally generated However, you can edit the elements in
this file further to change the size, weight (boldness), italics, and other properties if
desired
Loading the Font
Fonts are drawn using aSpriteFontobject, which you must declare at the top of
the game class:
private SpriteFont spriteFont;
TheSpriteFontobject actually is a sprite, so the corresponding data behind it
should be read in theLoadContent()method This object is loaded by first
retriev-ing the font description data from the spritefont file Since the spritefont file is
refer-enced in the Content folder, you do not need to specify a directory when loading it
To load this file with theLoad()method, you must pass the name of the spritefont
filename without the file extension:
Trang 9Ensuring Your Fonts Are Drawn in the Visible Portion of
the Window
We have already discussed the need to designate a title safe region in Chapter 4 Thisavoids truncating your graphics when running your games on the Xbox 360 Sincefonts are 2D, and you do not want your fonts to be truncated, you will need to displayyour text in a title-safe region ATitleSafeRegion()method was used in Chap-ter 4 to generate a rectangle that stores the top, bottom, left, and right marginsaround the safe area An alternate, but similar, version of the
TitleSafeRegion()method will be used in this chapter to calculate the startingpixel position on the window for each string that is written to it
This newTitleSafeRegion()method receives a string and aSpriteFont
object as parameters It then uses theSpriteFont’sMeasureString()method
to retrieve the width and height of the string TheMeasureString()method usesthe string to determine the output width and it uses theSpriteFontobject to calcu-late the height of the font Here is the revised version of theTitleSafeRegion()
method to conveniently generate margins for the visible display area from gameclass:
Rectangle TitleSafeRegion(string outputString, SpriteFont font){
Vector2 stringDimensions = font.MeasureString(outputString);
int stringWidth = (int)stringDimensions.X; // string pixel width int stringHeight = (int)stringDimensions.Y; // font pixel height
// some televisions only show 80% of the window
const float UNSAFEAREA = 0.2f;
const float MARGIN = UNSAFEAREA/2.0f;
// calculate title safe bounds for string
int top, left, safeWidth, safeHeight;
Trang 10Drawing the Font
The font render is triggered from theDraw()method It is drawn using an
overrid-denDrawString()method from theSpriteBatchclass TheDrawString()
parameters for this override include theFontBatchobject, an output string, the
starting top-left pixel position on the window where the string is drawn, and color
Whenever you draw a 2DSpriteBatchobject, the process must start with the
Be-gin()method and finish with theEnd()method If you are adding your font code to a
3D game project, call your font drawing code after all your other objects are rendered
This ensures that your text displays at the forefront of the window Otherwise your text
will be covered by other objects that are rendered later by theDraw()method
Saving Your Render States
When drawing 2D objects with aSpriteBatchobject, XNA automatically adjusts
the GraphicsDevice object to render in 2D This makes drawing with
SpriteBatch objects easy, but you have to be careful because the original
GraphicsDevicesettings are not restored If theGraphicsDevicesettings are
not restored, your 3D objects may disappear and your tiled images may be thrown
out of whack You can get some hair-raising results if you forget and think your game
code is broken To automatically restore the settings (after drawing 2D fonts in your
3D games), use theSaveStateMode.SaveStateproperty as the third parameter
in an overridden version of the Begin() method Be aware, though, that
SaveStateMode.SaveStatecan cause performance issues if you use it a lot in
your game, so you may want to manually reset your graphics device to avoid this
per-formance hit Restoring the graphics device manually is described in Chapter 12
Here is the code to draw your output:
private void DrawFonts(GameTime gameTime){
string outputString;
Rectangle safeArea;
// start drawing font sprites
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, // enable transparency
SpriteSortMode.Immediate, // use manual order SaveStateMode.SaveState); // store 3D settings outputString = "Score Tracking and Game Stats";
safeArea = TitleSafeRegion(outputString, spriteFont);
spriteBatch.DrawString(spriteFont, outputString, new Vector2(
safeArea.Left, safeArea.Top), Color.Yellow);
Trang 11// stop drawing - and 3D settings are restored if SaveState used
To create the frames-per-second count, you will use a timer like the one presented
in Chapter 12 In this example, the total frames rendered during 1-second intervalsare counted When each 1-second interval is complete, the total frame count gener-ated is displayed on the screen for the second that follows—until a new count is tal-lied and displayed
Some setup is required to store the count and interval times, so you will need toadd the following variable declarations (for storing the counter and time values) atthe top of your game class:
private double fps, fpsCounter;
private double intervalTime = 0; // time in current interval private double previousIntervalTime = 0; // interval time at last frame
The timer method discussed in Chapter 12 must also be added to measure theframe count in 1-second intervals A value of 1000 milliseconds is assigned for the in-terval to ensure that the timer returns atruevalue for every second
bool Timer(GameTime gameTime){
bool resetInterval = false;
// add time lapse between frames and keep value between 0 & 1000 ms intervalTime += (double)gameTime.ElapsedGameTime.Milliseconds;
intervalTime = intervalTime % 1000;
Trang 12// intervalTime has been reset so a new interval has started
The code that counts and stores the number of frames per second is also added to
the game class The count you see in the window is actually the total generated in the
previous 1-second interval:
public String FramesPerSecond(GameTime gameTime){
if (Timer(gameTime)){ // check if 1 second is up
fps = fpsCounter; // 1 second complete so assign new FPS value
fpsCounter = 0; // reset counter to 0 to start new interval
}
else
fpsCounter += 1; // increment counter when interval incomplete
return "Frames Per Second: " + fps.ToString();
}
Before drawing the text, the starting top-left pixel position is needed to
right-jus-tify the string at the bottom-right, title-safe corner of the window TheUpdate()
method is not necessarily called the same number of times as theDraw()method
This code belongs in the DrawFonts() routine immediately before
spriteBatch.End()is called:
outputString = FramesPerSecond(gameTime);
safeArea = TitleSafeRegion(outputString, spriteFont);
spriteBatch.DrawString(spriteFont, outputString, new Vector2(
safeArea.Right,safeArea.Bottom), Color.Yellow);
When you run this code, the frame count appears at the bottom right of the
win-dow Drawing information to the screen is not only useful for your gamers, but you
may also find the frames-per-second routine useful when testing your code’s
Trang 13C HAPTER 13 REVIEW EXERCISES
To get the most from this chapter, try out these chapter review exercises
Try the step-by-step examples presented in this chapter, but make the followingchanges
1. Use a Times New Roman font to display the “Score Tracking and GameStats” title and use a Courier New font to display the frames-per-secondcount
2. Create a custom score board Increment the score every time the spacebar ispressed
3. Change the size of your font to 24 points
Trang 14CHAPTER 14
3D Models
Trang 15You could add them by hand-coding a bunch of textured primitive objects, but thatwould be way too much work The obvious way to efficiently develop a complex 3Dobject is with a 3D modeling application Learning to work with 3D models is a giantstep in understanding game development It allows you to add realistic and excit-ing-looking objects to your game world By the end of this chapter—after you havecreated your own models and animated them in code—you will certainly choose theuse of 3D models over hand-coded primitive objects when possible
Once you have developed a 3D model, you can import it into your game and trol it with your code The two supported model formats currently for XNA are xand fbx Microsoft has provided a library of code to load these models into XNA foryou If you really wanted, you could use other model file formats in your game, butyou’d have to write a model loader
con-3 D MODELING TOOLS
Autodesk Maya, Autodesk 3D Studio Max, and Softimage XSI are three of the mostpopular modeling tools for professional game artists, but these packages are expen-sive That’s not to say they aren’t worth their cost; these packages are definitelyworth it if you can afford them If you are a student, you may be able to purchase aneducational license for a fraction of the cost of a commercial license
Most high-end modeling tools, such as Maya or 3ds Max, have the ability to port to Microsoft’s x format or Alias’s fbx format if you install the right combina-tion of plug-ins However, converting other model formats to x or fbx can be afinicky process If you plan to use a modeling tool, then experiment with it first sothat you are sure about the tool’s requirements for successful conversions
ex-An inexpensive, but popular, lightweight 3D modeling program is MilkShape, bychUmbaLum sOft MilkShape is used for the examples in this book because it is one
of the easiest modeling tools to learn In addition, MilkShape’s support for the fbxformat is excellent MilkShape also imports from and exports to over 70 relevantmodel file types for games Even if you decide later that you prefer a different model-ing tool, MilkShape is a great application to use when you are learning how to create3D models chUmbaLum sOft offers a free 30-day trial version The purchase price issurprisingly inexpensive—$35 (U.S.) at the time this book was written A link to their30-day trial version is available on this book’s website
If you have used MilkShape up until XNA 3.0 and shortly after this version was leased, you may find these models are not textured when loading them in an XNA 3.0project The XNA team upgraded their model loader in XNA 3.0 to stay current withthe FBX format but MilkShape had not yet made this change chUmbaLum sOft isaware of this issue and this problem should be resolved before the book is released