Chapter 8. Putting It All Together
13Feb2013

Ch 8 code
Section 8.1. Drawing 2D Text
Section 8.2. Randomly Generating Different Sprite Types
Section 8.3. Adding Some Variety to Your Sprites
Section 8.4. Adding a Background Image
Section 8.5. Game Scoring
Section 8.6. Game States
Section 8.7. Enabling/Disabling GameComponents
Section 8.8. Game-Over Logic and the Game-Over Screen
Section 8.9. Fine-Tuning Gameplay
Section 8.10. Creating Power-Ups
Section 8.11. What You Just Did
Section 8.12. Summary
Section 8.13. Test Your Knowledge: Quiz
Section 8.14. Test Your Knowledge: Exercise
There are a variety of finishing touches to implement in the code. From Chapter 7, you created sprites with different capabilities (intelligences?): the player sprite which moved freely, avoiding sprites that would change direction, chasing sprites that would follow the player sprite to do harm. This chapter adds scoring (and events to trigger), draws text on the screen with Sprite Fonts, has different look and sound for different sprites and your own background image. To complete, we add a power-up to the game along with examining the logic of game states

Section 8.1. Drawing 2D Text

First, you’ll need to add an integer variable representing a sprite’s score value to the Sprite base class (note the addition of the public get accessor as well, via autoimplemented properties):
public int scoreValue {get; protected set;}
Modify both constructors in the Sprite class to receive an integer value for the score value for that sprite. The first constructor should pass the value to the second constructor, and the second constructor should use that value to set the scoreValue member variable.

public Sprite(Texture2D textureImage, Vector2 position, Point frameSize,
	int collisionOffset, Point currentFrame, Point sheetSize, Vector2 speed,
	string collisionCueName, int scoreValue)
	: this(textureImage, position, frameSize, collisionOffset, currentFrame,
	sheetSize, speed, defaultMillisecondsPerFrame, collisionCueName,
	scoreValue)
{
}
public Sprite(Texture2D textureImage, Vector2 position, Point frameSize,
	int collisionOffset, Point currentFrame, Point sheetSize, Vector2 speed,
	int millisecondsPerFrame, string collisionCueName, int scoreValue)
{
	this.textureImage = textureImage;
	this.position = position;
	this.frameSize = frameSize;
	this.collisionOffset = collisionOffset;
	this.currentFrame = currentFrame;
	this.sheetSize = sheetSize;
	this.speed = speed;
	this.collisionCueName = collisionCueName;
	this.millisecondsPerFrame = millisecondsPerFrame;
	this.scoreValue = scoreValue;
}

You’ll also have to change the constructors for the derived classes (AutomatedSprite, ChasingSprite, EvadingSprite, and UserControlledSprite) to accept an integer parameter for the score value and to pass that value on to the base class constructor.

The UserControlledSprite will not have a score value associated with it, because the player can’t be awarded points for avoiding himself. Therefore, you won’t need to add a new parameter to the constructor for this class, but you will need to pass a 0 to the score Value parameter of the constructors for the base class.

Finally, in the SpriteManager class, you’ll need to add the score value as the final parameter in the constructor when initializing new Sprite objects. You’re currently creating objects only of type EvadingSprite, and you’re doing this at the end of the SpawnEnemy method. Add a zero as the score value for the EvadingSprites you’re creating. (You’ll be adding some logic later in this chapter that will create different types of sprites and assign different score values to those sprites based on their types.)

You now have a way to calculate the score during the game based on events with different sprites. Even though you’re currently only using zeros as the score values, the underlying code is now there, so you can start to write some scoring logic for the game. First, you’ll need to add to the Game1 class a variable that represents the total score of the current game:

int currentScore = 0;
Now you’re ready to draw the score on the screen. Drawing text in 2D is done in a very similar manner to the way that you draw sprites. For every frame that is drawn, you will draw text on that frame using a SpriteBatch and an object called a SpriteFont. When drawing 2D images on the screen, you specify an image file to use and a Texture2D object to hold that image. XNA then takes the image from memory and sends the data to the graphics card.

The same thing happens when drawing with SpriteFont objects. In this case, a spritefont file is created. This is an XML file defining the characteristics of a given font: font family, font size, font spacing, etc. A SpriteFont object is used in memory to represent the spritefont. When the SpriteFont object is drawn, XNA will build a 2D image using the text you want to draw and the font specified in the XML file. The image is then sent to the graphics device to be drawn on the screen.

Sprite.cs
AutomatedSprite.cs
ChasingSprite.cs
EvadingSprite.cs
UserControlledSprite.cs

Change the constructors for the derived classes to accept an integer parameter for the score value and to pass that value on to the base class constructor.

SpriteManager.cs

Constructors for the ChasingSprite class
public ChasingSprite(Texture2D textureImage, Vector2 position,
    Point frameSize, int collisionOffset, Point currentFrame,
    Point sheetSize, Vector2 speed, string collisionCueName,
    SpriteManager spriteManager, int scoreValue)
    : base(textureImage, position, frameSize, collisionOffset,
    currentFrame, sheetSize, speed, collisionCueName, scoreValue)
{
    this.spriteManager = spriteManager;
}
public ChasingSprite(Texture2D textureImage, Vector2 position,
    Point frameSize, int collisionOffset, Point currentFrame,
    Point sheetSize, Vector2 speed, int millisecondsPerFrame,
    string collisionCueName, SpriteManager spriteManager,
    int scoreValue)
    : base(textureImage, position, frameSize, collisionOffset,
    currentFrame, sheetSize, speed, millisecondsPerFrame,
    collisionCueName, scoreValue)
{
    this.spriteManager = spriteManager;
}

Finally, in the SpriteManager class, you’ll need to add the score value as the final parameter in the constructor when initializing new Sprite objects. You’re currently creating objects only of type EvadingSprite, and you’re doing this at the end of the SpawnEnemy method. Add a zero as the score value for the EvadingSprites you’re creating. (You’ll be adding some logic later in this chapter that will create different types of sprites and assign different score values to those sprites based on their types.) The code that creates your EvadingSprite objects in the SpawnEnemy method should now look like this:

spriteList.Add(
    new EvadingSprite (Game.Content.Load(@"images\skullball"),
    position, new Point(75, 75), 10, new Point(0, 0),
    new Point(6, 8), speed, "skullcollision", this, .75f, 150, 0));

You now have a way to calculate the score during the game based on events with different sprites. Even though you’re currently only using zeros as the score values, the underlying code is now there, so you can start to write some scoring logic for the game.

First, you’ll need to add to the Game1 class a variable that represents the total score of the current game: Game1.cs int currentScore = 0;

Now we can draw the score on the screen. This parallels how we drew sprites. For every frame that is drawn, you will draw text on that frame using a SpriteBatch and an object called a SpriteFont.

A sprite-font file is created. This is an XML file defining the characteristics of a given font: font family, font size, font spacing, etc. A SpriteFont object is used in memory to represent the spritefont. When the SpriteFont object is drawn, XNA will build a 2D image using the text you want to draw and the font specified in the XML file. The image is then sent to the graphics device to be drawn on the screen.
When drawing the game score, you’ll want to use the value of the currentScore variable. As the variable’s value changes, the score on the screen will be updated. To draw text on the screen using a SpriteFont, you need to add a SpriteFont resource to your project.

Note: Spritefonts are resources that are picked up and processed by the content pipeline. As such, they must be created within the AnimatedSpritesContent project of your project.


By default, XNA 4.0 uses a font named “Kootenay” when creating a new spritefont. You can change the type of font used in your spritefont by editing the file and changing the name of the font found in the line of code that reads Kootenay. It is recommended that you select a font from the Redistributable Font Pack.
xbox.create.msdn.com/en-US/contentpack/fontpack
Note: The fonts in this pack are included automatically as part of installing XNA Game Studio 4.0. If you are using XNA Game Studio 4.0, you do not need to download this pack. The pack is provided for the benefit of users of XNA Game Studio 3.0 and 3.1.
redistfonts.png Click to see them all
When the time comes to share your game with others, it will be important to have a "redistributable" version.

Section 8.2. Randomly Generating Different Sprite Types

To randomly generate sprites of different types, you first need to determine the likeli- hood that each type will be created. Most of the sprites in this game will be Automated Sprite s. ChasingSprite s will be the next most common, and EvadingSprite s will show up only occasionally. In this section, you'll be assigning a percentage likelihood to each type of sprite.

In SpriteManager we distribute: 75+20+5=100%

int likelihoodAutomated = 75; 
int likelihoodChasing = 20; 
int likelihoodEvading = 5;

Now you have to add some code that will generate a random number and, based on the value of that random number, create one of the three sprite types.

Section 8.3. Adding Some Variety to Your Sprites

Within this chapter’s source code (in the AnimatedSprites\AnimatedSpritesContent\Images folder), you’ll find some sprite sheet files for the different types of sprites.

Following the lengthy instructions pp. 133-139 in our textbook to include the 6 sprites and their sounds.

boltcollision.wav
fourbladescollision.wav
pluscollision.wav
threebladescollision.wav





Section 8.4. Adding a Background Image

With the source code for this chapter (again, in the AnimatedSprites\AnimatedSpritesContent\Images folder), you will find an image named background.jpg. Add the image to the project the same way you added the other images (right-click the AnimatedSpritesContent\Images folder, select Add->Existing Itemc..., and navigate to the background.jpg image included with the source code).

Your SpriteManager class was built to handle animated sprites and derived classes. Something as simple as a background image can just be added to your Game1 class. You’ll need to add a Texture2D variable for the image:

Texture2D backgroundTexture;
and load the Texture2D image in the LoadContent method:
backgroundTexture = Content.Load(@"Images\background");
Next, you’ll need to add the code to draw the image. Because you’ll now have multiple sprites being drawn within your Game1 class (the SpriteFont counts as a sprite, so you’ll be drawing a score sprite as well as a background sprite), you need to make sure that the score text is always on top of the background image. Typically, when trying to ensure that one sprite is on top of another, you modify the SpriteBatch.Begin call to include an appropriate SpriteSortMode. However, this is a case where you’re drawing only two items, and you know that you’ll always want to draw the score on top of the background. As such, you can forego the overhead involved in specifying a sort mode in the Begin method, and instead always draw the background first and then the score.

Section 8.5. Game Scoring

The first thing you need to do is determine what event(s) will trigger a change in score. For this game, you’ll be updating the score whenever the user successfully avoids a three-blade, four-blade, skull ball, or plus sprite. You actually have already added the logic to determine when one of those sprites has been successfully avoided; it lies in the code that deletes the sprites when they disappear off the edge of the screen. If a sprite makes it across the screen and needs to be deleted, that means the user has avoided that sprite, and if it was a three-blade, four-blade, skull ball, or plus sprite, you need to give some points to the user.

Read pp. 140-147 for the details in handling the scoring.

Section 8.6. Game States

Your game is coming along, but there has to be a way to end the game. Typically, when a game ends, the game window doesn’t just disappear; usually there’s some kind of game-over screen that displays your score or at least lets you know that you’ve failed (or succeeded) in your mission. That’s what you need to add next. While you’re at it, it’s also common to have the same kind of thing at the beginning of the game (perhaps a menu enabling the player to select options, or at least a splash screen presenting instructions and maybe displaying your name as the author of this great game).

Regardless of the specifics, the game moves through different states, and in those different states the game behaves differently. One way to implement splash screens and game-over screens is by making use of these states.

To define some states for your game, you’ll need to enumerate the different possible states that the game can have. Create an enum variable at the class level in your Game1 class. Currently, you have only three states in your game: Start (where you display your splash screen), InGame (where the game is actually running), and GameOver (where you’ll display your game over screen). You’ll also need to create a variable of that enum type that will hold the current state of the game. You’ll want to initialize that current state variable to the game state representing the start of the game:

enum GameState { Start, InGame, GameOver };
GameState currentGameState = GameState.Start;
You’re going to want to separate the logic in the Update and Draw methods to allow you to write specific code that will run only in certain situations, depending on the current state of the game. You can do this by adding a switch statement to both methods with different case statements for each possible game state. Then, when you want to write specific code to update or draw items that should take place only in a given game state, you add that code to the Update or Draw methods within the case for that particular game state.

In Update method of Game1 class:

protected override void Update(GameTime gameTime)
{
// Only perform certain actions based on
// the current game state
switch (currentGameState)
{
case GameState.Start:
break;
case GameState.InGame:
break;
case GameState.GameOver:
break;
}
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit( );
audioEngine.Update( );
base.Update(gameTime);
}

Also modify Draw

Section 8.7. Enabling/Disabling GameComponents

There are two properties that can be used to enable and disable a GameComponent. The Enabled property of a GameComponent will determine whether its Update method is called when the game’s own Update method is called. Likewise, the Visible property of a DrawableGameComponent will determine whether its Draw method is called when the game’s Draw method is called. Both of these properties are set to true by default. Go to the Initialize method in your Game1 class and set both properties to false immediately after adding the component to your list of game components (added lines are in bold):
spriteManager = new SpriteManager(this);
Components.Add(spriteManager);
spriteManager.Enabled = false;
spriteManager.Visible = false;

Section 8.8. Game-Over Logic and the Game-Over Screen



Section 8.9. Fine-Tuning Gameplay


Section 8.10. Creating Power-Ups

You have three sprites that don’t do anything at this point: the skull ball, the plus, and the bolt. These sprites are meant not to take away a player’s life when they collide with the player’s object, but rather to have some positive or negative effect on the player.

For the purposes of this book, the effects that these objects will have when they collide with the player object are as follows:
• The bolt causes the player object to move at 200% of its current speed for 5 seconds.
• The skull ball causes the player object to move at 50% of its current speed for 5 seconds.
• The plus causes the player object to be 200% larger than its current size for 5 seconds

Section 8.11. What You Just Did

Wow. Maybe this should say, “What didn’t you do?” This was a long chapter, but you
did some great stuff. Let’s take a look:
• You learned how to draw 2D text on the screen.
• You randomly generated sprites of different types.
• You added a background image.
• You fleshed out a system to keep score.
• You experimented with game states and implemented three states (start, in-game, and end).
• You added splash and game-over screens using game states.
• You added power-up effects and fine-tuning logic to your game

Section 8.12. Summary

• 2D fonts are drawn on the screen just the same as any Texture2D object.
• 2D fonts are built using the SpriteFont object.
• Background images are added to games by using a sprite that covers the entire screen.
• Game states are breaks in gameplay or transitions in gameplay from one state to another (for example, moving from one level to another, accomplishing a mission, losing the game, or something similar in concept).
• Game development is a very creative business. While the mechanics are very scientific, as in all programming, the actual fine-tuning of the game is a very artoriented craft that is heavily centered on how the gameplay feels to a user.
As a developer, you should play with your game as early and often as possible so that you can tweak the experience into something that you enjoy and that you envision others will enjoy as well.
• “Our mental discipline is matched only by our skill in XNA…I only hope these are enough to withstand this awful trial.” —Akara

Section 8.13. Test Your Knowledge: Quiz

1. What type of object is used to draw 2D text in XNA?
2. How is a background image different from an image used to represent a player or object in the game?
3. What are game states and how are they used?
4. In the Flight of the Conchords episode “Mugged,” what do the muggers steal from Jemaine?

Section 8.14. Test Your Knowledge: Exercise

Change the behavior of the skull power-up (or power-down, if you prefer) to freeze the player for 2 seconds rather than reduce the player’s speed by 50% for 5 seconds. Use different power-up timers for the skull, bolt, and plus sprites.