1. Content Project
When you create a new XNA project, it always creates
two projects, one project for your game and the other for your content.
The Content project provides compile-time processing for game assets,
including detecting missing assets, asset conversion, compression, and
pre-processing. The key component to the Content project is the Content
Pipeline.
1.1. Content Pipeline
The content pipeline does the work of preparing
assets for use within an XNA Framework game. Assets are processed in two
steps. The first step is importing the content. For the majority of
formats, XNA has Content Importer classes to convert content
into a common intermediate format. The second step converts the imported
content into a final compiled, compressed, format via a Content Processor.
1.2. Importing the Content
Importing content is as simple as adding new items to
the Content project. In most cases, the content pipeline automatically
detects the asset type and assigns the correct Content Importer and Content Processor. It is also possible to create custom Content Importer and Content Processor classes for assets not supported by the built-in classes.
HeroShip = this.Content.Load<Texture2D>("Sprites/heroship");
SpaceShip = this.Content.Load<Texture2D>("Sprites/spaceship");
Missile = this.Content.Load<Texture2D>("Sprites/missile");
Each content item is loaded into a single texture.
When drawn, each item requires its texture to be loaded by the GPU and
rendered. The above items are drawn to the screen with these three lines
of code in the Draw method:
spriteBatch.Draw(SpaceShip, SpaceShipPosition, Color.White);
spriteBatch.Draw(Missile, MissilePosition, Color.White);
spriteBatch.Draw(HeroShip, HeroShipPosition, Color.White);
Loading each texture is a performance hit for the
GPU. A better way is to load a single texture that contains all of the
images, and then just tell Draw which area of the texture to draw for
that item. This type of texture is called a sprite sheet and is very
common to use in game development, especially when creating sprite
animation. There are two
challenges with using sprite sheets: you have to take all of your
individual images and mash them into a larger image, and you have to
remember which part of the single large texture contains the particular
image you want to draw to the screen.
Luckily AppHub comes to the rescue again with a
ready-to-go custom Content Importer and Content Processor that can take a
collection of images and automatically turn them into a single sprite
sheet, solving the first challenge. For the complete background on the
sample, download it here:
http://create.msdn.com/en-US/education/catalog/sample/sprite_sheet
The sample also includes a runtime class to include in your game named SpriteSheet, which
allows either named or indexed access to images so that you do not have
to remember the exact pixel location, solving the second challenge
listed previously.
Right-click the References folder in the AlienShooterContent project, select Add Reference, choose the Projects tab, and select the SpriteSheetPipeline project. Next, right-click on the References folder in the AlienShooter project and select Add Reference, choose the Projects tab, and select the SpriteSheetRuntime (Phone) project.
In the AlienShooterContent project navigate to the Sprites folder and exclude from the project these assets: heroship.tga, missile.tga, and spaceship.tga.
We don't want to add the textures twice, so they should not be included
as individual assets any more. Next, right-click on the Sprites folder and select Add | New Item... and choose XML File with a name of AlienShooterSpriteSheet.xml. Listing 1 shows the edited XML file.
Example 1. AlienShooterSpriteSheet.xml Sprite Sheet XML File
<?xml version="1.0" encoding="utf-8" ?>
<XnaContent>
<Asset Type="System.String[]">
<Item>Sprites/heroship.tga</Item>
<Item>Sprites/missile.tga</Item>
<Item>Sprites/spaceship.tga</Item>
</Asset>
</XnaContent>
|
If you don't edit the file correctly, you will get a compile error if the SpriteSheetPipeline
Content Processor cannot find an asset listed in the XML file. Notice
in this example that the folder location is included relative to the AlienShooterContent project to correctly identify the asset location. Figure 7-3 shows the Solution tool window with the AlienShooterContent project expanded and the Properties dialog for the AlienShooterSpriteSheet.xml.
Notice in Figure 1
that the individual sprite images are hidden and not part of the
project in the Sprites folder, though they need to be accessible in the
file system to the SpriteSheetProcessor Content Processor so
that it can turn them into a single sprite sheet. The only content that
is part of the Sprites folder is the AlienShooterSpriteSheet.xml XML file. Also in Figure 7-3, notice that the custom SpriteSheetProcessor Content Processor is configured for the XML file. Let's now draw update the GamePlay screen to start to render our new AlienShooter assets. A using SpriteSheetRuntime statement is added to the top of the GamePlay.cs code file. Three new private fields are added at the top of the GamePlayScreen class:
SpriteSheet alienShooterSpriteSheet;
Texture2D backgroundTexture;
Rectangle screenRect;
The alienShooterSpriteSheet variable will let us render the entire sprite sheet to the screen for debug purposes. The backgroundTexture variable represents our game background as with the HelloXNA sample and the screenRect variable holds a variable that points to the Rectangle object that is the size of the draw area.
The LoadContent method is updated to load the alienShooterSpriteSheetSpriteSheet and backgroundTextureTexture2D variables:
alienShooterSpriteSheet = content.Load<SpriteSheet>("Sprites/AlienShooterSpriteSheet");
backgroundTexture = content.Load<Texture2D>("Textures/background");
Most of the code in the Update and HandleInput
methods is commented out since we no longer want the place holder code.
The placeholder draw code is modifiedin theDraw method, and the
following two additional items are added to draw the backgroundTexture and the alienShooterSpriteSheet to the screen:
spriteBatch.Begin();
//Draw background
spriteBatch.Draw(backgroundTexture, screenRect, Color.White);
//Draw Sprite Sheet
spriteBatch.Draw(alienShooterSpriteSheet.Texture,
new Rectangle(screenRect.Width / 2, screenRect.Height / 2,
alienShooterSpriteSheet.Texture.Width / 2,
alienShooterSpriteSheet.Texture.Height / 2),
Color.White);
spriteBatch.End();
Everything should compile and the game now shows AlienShooter assets on the GamePlayScreen, as shown in Figure 2.
Notice how tightly the Hero Spaceship, Missile, and Alien Ship are automatically packed in Figure 2
by the custom Content Processor. Just to close the loop, the code to
draw an individual image from the AlienShooterSpriteSheet is very
simple. Here is the code to just draw the Hero Spaceship at a point in
the middle of the screen:
spriteBatch.Draw(alienShooterSpriteSheet.Texture,
new Vector2((screenRect.Width / 2)-
alienShooterSpriteSheet.SourceRectangle("heroship").Width / 2,
(screenRect.Height / 2)-
alienShooterSpriteSheet.SourceRectangle("heroship").Height / 2),
alienShooterSpriteSheet.SourceRectangle("heroship"),
There is a verbose computation to create a Vector2 for the screen center minus the center of the Hero Ship, but otherwise it is the standard Draw(texture, Vector, SourceRectangle, Color) method. Notice how easy it is to identify the Rectangle on the alienShooterSpriteSheet where the Hero Ship resides. Just pass in the asset name and the SpriteSheet class will locate it.
When you download the SpriteSheet Sample
from AppHub, it includes an example extension on how to add a
compression option to the Content Processor. Unfortunately the extension
does not work for the XNA Game Studio 4.0 Reach profile. It just works
for the HiDef profile.
The "Reach" profile limits XNA Game Studio to a
subset of functions supported on Windows Phone, Xbox, and Windows for
maximum compatibility. The "HiDef' profile supports only Xbox and
Windows. It includes support for custom HLSL shaders as well as
additional features.
|
|
We dive into creating a more robust game below but
first let's cover some of the other constructs for a real game such as
displaying game status text and menus, which is covered in the next
section.
2. Text and Menus
Displayed text, such as game status and menus, is an
important component of game development. Users want a nice clean menu
system to navigate. Game status is critical to game play; it makes the
game interesting such as how much life is left, high score, rounds of
ammunition available, and the like.
There are two methods to display text in the XNA Framework. The first is via the SpriteFont class. The SpriteFont
class leverages the built in Font classes on your PC to create the font
texture to display letter character images when rendered. The SpriteFont class is covered in the next section.
The other method to display text in the XNA Framework
is with customized bitmap fonts. This method is more advanced, which
means it is highly customizable but also requires additional work.
2.1. SpriteFont
The SpriteFont class takes a built in font
and rasterizes it based on configuration parameters. To add a text Font
for rendering in XNA Game Studio, right-click on the FontsSampleContent project and select Add => Item... =>
select Sprite Font, and enter a name. Usually, you name the Sprite Font
item the same as the Font Name. If you include multiple sizes append
the size to the end, which will make more sense in a bit.
While we take fonts for granted when working in
Microsoft Word or in Outlook, fonts are licensed content. You must have
redistribution rights to include a font in your game. Search the Web for
"purchase fonts online" and quite a few sites show up. Some include
free fonts with others for purchase.
Luckily XNA Game Studio 4.0 includes a set of OpenTypefonts you can use in your games. Figure 3 shows the fonts that are licensed by Microsoft for your use. The image is taken from the AppHub education catalog.
For our sample, name the new Sprite Font item SegoeKeycaps24 and click Add. A new item named SegoeKeycaps16.spritefont is added to the FontSampleContent project. What may surprise you is that the SegoeKeycaps16.spritefont file is an XML file. Listing 1 shows the contents of the newly added file.
Example 1. SpriteFont XML File Format
<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">
<FontName>Segoe UI Mono</FontName>
<Size>14</Size>
<Spacing>0</Spacing>
<UseKerning>true</UseKerning>
<Style>Regular</Style>
<!-- <DefaultCharacter>*</DefaultCharacter> -->
<CharacterRegions>
<CharacterRegion>
<Start> </Start>
<End>~</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>
|
The XML file describes the parameters for what should
be rendered when text is drawn using this SpriteFont. For our example,
change the FontName element to Segoe Keycaps. Change the Size to 16 and leave everything else at the default values.
You can edit the Spacing element to increase the space between characters, as well as modify the UseKerning
item to modify how the font is laid out. Kerning adjusts the spacing
between characters to be more pleasing to the eye. An example of kerning
is when a capital "W" overhangs a neighboring "e" slightly when typing
the word "Well." Without kerning, letters do not overhang or underhang
as the case may be. The Style element indicates whether it should be Regular, Bold, or Italic.
The last element to discuss is the CharacterRegions
element. CharacterRegions element controls what letters are available in
the font. The default range is 32 (ASCII space) to 126 (ASCHII '~').
This reduces the number of characters rasterized as part of the content
import process.
To draw the SegoeKeycaps16 Sprite Font, add a new private member named segoeKeycaps16 of type SpriteFont to the Game1 class in the FontSample project. Next add this line of code to the Game1.LoadContent method:
segoeKeycaps16 = Content.Load<SpriteFont>("SegoeKeycaps16");
We used the SpriteBatch.Draw method to draw textures to the screen. The SpriteBatch.DrawString method has six overloads to provide flexibility in drawing text to the screen. Here are a couple of examples:
spriteBatch.DrawString(segoeKeycaps24, text, new Vector2(24, 130), Color.Yellow);
spriteBatch.DrawString(segoeKeycaps24, text, new Vector2(24, 450), Color.Orange,
-.25f * (float)Math.PI, Vector2.Zero, 3, SpriteEffects.None, 0);
You can tinge the font color by adjusting the fourth parameter with a color value such as Color.Yellow. The second DrawString
call above applies a –negative 45 degree rotation in the fifth
parameter and a 300 percent scale in the sixth parameter, with an orange
tinge applied to the Sprite Font. Another font is added to the project
named Miramonte with Italics style and a few more DrawString calls are in the code but Figure 4 shows the results.
The scaled font renders pretty well but you can see some stretching in the letter "e" in Figure 2 that could be more obvious with other fonts. In the next section we cover how to create a bitmap font.
2.2. Bitmap Fonts
Bitmap fonts are not dependent on a built-in font. Instead, the fonts are loaded from a texture similar to what is shown in Figure 5.
The example in Figure 7-7
was copied from MSDN by zooming in the browser by 200% and taking a
screenshot, but you can create a custom bitmap font texture using any
drawing program by following the conventions specified in the
documentation.
The character order matters, with a space as the
upper-left character. You can use a solid white or black background
color for monochrome characters. In the previous example, black is used
as the background color. Multicolored characters are supported with an
alpha channel.
The space between characters must be filled with Magenta color (Red:255 Green:0 Blue:255 Alpha:255). The FontTextureProcessor class is applied to the texture instead of the default processor. The FontTextureProcessor will pack the characters in as close as possible so you don't have to worry about exact spacing between characters.
The bitmap font texture in Figure 6 is added to the BitmapFontSampleContent project with a name of BitmapFont.bmp. The Content Processor is configured to Sprite Font Texture - XNA Framework from the default of Texture - XNA Framework.
The SpriteFont class is still used as the
member type for the bitmapFont object and the bitmap font is loaded in
LoadContent just like for a regular SpriteFont content:
SpriteFont bitmapFont;
...//Load Content
bitmapFont = Content.Load<SpriteFont>("BitmapFont");
Drawing is the same as well with the DrawString method:
spriteBatch.Begin();
spriteBatch.DrawString(bitmapFont, text, new Vector2(24, 70), Color.White);
spriteBatch.End();
In summary, the primary differences when using a
custom bitmap font is that you must draw out the characters correctly in
the proper order and you configure the Content Processor to Sprite Font Texture - XNA Framework for the bitmap font texture.
2.3. MenuScreen Class
The MenuScreen class is part of the GameStateManagementSample (Phone) sample project. It is the base class for the MainMenu.cs and OptionsMenu.cs screen objects that are part of the AlienShooter game project.
The MenuScreen class takes advantage of a helper class named MenuEntry, which draws the text and publishes a Selected event. The MenuEntry class does not detect the touch. Instead, the MenuScreen does most of the work to draw, detect a touch, and associate the touch with the correct MenuEntry item. A good way to understand how this works is to look at the constructor for the MainMenuScreen class:
public MainMenuScreen()
: base("Main Menu")
{
// Create our menu entries.
MenuEntry playGameMenuEntry = new MenuEntry("Play Game");
MenuEntry optionsMenuEntry = new MenuEntry("Options");
// Hook up menu event handlers.
playGameMenuEntry.Selected += PlayGameMenuEntrySelected;
optionsMenuEntry.Selected += OptionsMenuEntrySelected;
// Add entries to the menu.
MenuEntries.Add(playGameMenuEntry);
MenuEntries.Add(optionsMenuEntry);
}
The MainMenuScreen class creates the menu entries and associates event handlers with the menu entries. The base class MenuScreen handles the animation and positioning. A developer can customize the animation and layout by modifying the base MenuScreen class.
One way to customize the menu screens is to change the Font in the AlienShooterContent project. Open the /GameManagement/menufont.spritefont file and change the FontName element to Quartz MS from Figure 6 and change the Size to 24. The font has a science fiction look to it, which suites a game named AlienShooter pretty well.
We also modify the GamePlayScreen class to have Score and Lives text across the top with a SlateBlue color background. This is achieved via the SpriteBatch.DrawString method as well as adjusting the background color to SlateBlue and drawing the backgroundTexture 34 pixels lower via the added backgroundPosition Vector2 object. Figure 6 shows both the updated menu screen and game play screen.
When you run the project on a device, the
animations and transitions look pretty nice. These can certainly be
customized as well once you understand how to create animations and
transitions in XNA Game Studio. We cover both topics in detail to create
the actual game play.