Appendix A. Answers to Quizzes and Exercises

Chapter 1: What’s New in XNA 4.0?

Quiz Answers

  1. What significant change was made to the XNA folder structure in XNA 4.0?

    The Content node within your solution, which was previously a subfolder of your game project, is now its own project within your solution. This enables you to easily share content across multiplatform projects.

  2. What game platforms are supported with XNA 4.0?

    XNA 4.0 supports development on Windows, Xbox 360, and Windows Phone 7 series.

  3. What is the difference between the Reach and HiDef profiles in XNA 4.0?

    HiDef is designed for high-powered, top-of-the-line hardware, whereas Reach is designed to support a wider range of hardware devices. The Reach profile offers a limited set of graphic features and is a subset of the HiDef profile.

  4. Why do the Japanese tourists end up sleeping in a chest of drawers in Kramer’s apartment?

    Because Kramer spent all their money, thinking that fifty-thousand yen was a huge sum of money they had at their disposal:

    Elaine: Fifty-thousand yen? Isn’t that only a few hundred dollars?

    Kramer: Evidently. Oh, by the way, tell Brett that his chest of drawers are a big hit. My guests are very comfortable in them.

    Elaine: In them?!

    Jerry: You have them sleeping in drawers?!

    Kramer: Jerry, have you ever seen the business hotels in Tokyo? They sleep in tiny stacked cubicles all the time. They feel right at home.

    Jerry: This has “international incident” written all over it.

    Kramer: Oh yeah, yeah.

Chapter 2: Getting Started

Quiz Answers

  1. XNA Game Studio 4.0 allows you to write games for which platforms?

    XNA 4.0 supports development for Windows Vista, Windows 7, the Xbox 360, and Windows Phone 7.

  2. Which versions of Visual Studio support XNA Game Studio 4.0?

    Either Visual Studio 2010 Standard Edition or higher (with C# language support installed) or Visual C# 2010 Express Edition.

Chapter 3: Fun with Sprites

Quiz Answers

  1. What are the steps in an XNA game loop?

    The XNA game loop consists of only two methods: Update and Draw.

  2. If you wanted to load a Texture2D object, in which method should you do that?

    LoadContent.

  3. What line of code should you use to change the framerate of an XNA game to 20 fps?

    Either of these lines will do the trick:

    TargetElapsedTime = TimeSpan.FromMilliseconds(50);
    TargetElapsedTime = new TimeSpan(0, 0, 0, 0, 50);
  4. What should you pass in as the parameter of Content.Load when loading a Texture2D object?

    The asset name of the texture image you want to load. You can find an image’s asset name by viewing its properties in Solution Explorer.

  5. Fact or fiction: the content pipeline will let you know at compile time if you add an image to your project that it cannot parse.

    Fact. The content pipeline runs a compilation step on all content (textures, models, sounds, etc.) and then outputs the result as an XNA-compatible formatted object. If this step fails during compilation, the result is a compilation error.

  1. You’re drawing a sprite, and you want the background to be transparent. What steps do you need to take to draw it with a transparent background?

    There are two ways to draw transparent images in XNA. The first option has two requirements. First, the background of your image itself must be transparent. If it isn’t, you’ll need to edit the image in an image editor and give it a transparent background. Second, BlendState.AlphaBlend must be used (this is the default if no parameters are specified in SpriteBatch.Begin).

    The second option is to make sure that the transparent portion of the image is solid magenta (255, 0, 255).

  2. You have two sprites (A and B), and when they collide, you always want A to be drawn on top of B. What do you need to do?

    There are two possible solutions: using SpriteSortMode.FrontToBack, you can set the layer depth of A to a value greater than that of B (both must be within the range of 0 to 1); or, using SpriteSortMode.BackToFront, you can set the layer depth of A to a value lower than that of B.

  3. What are the things you need to keep track of to cycle through a sprite sheet?

    Current frame to draw, size of each individual frame, and number of columns and rows in your sprite sheet.

  4. What was the first television series to command more than $1 million per minute for advertising?

    Only the best show in this history of television: Seinfeld.

Exercise Answer

In this chapter, you built an example where two XNA logo images moved around the screen and bounced off the edges. Take the animated sprite example that you built at the end of this chapter and make the animated sprite move and bounce in a similar fashion—but in this case, make the animated sprite move in both X and Y directions and bounce off of all four edges of the screen.

This exercise is fairly straightforward, as it involves taking two examples from the chapter and merging pieces together. The animation aspect of the exercise is covered at the end of the chapter, and the movement and bouncing off screen edges is covered in the middle of the chapter.

One thing that might be tricky is moving in both X and Y directions at the same time. In the previous moving example, you used a float variable for the speed and added it to either the X or the Y coordinate, depending on which sprite you were moving (they both moved in only one direction).

Now I’d recommend using a Vector2 to represent speed. That way you can have different values for speed in the X direction and the Y direction. Also, you can add your Vector2 speed to your Vector2 position by simply adding the two together (you can add Vector2 objects just as you would add integer or float values).

The other catch to this exercise is that in the previous moving example you used the size of the Texture2D object to determine how far from the edge of the screen to bounce in the other direction, but now the size of your Texture2D object will be inaccurate because you’re using a sprite sheet. Instead, you’ll want to use the size of an individual frame within that sprite sheet to detect when the image has hit the edge of the screen.

Here is one possible solution for this exercise:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace AnimatedSprites
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D texture;
        Point frameSize = new Point(75, 75);
        Point currentFrame = new Point(0, 0);
        Point sheetSize = new Point(6, 8);

        //Framerate stuff
        int timeSinceLastFrame = 0;
        int millisecondsPerFrame = 16;

        //Speed and movement
        Vector2 speed = new Vector2(5, 2);
        Vector2 position = Vector2.Zero;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            texture = Content.Load<Texture2D>(@"Images	hreerings");
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non-ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
              ButtonState.Pressed)
                this.Exit();

            //Update time since last frame and only
            //change animation if framerate expired
            timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
            if (timeSinceLastFrame > millisecondsPerFrame)
            {
                timeSinceLastFrame -= millisecondsPerFrame;

                ++currentFrame.X;
                if (currentFrame.X >= sheetSize.X)
                {
                    currentFrame.X = 0;
                    ++currentFrame.Y;
                    if (currentFrame.Y >= sheetSize.Y)
                        currentFrame.Y = 0;
                }
            }

            //Move sprite
            position += speed;

            //If the sprite hit a wall, reverse direction
            if (position.X > Window.ClientBounds.Width - frameSize.X ||
                position.X < 0)
                speed.X *= -1;
            if (position.Y > Window.ClientBounds.Height - frameSize.Y ||
                position.Y < 0)
                speed.Y *= -1;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);

            spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);

            spriteBatch.Draw(texture, position,
                new Rectangle(currentFrame.X * frameSize.X,
                    currentFrame.Y * frameSize.Y,
                    frameSize.X,
                    frameSize.Y),
                    Color.White, 0, Vector2.Zero,
                    1, SpriteEffects.None, 0);

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Chapter 4: User Input and Collision Detection

Quiz Answers

  1. What object is used to read input from a mouse?

    Mouse.

  2. Fact or fiction: the X and Y coordinates from a mouse as read in an XNA application represent how much the mouse has moved since the previous frame.

    Fiction. The input is given in actual screen coordinates based off the upper-left corner of the game window. To get the distance moved since the previous frame, you have to compare the value from the current frame against the value of the previous frame.

  3. What is the difference between an analog input control and a digital input control?

    A digital input yields a Boolean yes/no, on/off status, whereas an analog input gives a range of inputs based on how far the button has been pressed.

  4. Describe the bounding-box collision-detection algorithm.

    The bounding-box algorithm is a simple collision-detection algorithm in which you “draw” imaginary boxes around objects and then run collision checks on the boxes themselves to see whether any objects are colliding.

  5. Describe the pros and cons of the bounding-box collision-detection algorithm.

    The two biggest pros of the algorithm are its speed and simplicity. The biggest drawback is its inherent inaccuracy—not all objects are square or rectangular, and as such, the algorithm has accuracy issues.

  6. What is the ratio of unicorns to leprechauns?

    According to Stanley Hudson of The Office, the ratio of unicorns to leprechauns equals the ratio of Stanley nickels to Schrute bucks.

    Dwight: Don’t you want to earn Schrute bucks?

    Stanley: No. In fact, I’ll give you a billion Stanley nickels if you never talk to me again.

    Dwight: What’s the ratio of Stanley nickels to Schrute bucks?

    Stanley: Same as the ratio of unicorns to leprechauns.

Exercise Answer

Let’s combine some aspects of this chapter and the previous one. Take the code where we left off at the end of this chapter and modify it to include another nonuser-controlled sprite (use the plus.png image, which is located with the source code for this chapter in the AnimatedSpritesAnimatedSpritesAnimatedSpritesContentImages folder). Add movement to both nonuser-controlled sprites, as you did in Chapter 3, so that each sprite moves in the X and Y directions and bounces off the edges of the screen. Add collision detection to the newly added sprite as well. The end result will be a game where you try to avoid two moving sprites. When you hit either sprite, the game ends.

For clarity in working with the plus.png image, the frame size of the sprite sheet is 75×75 pixels, and it has six columns and four rows (note that the rings and skull ball sprite sheets both had six columns and eight rows).

This exercise takes principles from Chapters 3 and 4 and combines them to create a very basic game where you try to avoid two sprites that move around the screen. The addition of a new animated sprite is a bit of a challenge, especially the way the code is currently written (in Chapter 5 you learned how to fine-tune the object-oriented design of the system you’re building). Other than that, collision detection and object movement and edge bouncing are handled the same way as in previous examples and should be fairly straightforward at this point. Here’s some sample code for this exercise:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace AnimatedSprites
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        //Rings variables
        Texture2D ringsTexture;
        Point ringsFrameSize = new Point(75, 75);
        Point ringsCurrentFrame = new Point(0, 0);
        Point ringsSheetSize = new Point(6, 8);
        int ringsTimeSinceLastFrame = 0;
        int ringsMillisecondsPerFrame = 50;


        //Skull variables
        Texture2D skullTexture;
        Point skullFrameSize = new Point(75, 75);
        Point skullCurrentFrame = new Point(0, 0);
        Point skullSheetSize = new Point(6, 8);
        int skullTimeSinceLastFrame = 0;
        const int skullMillisecondsPerFrame = 50;

        //Plus variables
        Texture2D plusTexture;
        Point plusFrameSize = new Point(75, 75);
        Point plusCurrentFrame = new Point(0, 0);
        Point plusSheetSize = new Point(6, 4);
        int plusTimeSinceLastFrame = 0;
        const int plusMillisecondsPerFrame = 50;

        //Rings movement
        Vector2 ringsPosition = Vector2.Zero;
        const float ringsSpeed = 6;
        MouseState prevMouseState;

        //Skull position
        Vector2 skullPosition = new Vector2(100, 100);
        Vector2 skullSpeed = new Vector2(4, 2);

        //Plus position
        Vector2 plusPosition = new Vector2(200, 200);
        Vector2 plusSpeed = new Vector2(2, 5);

        //Collision detection variables
        int ringsCollisionRectOffset = 10;
        int skullCollisionRectOffset = 10;
        int plusCollisionRectOffset = 10;


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures
            spriteBatch = new SpriteBatch(GraphicsDevice);

            ringsTexture = Content.Load<Texture2D>(@"images 	hreerings");
            skullTexture = Content.Load<Texture2D>(@"imagesskullball");
            plusTexture = Content.Load<Texture2D>(@"imagesplus");
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non-ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
              ButtonState.Pressed)
                this.Exit();

            //Update time since last frame and only
            //change animation if framerate expired
            ringsTimeSinceLastFrame +=
                gameTime.ElapsedGameTime.Milliseconds;
            if (ringsTimeSinceLastFrame > ringsMillisecondsPerFrame)
            {
                ringsTimeSinceLastFrame -= ringsMillisecondsPerFrame;

                ++ringsCurrentFrame.X;
                if (ringsCurrentFrame.X >= ringsSheetSize.X)
                {
                    ringsCurrentFrame.X = 0;
                    ++ringsCurrentFrame.Y;
                    if (ringsCurrentFrame.Y >= ringsSheetSize.Y)
                        ringsCurrentFrame.Y = 0;
                }
            }

            //Then do the same to update the skull animation
            skullTimeSinceLastFrame +=
                gameTime.ElapsedGameTime.Milliseconds;
            if (skullTimeSinceLastFrame > skullMillisecondsPerFrame)
            {
                skullTimeSinceLastFrame -= skullMillisecondsPerFrame;

                ++skullCurrentFrame.X;
                if (skullCurrentFrame.X >= skullSheetSize.X)                {
                    skullCurrentFrame.X = 0;
                    ++skullCurrentFrame.Y;
                    if (skullCurrentFrame.Y >= skullSheetSize.Y)
                        skullCurrentFrame.Y = 0;
                }
            }

            //Then do the same to update the plus animation
            plusTimeSinceLastFrame +=
                gameTime.ElapsedGameTime.Milliseconds;
            if (plusTimeSinceLastFrame > plusMillisecondsPerFrame)
            {
                plusTimeSinceLastFrame -= plusMillisecondsPerFrame;

                ++plusCurrentFrame.X;
                if (plusCurrentFrame.X >= plusSheetSize.X)
                {
                    plusCurrentFrame.X = 0;
                    ++plusCurrentFrame.Y;
                    if (plusCurrentFrame.Y >= plusSheetSize.Y)
                        plusCurrentFrame.Y = 0;
                }
            }

            //Move position of rings based on keyboard input
            KeyboardState keyboardState = Keyboard.GetState();
            if (keyboardState.IsKeyDown(Keys.Left))
                ringsPosition.X -= ringsSpeed;
            if (keyboardState.IsKeyDown(Keys.Right))
                ringsPosition.X += ringsSpeed;
            if (keyboardState.IsKeyDown(Keys.Up))
                ringsPosition.Y -= ringsSpeed;
            if (keyboardState.IsKeyDown(Keys.Down))
                ringsPosition.Y += ringsSpeed;

            //Move the skull
            skullPosition += skullSpeed;
            if (skullPosition.X >
                Window.ClientBounds.Width - skullFrameSize.X ||
                skullPosition.X < 0)
                skullSpeed.X *= -1;
            if (skullPosition.Y >
                Window.ClientBounds.Height - skullFrameSize.Y ||
                skullPosition.Y < 0)
                skullSpeed.Y *= -1;

            //Move the plus
            plusPosition += plusSpeed;
            if (plusPosition.X >
                Window.ClientBounds.Width - plusFrameSize.X ||
                plusPosition.X < 0)
                plusSpeed.X *= -1;            if (plusPosition.Y >
                Window.ClientBounds.Height - plusFrameSize.Y ||
                plusPosition.Y < 0)
                plusSpeed.Y *= -1;

            //Move rings based on mouse movement
            MouseState mouseState = Mouse.GetState();
            if (mouseState.X != prevMouseState.X ||
            mouseState.Y != prevMouseState.Y)
                ringsPosition = new Vector2(mouseState.X, mouseState.Y);
            prevMouseState = mouseState;

            //Move rings based on gamepad input
            GamePadState gamepadState = GamePad.GetState(PlayerIndex.One);
            if (gamepadState.Buttons.A == ButtonState.Pressed)
            {
                //A is pressed, double speed and vibrate
                ringsPosition.X +=
                    ringsSpeed * 2 * gamepadState.ThumbSticks.Left.X;
                ringsPosition.Y -=
                    ringsSpeed * 2 * gamepadState.ThumbSticks.Left.Y;
                GamePad.SetVibration(PlayerIndex.One, 1f, 1f);
            }
            else
            {
                //A is not pressed, normal speed and stop vibration
                ringsPosition.X += ringsSpeed * gamepadState.ThumbSticks.Left.X;
                ringsPosition.Y -= ringsSpeed * gamepadState.ThumbSticks.Left.Y;
                GamePad.SetVibration(PlayerIndex.One, 0, 0);
            }

            //Adjust position of rings to keep it in the game window
            if (ringsPosition.X < 0)
                ringsPosition.X = 0;
            if (ringsPosition.Y < 0)
                ringsPosition.Y = 0;
            if (ringsPosition.X >
                Window.ClientBounds.Width - ringsFrameSize.X)
                ringsPosition.X =
                    Window.ClientBounds.Width - ringsFrameSize.X;
            if (ringsPosition.Y >
                Window.ClientBounds.Height - ringsFrameSize.Y)
                ringsPosition.Y =
                    Window.ClientBounds.Height - ringsFrameSize.Y;

            //If objects collide, exit the game
            if (Collide())
                Exit();

            base.Update(gameTime);
        }

        protected bool Collide()
        {
            Rectangle ringsRect = new Rectangle(
                (int)ringsPosition.X + ringsCollisionRectOffset,
                (int)ringsPosition.Y + ringsCollisionRectOffset,
                ringsFrameSize.X - (ringsCollisionRectOffset * 2),
                ringsFrameSize.Y - (ringsCollisionRectOffset * 2));

            Rectangle skullRect = new Rectangle(
                (int)skullPosition.X + skullCollisionRectOffset,
                (int)skullPosition.Y + skullCollisionRectOffset,
                skullFrameSize.X - (skullCollisionRectOffset * 2),
                skullFrameSize.Y - (skullCollisionRectOffset * 2));

            Rectangle plusRect = new Rectangle(
                (int)plusPosition.X + plusCollisionRectOffset,
                (int)plusPosition.Y + plusCollisionRectOffset,
                plusFrameSize.X - (plusCollisionRectOffset * 2),
                plusFrameSize.Y - (plusCollisionRectOffset * 2));

            return ringsRect.Intersects(skullRect) ||
                ringsRect.Intersects(plusRect);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);

            spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);


            //Draw the rings
            spriteBatch.Draw(ringsTexture, ringsPosition,
                new Rectangle(ringsCurrentFrame.X * ringsFrameSize.X,
                    ringsCurrentFrame.Y * ringsFrameSize.Y,
                    ringsFrameSize.X,
                    ringsFrameSize.Y),
                    Color.White, 0, Vector2.Zero,
                    1, SpriteEffects.None, 0);

            //Draw the skull
            spriteBatch.Draw(skullTexture, skullPosition,
                new Rectangle(skullCurrentFrame.X * skullFrameSize.X,
                    skullCurrentFrame.Y * skullFrameSize.Y,
                    skullFrameSize.X,
                    skullFrameSize.Y),
                    Color.White, 0, Vector2.Zero,
                    1, SpriteEffects.None, 0);

            //Draw the plus
            spriteBatch.Draw(plusTexture, plusPosition,
                new Rectangle(plusCurrentFrame.X * plusFrameSize.X,
                    plusCurrentFrame.Y * plusFrameSize.Y,
                    plusFrameSize.X,
                    plusFrameSize.Y),
                    Color.White, 0, Vector2.Zero,
                    1, SpriteEffects.None, 0);

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Chapter 5: Applying Some Object-Oriented Design

Quiz Answers

  1. What class does a game component derive from?

    GameComponent.

  2. If you want to be able to draw on the screen with your game component, what class do you need to derive from?

    DrawableGameComponent.

  3. Fact or fiction: time spent building a solid object-oriented design should not count as time spent developing software, because it is unnecessary and superfluous.

    Absolutely fiction. Creating a proper design up front will help you avoid countless headaches and maintenance issues down the road. Always, no matter what the project, plan ahead and code around a solid design.

  4. What is spontaneous dental hydroplosion?

    As explained by Pam and Jim in The Office episode “Health Care”, spontaneous dental hydroplosion occurs when a person’s teeth turn to liquid and proceed to drip down the back of their throat.

Exercise Answer

Modify the code that you worked on this chapter to create four sprites which move and bounce off all four edges of the screen. To accomplish this, create a new class called BouncingSprite that derives from AutomatedSprite. BouncingSprite should do the same thing that AutomatedSprite does, with the exception that it will check during the Update method to determine whether the sprite has gone off the edge of the screen. If it has, reverse the direction of the sprite by multiplying the speed variable by −1.

Also, make two of the bouncing sprites use the skull image and two of them use the plus image (located with the source code for this chapter in the AnimatedSpritesAnimatedSpritesAnimatedSpritesContentImages directory).

Note that when running this game after making these changes, you’ll have four sprites moving around the screen and the game will exit when any of them collide with the user-controlled sprite. This could cause some issues in testing the game because the sprites may be colliding when the game first loads. Try moving your mouse to a far corner of the screen when loading the game to make sure your user-controlled sprite is out of the way to begin with.

Creating a bouncing sprite should be fairly straightforward at this point. You’ve already created a sprite that bounces off the edges of the game window in a previous chapter (more than once if you did the exercises for the previous chapters). All you’ll need to do is check in the Update method whether the sprite has gone off the edge of the game window and, if it has, reverse its direction. You’ll then need to update the code in your SpriteManager class that creates the automated sprites to have it create BouncingSprites with both the skullball and plus sprites.

Here’s the BouncingSprite class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace AnimatedSprites
{
    class BouncingSprite: AutomatedSprite
    {
        public BouncingSprite(Texture2D textureImage, Vector2 position,
            Point frameSize, int collisionOffset, Point currentFrame,
            Point sheetSize, Vector2 speed)
            : base(textureImage, position, frameSize, collisionOffset,
            currentFrame, sheetSize, speed)
        {
        }
        public BouncingSprite(Texture2D textureImage, Vector2 position,
            Point frameSize, int collisionOffset, Point currentFrame,
            Point sheetSize, Vector2 speed, int millisecondsPerFrame)
            : base(textureImage, position, frameSize, collisionOffset,
            currentFrame, sheetSize, speed, millisecondsPerFrame)
        {
        }

        public override void Update(GameTime gameTime, Rectangle clientBounds)
        {
            position += direction;

            //Reverse direction if hit a side
            if (position.X > clientBounds.Width - frameSize.X ||
                position.X < 0)
                speed.X *= −1;            if (position.Y > clientBounds.Height - frameSize.Y ||
                position.Y < 0)
                speed.Y *= −1;

            base.Update(gameTime, clientBounds);
        }
    }
    }

And you should update the code that creates your automated sprites in your SpriteManager class’s LoadContent method to look something like this:

//Load several different automated sprites into the list
spriteList.Add(new BouncingSprite(
    Game.Content.Load<Texture2D>(@"Images/skullball"),
    new Vector2(150, 150), new Point(75, 75), 10, new Point(0, 0),
    new Point(6, 8), new Vector2(1,1)));
spriteList.Add(new BouncingSprite(
    Game.Content.Load<Texture2D>(@"Images/plus"),
    new Vector2(300, 150), new Point(75, 75), 10, new Point(0, 0),
    new Point(6, 4), new Vector2(1,0)));
spriteList.Add(new BouncingSprite(
    Game.Content.Load<Texture2D>(@"Images/plus"),
    new Vector2(150, 300), new Point(75, 75), 10, new Point(0, 0),
    new Point(6, 4), new Vector2(0,1)));
spriteList.Add(new BouncingSprite(
    Game.Content.Load<Texture2D>(@"Images/skullball"),
    new Vector2(600, 400), new Point(75, 75), 10, new Point(0, 0),
    new Point(6, 8), new Vector2(-1,-1)));

Chapter 6: Sound Effects and Audio

Quiz Answers

  1. What do you use to reference a sound that has been included in an XACT audio file?

    To play a sound in an XACT audio file, you reference the sound by its associated cue name.

  2. What are the pros and cons of using the simple sound API available in XNA 4.0 instead of using XACT?

    Pros: simple and fast, and supported on the Reach game profile. Cons: no design-time modification of sound properties.

  3. Fact or fiction: the only way to get a soundtrack to loop during gameplay is to manually program the sound in code to play over and over.

    Fiction. You can set the looping property of a particular sound in XACT by specifying a certain number of times for the sound to play or by specifying the sound to play in an infinite loop.

  1. Fact or fiction: you can adjust the volume of your sounds using XACT.

    Fact. You can adjust the volume, pitch, and other properties of a sound file using XACT.

  2. How do you pause and restart a sound in XNA when using XACT audio files?

    If you capture the Cue object from the GetCue method and play the sound from the Cue object, you can call Pause, Stop, Play, and other methods on the Cue object to manipulate playback of that particular sound.

  3. What was the best-selling video game of 2009?

    Call of Duty: Modern Warfare 2 was the best-selling game of 2009, selling just under 12 million copies globally.

Exercise Answer

Try experimenting with different sounds and sound settings in XNA using XACT. Find a few .wav files and plug them into the game. Experiment with different settings in XACT by grouping multiple sounds in a single cue.

There’s really no right or wrong answer to this exercise. Follow the steps from earlier in the chapter, add some different sounds, and play with the settings in XACT. It doesn’t need to sound pretty; just use this as a chance to become more familiar with XACT and all that it can do.

Chapter 7: Basic Artificial Intelligence

Quiz Answers

  1. What is the Turing Test?

    Developed by Alan Turing, the Turing Test involved having a human interact with a computer and another human, asking questions to determine which was which. The test was designed to determine whether a computer was intelligent. If the interrogator was unable to determine which was the computer and which was the human, the computer was deemed “intelligent.”

  2. Why is artificial intelligence so difficult to perfect?

    Because intelligence itself is so difficult to define. It’s something that currently is not truly understood and therefore is ambiguous by nature.

  3. What constitutes irrelevancy for an object in a video game? What should be done with irrelevant objects, and why?

    An object is irrelevant if it can no longer affect the game. Irrelevant objects should be deleted because otherwise they will continue to be updated and drawn in each frame, which will negatively affect performance.

  4. If you have a player whose position is stored in a Vector2 object called PlayerPos and a chasing object whose position is stored in a Vector2 object called ChasePos, what algorithm will cause your chasing object to chase after your player?

    if(PlayerPos.X < ChasePos.X)
        --ChasePos.X;
    else
        ++ChasePos.X;
    if(PlayerPos.Y < ChasePos.Y)
        --ChasePos.Y;
    else
        ++ChasePos.Y;
  5. In the beginning, what was created that made a lot of people very angry and has been widely regarded as a bad move?

    According to The Restaurant at the End of the Universe, the second book in Douglas Adams’ hilarious The Hitchhiker’s Guide to the Galaxy series: “In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.”

Exercise Answer

Take what you’ve learned in this chapter and make yet another type of sprite object, one that moves randomly around the screen. To do this, you’ll want to create a random timer that signifies when the object should change directions. When the timer expires, have the object move in a different direction, and then reset the random timer to a new random time at which the object will again shift its direction.

When dealing with a random object, you need to figure out when it will randomly change direction. Ideally, it will randomly change direction at random intervals. The actual values that are used to determine the thresholds of the random variables can be customized to get the desired functionality, but the underlying code is the same. Here is a randomly moving sprite class:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace AnimatedSprites
{
    class RandomSprite : Sprite
    {
        SpriteManager spriteManager;

        //Random variable to determine when to change directions
        int minChangeTime = 500;
        int maxChangeTime = 1000;
        int changeDirectionTimer;
        Random rnd;        public RandomSprite(Texture2D textureImage, Vector2 position,
            Point frameSize, int collisionOffset, Point currentFrame,
            Point sheetSize, Vector2 speed, string collisionCueName,
            SpriteManager spriteManager, Random rnd)
            : base(textureImage, position, frameSize, collisionOffset,
            currentFrame, sheetSize, speed, collisionCueName)
        {
            this.spriteManager = spriteManager;
            this.rnd = rnd;
            ResetTimer();
        }

        public RandomSprite(Texture2D textureImage, Vector2 position,
            Point frameSize, int collisionOffset, Point currentFrame,
            Point sheetSize, Vector2 speed, int millisecondsPerFrame,
            string collisionCueName, SpriteManager spriteManager,
            Random rnd)
            : base(textureImage, position, frameSize, collisionOffset,
            currentFrame, sheetSize, speed, millisecondsPerFrame,
            collisionCueName)
        {
            this.spriteManager = spriteManager;
            this.rnd = rnd;
            ResetTimer();
        }

        public override Vector2 direction
        {
            get { return speed; }
        }

        public override void Update(GameTime gameTime, Rectangle clientBounds)
        {
            //Move forward
            position += speed;
            Vector2 player = spriteManager.GetPlayerPosition();

            //Is it time to change directions?
            changeDirectionTimer −= gameTime.ElapsedGameTime.Milliseconds;
            if (changeDirectionTimer < 0)
            {
                //Pick a new random direction
                float Length = speed.Length();
                speed = new Vector2((float)rnd.NextDouble() - .5f,
                   (float)rnd.NextDouble() - .5f);
                speed.Normalize();
                speed *= Length;

                ResetTimer();
            }

            base.Update(gameTime, clientBounds);
        }

        private void ResetTimer()
        {
            changeDirectionTimer = rnd.Next(
                minChangeTime, maxChangeTime);
        }

    }
}

Chapter 8: Putting It All Together

Quiz Answers

  1. What type of object is used to draw 2D text in XNA?

    You need a SpriteFont object 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?

    It really isn’t any different. The underlying concept is exactly the same: you’re drawing a 2D image on the game window. The background image in this case doesn’t animate, and the file containing the background image contains only that image (in contrast to the player sprites, which are animated; the files used for those objects contain sprite sheets with multiple images forming an animation sequence).

  3. What are game states and how are they used?

    Game states represent a way to indicate the current status of the game as a whole or some activity within the game. Game states are used to transition from splash screens to gameplay, from gameplay to end-game screens, and so on.

  4. In the Flight of the Conchords episode “Mugged,” what do the muggers steal from Jemaine?

    The muggers steal Jemaine’s camera-phone—which was actually a gift to him from Bret and is also really just Jemaine’s phone glued to a camera.

Exercise Answer

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.

There really isn’t anything too complicated involved in adding a new power-up (or power-down), now that you’ve fleshed out the core logic. You already have a built-in way to modify the speed of the player via a power-up (the bolt power-up increases the speed by 100%, and the skull currently slows the player by 50%). To freeze the player, all you’ll need to do is reduce the player’s speed to zero while the power-up is in effect. The other thing to think about is that this power-up will last only two seconds, whereas the others lasted five seconds. So, you’ll need to add a new variable to track when this freeze power-up expires.

The modified SpriteManager class is shown here:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace AnimatedSprites
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class SpriteManager : Microsoft.Xna.Framework.DrawableGameComponent
    {
        // SpriteBatch for drawing
        SpriteBatch spriteBatch;

        // A sprite for the player and a list of automated sprites
        UserControlledSprite player;
        List<Sprite> spriteList = new List<Sprite>();

        // Variables for spawning new enemies
        int enemySpawnMinMilliseconds = 1000;
        int enemySpawnMaxMilliseconds = 2000;
        int enemyMinSpeed = 2;
        int enemyMaxSpeed = 6;
        int nextSpawnTime = 0;

        // Chance of spawning different enemies
        int likelihoodAutomated = 75;
        int likelihoodChasing = 20;
        int likelihoodEvading = 5;

        // Scoring
        int automatedSpritePointValue = 10;
        int chasingSpritePointValue = 20;
        int evadingSpritePointValue = 0;

        // Lives
        List<AutomatedSprite> livesList = new List<AutomatedSprite>();

        //Spawn time variables
        int nextSpawnTimeChange = 5000;
        int timeSinceLastSpawnTimeChange = 0;

        // Powerup stuff
        int powerUpExpiration = 0;
        int powerUpFreezeExpiration = 0;

        public SpriteManager(Game game)
            : base(game)
        {
            // TODO: Construct any child components here
        }

        /// <summary>
        /// Allows the game component to perform any initialization it 
        /// needs to before starting to run.  This is where it can query for 
        /// any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // Initialize spawn time
            ResetSpawnTime();

            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(Game.GraphicsDevice);

            player = new UserControlledSprite(
                Game.Content.Load<Texture2D>(@"Images/threerings"),
                new Vector2(Game.Window.ClientBounds.Width / 2,
                    Game.Window.ClientBounds.Height / 2),
                new Point(75, 75), 10, new Point(0, 0),
                new Point(6, 8), new Vector2(6, 6));


            // Load player lives list
            for (int i = 0; i < ((Game1)Game).NumberLivesRemaining; ++i)
            {
                int offset = 10 + i * 40;
                livesList.Add(new AutomatedSprite(
                    Game.Content.Load<Texture2D>(@"images	hreerings"),
                    new Vector2(offset, 35), new Point(75, 75), 10,
                    new Point(0, 0), new Point(6, 8), Vector2.Zero,
                    null, 0, .5f));
            }

            base.LoadContent();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // Time to spawn enemy?
            nextSpawnTime -= gameTime.ElapsedGameTime.Milliseconds;
            if (nextSpawnTime < 0)
            {
                SpawnEnemy();

                // Reset spawn timer
                ResetSpawnTime();
            }

            UpdateSprites(gameTime);

            // Adjust sprite spawn times
            AdjustSpawnTimes(gameTime);

            // Expire Powerups?
            CheckPowerUpExpiration(gameTime);

            base.Update(gameTime);
        }

        protected void UpdateSprites(GameTime gameTime)
        {
            // Update player
            player.Update(gameTime, Game.Window.ClientBounds);

            // Update all nonplayer sprites
            for (int i = 0; i < spriteList.Count; ++i)
            {
                Sprite s = spriteList[i];

                s.Update(gameTime, Game.Window.ClientBounds);

                // Check for collisions
                if (s.collisionRect.Intersects(player.collisionRect))
                {
                    // Play collision sound
                    if (s.collisionCueName != null)
                        ((Game1)Game).PlayCue(s.collisionCueName);

                    // If collided with AutomatedSprite
                    // remove a life from the player
                    if (s is AutomatedSprite)
                    {
                        if (livesList.Count > 0)
                        {
                            livesList.RemoveAt(livesList.Count - 1);
                            --((Game1)Game).NumberLivesRemaining;
                        }
                    }
                    else if (s.collisionCueName == "pluscollision")
                    {
                        // Collided with plus - start plus power-up
                        powerUpExpiration = 5000;
                        player.ModifyScale(2);
                    }
                    else if (s.collisionCueName == "skullcollision")
                    {
                        // Collided with skull - start skull power-up
                        powerUpExpiration = 2000;
                        player.ModifySpeed(0f);
                    }
                    else if (s.collisionCueName == "boltcollision")
                    {
                        // Collided with bolt - start bolt power-up
                        powerUpExpiration = 5000;
                        player.ModifySpeed(2);
                    }

                    // Remove collided sprite from the game
                    spriteList.RemoveAt(i);
                    --i;
                }

                // Remove object if it is out of bounds
                if (s.IsOutOfBounds(Game.Window.ClientBounds))
                {
                    ((Game1)Game).AddScore(spriteList[i].scoreValue);
                    spriteList.RemoveAt(i);
                    --i;
                }

            }

            // Update lives-list sprites
            foreach (Sprite sprite in livesList)
                sprite.Update(gameTime, Game.Window.ClientBounds);
        }

        public override void Draw(GameTime gameTime)
        {
            spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);

            // Draw the player
            player.Draw(gameTime, spriteBatch);

            // Draw all sprites
            foreach (Sprite s in spriteList)
                s.Draw(gameTime, spriteBatch);

            // Draw player lives
            foreach (Sprite sprite in livesList)
                sprite.Draw(gameTime, spriteBatch);

            spriteBatch.End();
            base.Draw(gameTime);
        }

        private void ResetSpawnTime()
        {
            // Set the next spawn time for an enemy
            nextSpawnTime = ((Game1)Game).rnd.Next(
                enemySpawnMinMilliseconds,
                enemySpawnMaxMilliseconds);
        }

        private void SpawnEnemy()
        {
            Vector2 speed = Vector2.Zero;
            Vector2 position = Vector2.Zero;

            // Default frame size
            Point frameSize = new Point(75, 75);

            // Randomly choose which side of the screen to place enemy,
            // then randomly create a position along that side of the screen
            // and randomly choose a speed for the enemy
            switch (((Game1)Game).rnd.Next(4))
            {
                case 0: // LEFT to RIGHT
                    position = new Vector2(
                        -frameSize.X, ((Game1)Game).rnd.Next(0,
                        Game.GraphicsDevice.PresentationParameters.
                        BackBufferHeight - frameSize.Y));
                    speed = new Vector2(((Game1)Game).rnd.Next(
                        enemyMinSpeed,
                        enemyMaxSpeed), 0);
                    break;
                case 1: // RIGHT to LEFT
                    position = new
                        Vector2(
                        Game.GraphicsDevice.PresentationParameters.
                        BackBufferWidth,
                        ((Game1)Game).rnd.Next(0,
                        Game.GraphicsDevice.PresentationParameters.
                        BackBufferHeight - frameSize.Y));

                    speed = new Vector2(-((Game1)Game).rnd.Next(
                        enemyMinSpeed, enemyMaxSpeed), 0);
                    break;
                case 2: // BOTTOM to TOP
                    position = new Vector2(((Game1)Game).rnd.Next(0,
                    Game.GraphicsDevice.PresentationParameters.
                        BackBufferWidth - frameSize.X),
                        Game.GraphicsDevice.PresentationParameters.
                        BackBufferHeight);

                    speed = new Vector2(0,
                        -((Game1)Game).rnd.Next(enemyMinSpeed,
                        enemyMaxSpeed));
                    break;
                case 3: // TOP to BOTTOM
                    position = new Vector2(((Game1)Game).rnd.Next(0,
                        Game.GraphicsDevice.PresentationParameters.BackBufferWidth
                        - frameSize.X), -frameSize.Y);

                    speed = new Vector2(0,
                        ((Game1)Game).rnd.Next(enemyMinSpeed,
                        enemyMaxSpeed));
                    break;
            }

            // Get random number between 0 and 99
            int random = ((Game1)Game).rnd.Next(100);
            if (random < likelihoodAutomated)
            {
                // Create an AutomatedSprite.
                // Get new random number to determine whether to
                // create a three-blade or four-blade sprite.
                if (((Game1)Game).rnd.Next(2) == 0)
                {
                    // Create a four-blade enemy
                    spriteList.Add(
                    new AutomatedSprite(
                        Game.Content.Load<Texture2D>(@"imagesfourblades"),
                        position, new Point(75, 75), 10, new Point(0, 0),
                        new Point(6, 8), speed, "fourbladescollision",
                        automatedSpritePointValue));
                }
                else
                {
                    // Create a three-blade enemy
                    spriteList.Add(
                    new AutomatedSprite(
                        Game.Content.Load<Texture2D>(@"images	hreeblades"),
                        position, new Point(75, 75), 10, new Point(0, 0),
                        new Point(6, 8), speed, "threebladescollision",
                        automatedSpritePointValue));
                }
            }
            else if (random < likelihoodAutomated +
            likelihoodChasing)
            {
                // Create a ChasingSprite.
                // Get new random number to determine whether
                // to create a skull or a plus sprite.
                if (((Game1)Game).rnd.Next(2) == 0)
                {
                    // Create a skull
                    spriteList.Add(
                    new ChasingSprite(
                        Game.Content.Load<Texture2D>(@"imagesskullball"),
                        position, new Point(75, 75), 10, new Point(0, 0),
                        new Point(6, 8), speed, "skullcollision", this,
                        chasingSpritePointValue));
                }
                else
                {                    // Create a plus
                    spriteList.Add(
                    new ChasingSprite(
                        Game.Content.Load<Texture2D>(@"imagesplus"),
                        position, new Point(75, 75), 10, new Point(0, 0),
                        new Point(6, 4), speed, "pluscollision", this,
                        chasingSpritePointValue));
                }
            }
            else
            {
                // Create an EvadingSprite
                spriteList.Add(
                new EvadingSprite(
                    Game.Content.Load<Texture2D>(@"imagesolt"),
                    position, new Point(75, 75), 10, new Point(0, 0),
                    new Point(6, 8), speed, "boltcollision", this,
                    .75f, 150, evadingSpritePointValue));
            }
        }

        // Return current position of the player sprite
        public Vector2 GetPlayerPosition()
        {
            return player.GetPosition;
        }

        protected void AdjustSpawnTimes(GameTime gameTime)
        {
            // If the spawn max time is > 500 milliseconds
            // decrease the spawn time if it is time to do
            // so based on the spawn-timer variables
            if (enemySpawnMaxMilliseconds > 500)
            {
                timeSinceLastSpawnTimeChange += 
                    gameTime.ElapsedGameTime.Milliseconds;
                if (timeSinceLastSpawnTimeChange > nextSpawnTimeChange)
                {
                    timeSinceLastSpawnTimeChange -= nextSpawnTimeChange;
                    if (enemySpawnMaxMilliseconds > 1000)
                    {
                        enemySpawnMaxMilliseconds -= 100;
                        enemySpawnMinMilliseconds -= 100;
                    }
                    else
                    {
                        enemySpawnMaxMilliseconds -= 10;
                        enemySpawnMinMilliseconds -= 10;
                    }
                }
            }
        }

        protected void CheckPowerUpExpiration(GameTime gameTime)
        {
            // Is a power-up active?
            if (powerUpExpiration > 0)
            {
                // Decrement power-up timer
                powerUpExpiration -= gameTime.ElapsedGameTime.Milliseconds;
                if (powerUpExpiration <= 0)
                {
                    // If power-up timer has expired, end all power-ups
                    powerUpExpiration = 0;
                    player.ResetScale();
                    player.ResetSpeed();
                }
            }

            // Is a freeze power-up active?
            if (powerUpFreezeExpiration > 0)
            {
                // Decrement power-up timer
                powerUpFreezeExpiration -= 
                    gameTime.ElapsedGameTime.Milliseconds;
                if (powerUpFreezeExpiration <= 0)
                {
                    // If power-up timer has expired, end all power-ups
                    powerUpFreezeExpiration = 0;
                    player.ResetSpeed();
                }
            }
        }
    }
}

Chapter 9: 3D Game Development

Quiz Answers

  1. Does XNA use a right-handed or left-handed coordinate system?

    XNA uses a right-handed coordinate system, which means that if you looked at the origin down the Z axis with positive X moving to your right, the Z axis would be positive in the direction coming toward you.

  2. What makes up a viewing frustum (or field of view) for a camera in XNA 3D?

    The viewing frustum is made up of a camera angle and near and far clipping planes.

  3. What is culling?

    Culling is the process of not drawing objects that are not facing the camera. For example, you never need to see the inside of a soccer ball when playing a soccer game in XNA, so the processor doesn’t draw that side of the object, which saves valuable processor time.

  1. What is a vertex declaration?

    A vertex declaration lets the graphics device know what type of data you are about to send so it knows how to process that data.

  2. Fact or fiction: there is a difference between applying a rotation multiplied by a translation and applying a translation multiplied by a rotation.

    Fact. A rotation * translation will cause an object to spin in place, whereas a translation * rotation will cause an object to orbit.

  3. What order of translation and rotation would be needed in order to simulate a planet spinning in place while orbiting the origin?

    To spin in place, you first need a rotation. Then to orbit, you need a translation and a rotation, in that order. So, the answer is rotation * translation * rotation.

  4. Fact or fiction: to map the lower-right corner of a texture that is 250×300 pixels in size to a vertex, you should specify the (U, V) coordinate (250, 300).

    Fiction. (U, V) coordinates must be between 0 and 1. To specify the upper-left corner of a texture, you use the coordinate (0, 0). To specify the lower-right corner, you use the coordinate (1, 1).

  5. How many vertices are needed to draw three triangles using a triangle list?

    A triangle list uses three vertices for each triangle. To draw three triangles, nine vertices are required.

  6. How many vertices are needed to draw three triangles using a triangle strip?

    A triangle strip builds a triangle out of the first three vertices and a new triangle with every additional vertex, using the new vertex and the two previous vertices. Five vertices are required to draw three triangles using a triangle strip.

  7. How many polygons were used to draw Marcus in Gears of War?

    According to d’Artiste: Character Modeling 2 (Ballistic Publishing, 2010), Marcus took a whopping 15,000 polygons to draw. Crazy to think of how fast the computer and the graphics card are working to bring something that complex to life. Luckily, we don’t have to draw complex models by hand like you did to create the triangle and rectangle in this chapter. In Chapter 10, you’ll see how much easier it is to draw complex objects using models.

Exercise Answer

Building on the code that you wrote in this chapter, create a six-sided cube with different textures on each side that rotates on multiple axes.

To create the six-sided cube with different textures on each side, first you’ll need to add five extra images to your project. Then, you’ll have to figure out the coordinates to draw 12 triangles: 2 for each side of the cube. Next, create vertices for the sides of the cube in your vertex array, and then draw each side in your Draw method. One thing to be aware of is that you’ll need to begin and end the effect for each side of the cube because you’ll need to reset the texture for each side (which must be done before BasicEffect.Begin is called).

The Game1 class is the only thing that changes in the solution. It’s listed here:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace _3D_Madness
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        // Game camera
        Camera camera;

        // Vertex data
        VertexPositionTexture[] verts;
        VertexBuffer vertexBuffer;

        // Effect
        BasicEffect effect;

        // Movement and rotation stuff
        Matrix worldTranslation = Matrix.Identity;
        Matrix worldRotation = Matrix.Identity;

        // Texture info
        List<Texture2D> textureList = new List<Texture2D>();

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it 
        /// needs to before starting to run. This is where it can query for 
        /// any required services and load any content. 
        /// </summary>
        protected override void Initialize()
        {
            // Initialize camera
            camera = new Camera(this, new Vector3(0, 0, 5),
                Vector3.Zero, Vector3.Up);
            Components.Add(camera);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //initialize vertices
            verts = new VertexPositionTexture[24];
            //FRONT
            verts[0] = new VertexPositionTexture(
                new Vector3(-1, 1, 1), new Vector2(0, 0));
            verts[1] = new VertexPositionTexture(
                new Vector3(1, 1, 1), new Vector2(1, 0));
            verts[2] = new VertexPositionTexture(
                new Vector3(-1, -1, 1), new Vector2(0, 1));
            verts[3] = new VertexPositionTexture(
                new Vector3(1, -1, 1), new Vector2(1, 1));

            //BACK
            verts[4] = new VertexPositionTexture(
                new Vector3(1, 1, -1), new Vector2(0, 0));
            verts[5] = new VertexPositionTexture(
                new Vector3(-1, 1, -1), new Vector2(1, 0));
            verts[6] = new VertexPositionTexture(
                new Vector3(1, -1, -1), new Vector2(0, 1));
            verts[7] = new VertexPositionTexture(
                new Vector3(-1, -1, -1), new Vector2(1, 1));

            //LEFT
            verts[8] = new VertexPositionTexture(
                new Vector3(-1, 1, -1), new Vector2(0, 0));
            verts[9] = new VertexPositionTexture(
                new Vector3(-1, 1, 1), new Vector2(1, 0));
            verts[10] = new VertexPositionTexture(
                new Vector3(-1, -1, -1), new Vector2(0, 1));
            verts[11] = new VertexPositionTexture(
                new Vector3(-1, -1, 1), new Vector2(1, 1));

            //RIGHT
            verts[12] = new VertexPositionTexture(
                new Vector3(1, 1, 1), new Vector2(0, 0));            verts[13] = new VertexPositionTexture(
                new Vector3(1, 1, -1), new Vector2(1, 0));
            verts[14] = new VertexPositionTexture(
                new Vector3(1, -1, 1), new Vector2(0, 1));
            verts[15] = new VertexPositionTexture(
                new Vector3(1, -1, -1), new Vector2(1, 1));

            //TOP
            verts[16] = new VertexPositionTexture(
                new Vector3(-1, 1, -1), new Vector2(0, 0));
            verts[17] = new VertexPositionTexture(
                new Vector3(1, 1, -1), new Vector2(1, 0));
            verts[18] = new VertexPositionTexture(
                new Vector3(-1, 1, 1), new Vector2(0, 1));
            verts[19] = new VertexPositionTexture(
                new Vector3(1, 1, 1), new Vector2(1, 1));

            //BOTTOM
            verts[20] = new VertexPositionTexture(
                new Vector3(-1, -1, 1), new Vector2(0, 0));
            verts[21] = new VertexPositionTexture(
                new Vector3(1, -1, 1), new Vector2(1, 0));
            verts[22] = new VertexPositionTexture(
                new Vector3(-1, -1, -1), new Vector2(0, 1));
            verts[23] = new VertexPositionTexture(
                new Vector3(1, -1, -1), new Vector2(1, 1));


            // Set vertex data in VertexBuffer
            vertexBuffer = new VertexBuffer(GraphicsDevice, 
                typeof(VertexPositionTexture), verts.Length,
                BufferUsage.None);
            vertexBuffer.SetData(verts);

            // Initialize the BasicEffect
            effect = new BasicEffect(GraphicsDevice);

            //load all textures
            textureList.Add(Content.Load<Texture2D>(@"TexturesTrees"));
            textureList.Add(Content.Load<Texture2D>(@"Textures	1"));
            textureList.Add(Content.Load<Texture2D>(@"Textures	2"));
            textureList.Add(Content.Load<Texture2D>(@"Textures	3"));
            textureList.Add(Content.Load<Texture2D>(@"Textures	4"));
            textureList.Add(Content.Load<Texture2D>(@"Textures	5"));
           
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the 
        /// place to unload all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non-ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == 
                ButtonState.Pressed)
                this.Exit();

            // Translation
            KeyboardState keyboardState = Keyboard.GetState();
            if (keyboardState.IsKeyDown(Keys.Left))
                worldTranslation *= Matrix.CreateTranslation(-.01f, 0, 0);
            if (keyboardState.IsKeyDown(Keys.Right))
                worldTranslation *= Matrix.CreateTranslation(.01f, 0, 0);

            // Rotation
            worldRotation *= Matrix.CreateFromYawPitchRoll(
                MathHelper.PiOver4 / 60,
                MathHelper.PiOver4 / 360,
                MathHelper.PiOver4 / 180);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            

            // Set the vertex buffer on the GraphicsDevice
            GraphicsDevice.SetVertexBuffer(vertexBuffer);

            //Set object and camera info
            effect.World = worldRotation * worldTranslation * worldRotation;
            effect.View = camera.view;
            effect.Projection = camera.projection;
            effect.TextureEnabled = true;

            // Draw front
            effect.Texture = textureList[0];
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>
                    (PrimitiveType.TriangleStrip, verts, 0, 2);

            }

            //draw back 
            effect.Texture = textureList[1];
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>
                    (PrimitiveType.TriangleStrip, verts, 4, 2);

            }


            //draw left
            effect.Texture = textureList[2];
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>
                    (PrimitiveType.TriangleStrip, verts, 8, 2);

            }

            //draw right
            effect.Texture = textureList[3];
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>
                    (PrimitiveType.TriangleStrip, verts, 12, 2);

            }

            //draw top
            effect.Texture = textureList[4];
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>
                    (PrimitiveType.TriangleStrip, verts, 16, 2);

            }

            //draw bottom
            effect.Texture = textureList[5];
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>
                    (PrimitiveType.TriangleStrip, verts, 20, 2);

            }


            base.Draw(gameTime);
        }
    }
}

Chapter 10: 3D Models

Quiz Answers

  1. What model format(s) are supported in XNA?

    XNA supports .x and .fbx model files.

  2. Why use a model when you can just draw things on your own?

    Models allow you to build a model or design in a third-party tool specifically designed for artistically modeling and molding three-dimensional objects. It would be nearly impossible to develop objects by hand in XNA 3D using primitives and attain the same level of detail and complexity that is achievable using a model.

  3. What type of effect are models loaded with by default when they’re loaded into XNA?

    By default, models in XNA use BasicEffects.

  4. Fact or fiction: if your model has separate texture files associated with it, but those files aren’t in the location specified by the model file, your game will crash when it tries to load the model.

    Fiction. Your game would not compile if textures were missing. The content pipeline would throw a compilation error.

  5. What number comes next in the sequence {4, 8, 15, 16, 23}?

    42. These are the numbers that haunted Hurley in one of the greatest television series of all time, ABC’s Lost.

Exercise Answer

Take the code from this chapter and create a new subclass of BasicModel in which the ship moves back and forth between (0, 0, 0) and (0, 0, −400). Make the ship turn appropriately to always face the direction in which it is going.

There are numerous solutions that will give you the effect described here. The following solution uses a variable to indicate how far the ship can go in the background and a direction Vector3 variable to indicate the ship’s current direction. When the ship reaches a point where it must turn around, the Direction’s Z value is multiplied by −1 and the ship is rotated on a 180 degree yaw to make it face the direction it is heading. The code for a subclass of BasicModel that solves this exercise is shown here (note that you’ll have to create this class and then also modify the ModelManager to create an object of this type rather than the SpinningEnemy type):

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


namespace _3D_Game
{
    class FlyingShip : BasicModel
    {

        Matrix rotation = Matrix.CreateRotationY(MathHelper.Pi);
        Matrix translation = Matrix.Identity;

        float maxDistance = −400;
        Vector3 direction = new Vector3(0, 0, −1);

        public FlyingShip(Model m)
            : base(m)
        {
        }

        public override void Update()
        {
            //if the object has traveled past the max distance
            //or in front of the origin, reverse direction
            //and rotate ship 180 degrees
            if (translation.Translation.Z < maxDistance ||
                translation.Translation.Z > 0)
            {
                direction.Z *= −1;
                rotation *= Matrix.CreateRotationY(MathHelper.Pi);
            }

            translation *= Matrix.CreateTranslation(direction);
        }

        public override Matrix GetWorld()
        {
            return world * rotation * translation;
        }
    }
}

Chapter 11: Creating a First-Person Camera

Quiz Answers

  1. When performing a yaw rotation with a camera, what camera vector(s) rotate(s)? What axis would you rotate on?

    In a yaw, the only vector that changes is the camera direction. You rotate the direction vector around the camera’s up vector.

  2. When performing a roll rotation with a camera, what camera vector(s) rotate(s)? What axis would you rotate on?

    In a roll, the only vector that changes is the camera’s up vector. You rotate the up vector around the camera’s direction vector.

  3. When performing a pitch rotation with a camera, what camera vector(s) rotate(s)? What axis would you rotate on?

    In a pitch, the direction vector always changes and, depending on whether you’re creating a land-based camera or a flight-simulator camera, you may also rotate the up vector. You rotate both of these vectors around the cross product of the camera’s up and direction vectors.

  4. What famous holiday includes an event titled the “Airing of Grievances”?

    Festivus (a holiday invented by Frank Costanza on the greatest show of all time, Seinfeld) includes the “Airing of Grievances,” which takes place immediately after the Festivus feast and consists of all participants letting everybody know how much they have disappointed you that year.

Exercise Answer

Remember that to move a 3D camera forward, you use the following code:

    cameraPosition += cameraDirection * speed;

There is a small problem with this method, however, when it’s applied to land-based cameras. Essentially, to eliminate the ability to “fly” when looking up, you remove the Y component of the cameraDirection vector prior to moving the camera with this line of code. This causes your camera to stay at the same Y value, which keeps the camera on the ground.

However, consequently the higher you pitch your camera, the slower you end up moving. For example, if your cameraDirection vector was (10, 2, 0) when you removed the Y component, you would end up with the vector (10, 0, 0) and you’d move at that speed. If your camera was pitched at a larger angle and your cameraDirection vector was (2, 10, 0), the resulting vector after removing the Y component would be (2, 0, 0) and you’d move forward at that speed.

Convert your 3D Flying Camera solution to a land-based camera and solve this problem so that when moving forward and backward your camera moves at the same speed, regardless of the pitch angle. Use the code you created in the first half of this chapter.

Hint: remember that Vector3.Normalize will take any Vector3 and give it a magnitude or length of 1.

The solution to this problem is fairly straightforward. First, you remove the Y component of the direction vector, and then you normalize the resulting vector, which gives the vector a magnitude (or length) of 1. Once you do this, you can multiply the vector by the speed of the camera and add it to your camera position. The code that moves the camera forward and backward is found in the Update method of the Camera class. The solution to the problem affects only that code and is shown here:

//Remove the Y component of the camera direction
Vector3 movementDirection = cameraDirection;
movementDirection.Y = 0;

//Normalize the vector to ensure constant speed
movementDirection.Normalize();

// Move forward/backward
if (Keyboard.GetState().IsKeyDown(Keys.W))
    cameraPosition += movementDirection * speed;
if (Keyboard.GetState().IsKeyDown(Keys.S))
    cameraPosition −= movementDirection * speed;

Chapter 12: 3D Collision Detection and Shooting

Quiz Answers

  1. When firing a shot in a 3D (or 2D, for that matter) game, how do you determine the direction of the shot?

    Typically, the thing that’s firing a shot (a camera, a gun, etc.) has a direction of its own. When firing a shot, you give the bullet or projectile the same direction vector as the item from which it emanates.

  2. Fact or fiction: every model has a BoundingSphere object that surrounds the entire model and can be used for collision detection.

    Fiction. The BoundingSphere object belongs to a ModelMesh object. Every Model has one or more ModelMesh objects. The BoundingSphere therefore may cover the entire model, but a Model may also have several meshes, and in that case, the Model will have multiple BoundingSphere objects that each surround portions of the Model.

  1. When using BoundingSpheres associated with a moving model for collision detection, what must be done to the BoundingSphere in order to accurately detect collisions?

    BoundingSpheres do not automatically move, rotate, and scale with the model that owns them. You need to apply the movement, rotation, and scale matrices to the BoundingSphere before using them for collision detection.

  2. What is the difference between drawing 2D images on screen in a 3D game and drawing 2D images on the screen in a 2D game?

    Nothing. Drawing in 2D is the same, regardless of whether there are also 3D graphics in the game.

  3. Why does Kramer’s advice to Elaine regarding his karate class and the power of the inner katra backfire on Elaine?

    After Kramer encourages Elaine to run the J. Peterman catalog by relaying stories of his dominance of the karate dojo, Elaine gains new perspective and excitement for the task ahead. But everything falls apart when she finds out that Kramer is dominating the dojo because his peers are 8-year-old children:

    Elaine: Kramer!

    Kramer: Oh, hey.

    Elaine: What are you doing?

    Kramer: Oh, well, I-I-I’m dominating.

    Elaine: You never said you were fighting children.

    Kramer: Well, it’s not the size of the opponent, Elaine, it’s, uh, the ferocity.

    Elaine: This is what you used to build me up? This is where you got all that stupid katra stuff?

    Kramer: No, no. That’s from, uh, Star Trek IIIThe Search for Spock.

    Elaine: Search…for Spock?!

    Kramer: Yeah, I know Jerry will tell you that The Wrath of Khan is the better picture, but for me, I always….

    Elaine: (pushes him) You doofus!

Exercise Answer

To familiarize yourself further with what was covered in this chapter, customize the shot firing mechanism by modifying the code to do the following:

  • Slow the shots down by 50%.

  • Cut the shot delay in half (i.e., make shots fire twice as often when holding the space bar continuously).

  • Every time a shot is fired, fire three shots in a spread (i.e., one shot down the center and two other shots, one to the left and one to the right).

Well, the first two problems here are easy: by changing the initial values for the variables representing the shot speed and the shot delay, you can solve each of those problems with minimal coding. To fire three shots in a spread, you’ll have to find the code where a single shot is fired and add two more shots to the model manager (one to the right of the original shot and one to the left).

The Game1 class is the only one that needs modification. Here is a sample solution:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace _3D_Game
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public Camera camera { get; protected set; }
        ModelManager modelManager;

        //Randomness
        public Random rnd { get; protected set; }

        //Shots
        float shotSpeed = 5;
        int shotDelay = 150;
        int shotCountdown = 0;

        //Crosshair
        Texture2D crosshairTexture;

        //Audio
        AudioEngine audioEngine;
        WaveBank waveBank;
        SoundBank soundBank;
        Cue trackCue;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 1024;
#if !DEBUG
            graphics.IsFullScreen = true;
#endif

            rnd = new Random();
        }

        protected override void Initialize()
        {
            //Initialize camera
            camera = new Camera(this, new Vector3(0, 0, 50),
                Vector3.Zero, Vector3.Up);
            Components.Add(camera);

            //Initialize model manager
            modelManager = new ModelManager(this);
            Components.Add(modelManager);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //Load crosshair
            crosshairTexture = Content.Load<Texture2D>(@"textures
crosshair");

            // Load sounds and play initial sounds
            audioEngine = new AudioEngine(
                @"ContentAudioGameAudio.xgs");
            waveBank = new WaveBank(audioEngine,
                @"ContentAudioWave Bank.xwb");
            soundBank = new SoundBank(audioEngine,
                @"ContentAudioSound Bank.xsb");

            trackCue = soundBank.GetCue("Tracks");
            trackCue.Play();
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non-ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
                ButtonState.Pressed)
                this.Exit();            // See if the player has fired a shot
            FireShots(gameTime);

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // TODO: Add your drawing code here

            base.Draw(gameTime);

            //Draw crosshair
            spriteBatch.Begin();

            spriteBatch.Draw(crosshairTexture,
                new Vector2((Window.ClientBounds.Width / 2)
                    - (crosshairTexture.Width / 2),
                    (Window.ClientBounds.Height / 2)
                    - (crosshairTexture.Height / 2)),
                    Color.White);

            spriteBatch.End();
        }

        protected void FireShots(GameTime gameTime)
        {
            if (shotCountdown <= 0)
            {
                // Did player press space bar or left mouse button?
                if (Keyboard.GetState().IsKeyDown(Keys.Space) ||
                    Mouse.GetState().LeftButton == ButtonState.Pressed)
                {
                    // Add a shot to the model manager
                    modelManager.AddShot(
                        camera.cameraPosition + new Vector3(0, −5, 0),
                        camera.GetCameraDirection * shotSpeed);

                    //Add shot in spread to the right
                    Vector3 initialPosition = camera.cameraPosition +
                        Vector3.Cross(camera.GetCameraDirection,
                        camera.cameraUp) * 5;

                    modelManager.AddShot(
                        initialPosition + new Vector3(0, −5, 0),
                        camera.GetCameraDirection * shotSpeed);

                    //Add shot in spread to the left
                    initialPosition = camera.cameraPosition -
                        Vector3.Cross(camera.GetCameraDirection,
                        camera.cameraUp) * 5;

                    modelManager.AddShot(
                        initialPosition + new Vector3(0, −5, 0),
                        camera.GetCameraDirection * shotSpeed);


                    // Play shot audio
                    PlayCue("Shot");

                    // Reset the shot countdown
                    shotCountdown = shotDelay;
                }
            }
            else
                shotCountdown −= gameTime.ElapsedGameTime.Milliseconds;
        }

        public void PlayCue(string cue)
        {
            soundBank.PlayCue(cue);
        }
    }
}

Chapter 13: HLSL Basics

Quiz Answers

  1. In HLSL, how can you access the first element in a float4 object?

    If you want to access the first element in a float4 object called color, you can use array notation (color[0]), or you can use the namespaces for color and position (color.r or color.x).

  2. What is swizzling?

    The term swizzling refers to accessing multiple elements of a float4 or similar datatype at the same time by using two or more elements from the color or position namespaces (e.g., color.rb or color.xyz).

  3. In HLSL, how do you specify which vertex and pixel shader versions to use?

    In the pass block of the technique for an HLSL effect file, you specify a VertexShader and/or a PixelShader by providing the compile keyword followed by a shader version. For vertex shaders, you use vs_2_0 syntax, and for pixel shaders, you use ps_2_0 syntax.

  4. What does HLSL do for you that you can’t accomplish without it?

    HLSL allows developers to access hardware functions that aren’t available via the XNA Framework. The reason: graphics hardware has become more and more complex, and if the XNA Framework were expanded to handle all capabilities of graphics cards, the framework would be enormous. Instead, HLSL works with XNA and allows you to write code for the graphics card itself.

  5. How do you multiply two matrices together in HLSL?

    The mul function in HLSL will multiply together two matrices.

  6. What is the role of a semantic in HLSL?

    A semantic marks a variable as being used for a certain purpose. For input parameters, the semantic means that the parameter will automatically be given a value specified by the semantic. For output parameters, it is a way to flag certain variables as containing certain information that is required for processing that takes place after the shader is finished executing.

  7. Who burninates the countryside, burninates the peasants, burninates all the people, and their thatch-roofed cottages?

    Trogdor, the man…er…dragon man…er…the dragon, of course.

Exercise Answer

Take the code you built in this chapter and draw a six-sided cube using the trees image provided as the texture for each side of the cube. One each side of the cube, use one of the four texture effects you built in this chapter (normal texture, burred texture, negative texture, grayscale texture). Use each of the four effects at least once on the cube.

This exercise is very similar to the one from Chapter 9 in which you built a six-sided cube with different textures on each side. The difference here is that you’ll be applying the same texture to each side of the cube and then applying different HLSL effects to those sides.

First, create the different effect files as shown in this chapter, and then use something similar to the following code to create the cube and apply the effects:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace _3D_Madness
{
    /// <summary>
    /// This is the main type for your game.
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        // Game camera
        Camera camera;

        // Vertex data
        VertexPositionTexture[] verts;
        VertexBuffer vertexBuffer;

        // Effect
        Effect normalEffect;
        Effect blurEffect;
        Effect negativeEffect;
        Effect grayscaleEffect;

        // Movement and rotation stuff
        Matrix worldTranslation = Matrix.Identity;
        Matrix worldRotation = Matrix.Identity;

        // Texture info
        Texture2D texture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it 
        /// needs to before starting to run. This is where it can query for 
        /// any required services and load content.  
        /// </summary>
        protected override void Initialize()
        {
            // Initialize camera
            camera = new Camera(this, new Vector3(0, 0, 5),
                Vector3.Zero, Vector3.Up);
            Components.Add(camera);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //initialize vertices
            verts = new VertexPositionTexture[24];
            //FRONT
            verts[0] = new VertexPositionTexture(
                new Vector3(-1, 1, 1), new Vector2(0, 0));
            verts[1] = new VertexPositionTexture(
                new Vector3(1, 1, 1), new Vector2(1, 0));
            verts[2] = new VertexPositionTexture(
                new Vector3(-1, -1, 1), new Vector2(0, 1));
            verts[3] = new VertexPositionTexture(
                new Vector3(1, -1, 1), new Vector2(1, 1));

            //BACK
            verts[4] = new VertexPositionTexture(
                new Vector3(1, 1, -1), new Vector2(0, 0));
            verts[5] = new VertexPositionTexture(
                new Vector3(-1, 1, -1), new Vector2(1, 0));
            verts[6] = new VertexPositionTexture(
                new Vector3(1, -1, -1), new Vector2(0, 1));
            verts[7] = new VertexPositionTexture(
                new Vector3(-1, -1, -1), new Vector2(1, 1));

            //LEFT
            verts[8] = new VertexPositionTexture(
                new Vector3(-1, 1, -1), new Vector2(0, 0));
            verts[9] = new VertexPositionTexture(
                new Vector3(-1, 1, 1), new Vector2(1, 0));
            verts[10] = new VertexPositionTexture(
                new Vector3(-1, -1, -1), new Vector2(0, 1));
            verts[11] = new VertexPositionTexture(
                new Vector3(-1, -1, 1), new Vector2(1, 1));

            //RIGHT
            verts[12] = new VertexPositionTexture(
                new Vector3(1, 1, 1), new Vector2(0, 0));
            verts[13] = new VertexPositionTexture(
                new Vector3(1, 1, -1), new Vector2(1, 0));
            verts[14] = new VertexPositionTexture(
                new Vector3(1, -1, 1), new Vector2(0, 1));
            verts[15] = new VertexPositionTexture(
                new Vector3(1, -1, -1), new Vector2(1, 1));

            //TOP
            verts[16] = new VertexPositionTexture(
                new Vector3(-1, 1, -1), new Vector2(0, 0));
            verts[17] = new VertexPositionTexture(
                new Vector3(1, 1, -1), new Vector2(1, 0));
            verts[18] = new VertexPositionTexture(
                new Vector3(-1, 1, 1), new Vector2(0, 1));
            verts[19] = new VertexPositionTexture(
                new Vector3(1, 1, 1), new Vector2(1, 1));

            //BOTTOM
            verts[20] = new VertexPositionTexture(
                new Vector3(-1, -1, 1), new Vector2(0, 0));
            verts[21] = new VertexPositionTexture(
                new Vector3(1, -1, 1), new Vector2(1, 0));
            verts[22] = new VertexPositionTexture(
                new Vector3(-1, -1, -1), new Vector2(0, 1));
            verts[23] = new VertexPositionTexture(
                new Vector3(1, -1, -1), new Vector2(1, 1));

            // Set vertex data in VertexBuffer
            vertexBuffer = new VertexBuffer(GraphicsDevice,
                typeof(VertexPositionTexture), verts.Length,
                BufferUsage.None);
            vertexBuffer.SetData(verts);

            //Load effect
            normalEffect = Content.Load<Effect>(@"effectsRed");
            grayscaleEffect = Content.Load<Effect>(@"effectsGrayscale");
            negativeEffect = Content.Load<Effect>(@"effectsNegative");
            blurEffect = Content.Load<Effect>(@"effectsBlur");

            // Load texture
            texture = Content.Load<Texture2D>(@"Textures	rees");
           
        }

        /// <summary>
        /// UnloadContent will be called once per game and is 
        /// the place to unload all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non-ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
                ButtonState.Pressed)
                this.Exit();

            // Translation
            KeyboardState keyboardState = Keyboard.GetState();
            if (keyboardState.IsKeyDown(Keys.Left))
                worldTranslation *= Matrix.CreateTranslation(-.01f, 0, 0);
            if (keyboardState.IsKeyDown(Keys.Right))
                worldTranslation *= Matrix.CreateTranslation(.01f, 0, 0);

            // Rotation
            worldRotation *= Matrix.CreateFromYawPitchRoll(
                MathHelper.PiOver4 / 60,
                0, 
                0);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.
        /// </param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            

            // Set the vertex buffer on the GraphicsDevice
            GraphicsDevice.SetVertexBuffer(vertexBuffer);

            //Draw front
            DrawVerts(normalEffect, 0, 2);
            //Draw back
            DrawVerts(blurEffect, 4, 2);
            //Draw left
            DrawVerts(grayscaleEffect, 8, 2);
            //Draw right
            DrawVerts(negativeEffect, 12, 2);
            //Draw top
            DrawVerts(blurEffect, 16, 2);
            //Draw bottom
            DrawVerts(grayscaleEffect, 20, 2);

            base.Draw(gameTime);
        }

        protected void DrawVerts(Effect effect, int start, int end)
        {
            effect.CurrentTechnique = effect.Techniques["Textured"];
            Matrix world = worldRotation * worldTranslation;
            effect.Parameters["xWorldViewProjection"].SetValue(
                world * camera.view * camera.projection);
            effect.Parameters["xColoredTexture"].SetValue(texture);

            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();
                GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>
                    (PrimitiveType.TriangleStrip, verts, start, end);
            }

        }
    }
}

Chapter 14: Particle Systems

Quiz Answers

  1. What is a particle engine?

    A particle engine is a mechanism that manipulates, moves, adds, removes, and draws particles to create a particle effect.

  2. Why were there two textures used for the particles in your explosion? What was the purpose of each?

    The first texture you added to the project (particle.png) represents the shape of each particle. It is simply a shaded, round circle, all white, with a transparent background. The second texture (particleColors.png) is used to get random colors with which to color each particle. The same technique was used with the stars using the particle.png file for the shape and the stars.png file for the colors.

  3. What are texture (U, V) coordinates?

    (U, V) coordinates map texture sizes into a coordinate system that ranges from 0 to 1 horizontally (U) and 0 to 1 vertically (V).

  4. According to Napoleon Dynamite’s Uncle Rico, how far could Uncle Rico throw a “pigskin” back in 1982?

    In the movie Napoleon Dynamite, Uncle Rico often reminisces of the good ol’ days in ’82:

    Uncle Rico: Back in ’82, I used to be able to throw a pigskin a quarter mile.

    Kip: Are you serious?

    Uncle Rico: I’m dead serious.

Chapter 15: Wrapping Up Your 3D Game

Exercise Answer

Create a multishot power-up that, when active, will fire four shots instead of one. Instead of shooting one shot in the center of the camera, when multishot is active, shoot one shot from above and right of the camera, one from above and left, one from below and right, and one from below and left.

When the player shoots three ships in a row, the game will randomly choose which power-up to activate (rapid fire or multishot).

You have all the code fleshed out to create a power-up. Now you’ll need to take that code and add a new multishot power-up. Start with adding a new value to the PowerUps enum to represent multishot mode.

Once that’s done, there are really only two other steps: add code when a power-up is triggered that randomly picks the multishot or rapid fire mode, and add the code to fire multiple shots in multishot mode.

The difficult part will be in the FireShots method of the Game1 class, where you’ll need to add the code for multiple shots. Here is one possible solution for that part of the problem:

protected void FireShots(GameTime gameTime)
{
    if (shotCountdown <= 0)
    {
        // Did player press space bar or left mouse button?
        if (Keyboard.GetState().IsKeyDown(Keys.Space) ||
            Mouse.GetState().LeftButton == ButtonState.Pressed)
        {
            if (currentPowerUp != PowerUps.MULTI_SHOT)
            {
                //Normal mode - fire one shot

                // Add a shot to the model manager
                modelManager.AddShot(
                    camera.cameraPosition + new Vector3(0, −5, 0),
                    camera.GetCameraDirection * shotSpeed);
            }
            else
            {
                //Multi-shot mode!

                //Add shot in spread to the top right
                Vector3 initialPosition = camera.cameraPosition +
                    Vector3.Cross(camera.GetCameraDirection, camera.
                        cameraUp) * 5
                        + (camera.cameraUp * 5);
                modelManager.AddShot(
                    initialPosition + new Vector3(0, −5, 0),
                    camera.GetCameraDirection * shotSpeed);

                //Add shot in spread to the bottom right
                initialPosition = camera.cameraPosition +
                    Vector3.Cross(camera.GetCameraDirection, camera.
                        cameraUp) * 5
                        - (camera.cameraUp * 5);
                modelManager.AddShot(
                    initialPosition + new Vector3(0, −5, 0),
                    camera.GetCameraDirection * shotSpeed);

                //Add shot in spread top left
                initialPosition = camera.cameraPosition −
                    Vector3.Cross(camera.GetCameraDirection, camera.
                        cameraUp) * 5
                        + (camera.cameraUp * 5);
                modelManager.AddShot(
                    initialPosition + new Vector3(0, −5, 0),
                    camera.GetCameraDirection * shotSpeed);

                //Add shot in spread bottom left
                initialPosition = camera.cameraPosition −
                    Vector3.Cross(camera.GetCameraDirection, camera.
                        cameraUp) * 5
                        − (camera.cameraUp * 5);
                modelManager.AddShot(
                    initialPosition + new Vector3(0, −5, 0),
                    camera.GetCameraDirection * shotSpeed);
            }

            // Play shot audio
            PlayCue("Shot");

            // Reset the shot countdown
            shotCountdown = shotDelay;
        }
    }
    else
        shotCountdown −= gameTime.ElapsedGameTime.Milliseconds;
}

Chapter 16: Deploying to the Xbox 360

Quiz Answers

  1. What piece of information does a PC use to identify a specific Xbox 360 machine?

    XNA uses a connection key to identify an Xbox 360 machine on a PC.

  2. Fact or fiction: to debug a project that has been deployed on an Xbox 360, you have to load the code in the Xbox 360 code editor and place a breakpoint within the code on the Xbox 360 machine.

    Fiction. To debug projects that are deployed on an Xbox 360, place a breakpoint in the code on the PC used to deploy the project. If you then start the project in debug mode, it will run on the Xbox 360 and allow you to debug on the connected PC.

  3. Fact or fiction: if you’ve created a Windows game project and you want to deploy that project to your Xbox 360, you need to create a new project in order to do so.

    Fact. Creating a Windows project allows you to run your game on Windows only, and creating an Xbox 360 project allows you to run your game on an Xbox 360 only. To facilitate multiplatform development, you can create a project for one platform and then create a copy of it for another platform and share files between the two.

  1. What is a preprocessor directive?

    Preprocessor directives give developers a way to write code intended for the preprocessor rather than the compiler. Developers can perform logic that affects what code is compiled and how it is compiled using these directives.

  2. What does the following code do in a Windows project?

    #if (XBOX360)
        int A = 5;
        int B = 4;
        int C = A − B;
    #endif

    Because the XBOX360 symbol is not defined for a Windows project, this code not only does nothing, but it will not even compile in the game. The end result of the project will be exactly the same as if the code did not exist.

  3. What does “serenity now” lead to?

    According to Lloyd Braun, a childhood neighbor of George Costanza, serenity now leads to insanity later.

Chapter 17: Developing for Windows Phone 7

Quiz Answers

  1. What are the three types of developer accounts you can create for development on Windows Phone 7?

    You can register for an account as a business, an individual, or a student.

  2. What software do you use to unlock your Windows Phone 7 device?

    You use the Microsoft Zune software to unlock your Windows Phone 7 as well as to connect to the device through Visual Studio.

  3. How do you read data from the Windows Phone 7 accelerometer?

    You handle the ReadingChanged event on the accelerometer and capture the X, Y, and/or Z values in that method’s AccelerometerReadingEventArgs parameter.

  4. What is the default screen resolution and screen rotation of a Windows Phone 7 game?

    The default screen rotation is LandscapeLeft, and the resolution is 800×480.

  5. What are the possible states of a TouchLocation object?

    Invalid (indicating an error occurred), Moved (indicating a single touch has changed position), Pressed (indicating a new touch location was pressed), and Released (indicating that the touch location was released).

  1. Why does Brody the Bootlegger get angry with Jerry Seinfeld during the movie Death Blow?

    Because Jerry made a comment about the large bag of candy that Brody was chowing down on all by himself:

    Kramer: There’s Brody. Brody! Over here…

    Brody: Hey, Kramer. And you must be Jerry. Thanks for the ticket.

    Jerry: That’s quite a feed bag you’re workin’ on there.

    Brody: It’s for all of us. Is there a problem?

    Kramer: Brody, c’mon. He’s just kidding. He’s a joke maker. Tell him, Jerry.

    Jerry: I’m a joke maker.

Chapter 18: Multiplayer Games

Quiz Answers

  1. If you create a two-player split screen, what should you use for the camera’s aspect ratio to ensure that your graphics don’t look squished?

    Instead of using the width and height of the game window for your aspect ratio, you should use the width and height of the viewport for that split-screen view.

  2. Fact or fiction: networked games in XNA use a networking API that works on the PC and Xbox 360 but is different on the Zune.

    Fiction. The networking API in XNA is compatible with all three platforms (PC, Xbox 360, and Zune)—although each platform can communicate only with devices of its own type.

  3. What’s the difference between a peer-to-peer and a client/server network architecture?

    Peer-to-peer networks have no server, and all machines send data to all other machines in the network. Client/server networks have a server and one or more clients. All clients send data to the server, and the server broadcasts all messages to all clients.

  4. Which network type (peer-to-peer or client/server) is better?

    That totally depends on the type of game you’re creating. You’ll need to consider the number of players involved as well as how much information needs to be updated throughout the game.

  5. What will happen if you don’t call NetworkSession.Update in your game?

    The NetworkSession.Update call updates your session and pumps all network messages through the session. If you don’t call this method, the machines on the network will not be able to communicate and sync up in order for the game to be played.

  6. How do you force a user to sign in using the gamer services sign-in windows?

    Calling Guide.ShowSignIn makes the game window render a series of gamer services windows that allow players to sign into their online gamertags or local accounts.

  7. How do you send a message to another player in a networked XNA game?

    You’ll first need to write the data to a PacketWriter object using the PacketWriter.Write method. You’ll then send the data via the local gamer’s SendData method.

  8. How do you read a message from another player?

    You use a PacketReader object in the local gamer’s ReceiveData method to pull the packet from the network. You then use different methods of the PacketReader to pull out different types of data (for example, ReadInt32).

  9. When receiving a network message in XNA, how do you know what type of data is going to be read from the PacketReader and what that data means?

    You’ll typically want to create an enum indicating the type of each message. You’ll then send that enum value as the first item in every packet. When reading packets, you’ll read the message type and perform the action appropriate for that message type.

  10. What, according to Harry Dunne, is worse than his roommate, Lloyd Christmas, getting robbed by an old lady?

    In the movie Dumb and Dumber—one of the greatest films of our time—Harry and Lloyd go from bad to worse when Harry’s pet parakeet comes down with a sudden illness:

    Lloyd: I got robbed by a sweet old lady on a motorized cart. I didn’t even see it coming.

    Harry: Oh, no, no.

    Lloyd: Come on, Harry.

    Harry: It gets worse. My parakeet, Petey.

    Lloyd: Yeah?

    Harry: He’s dead.

    Lloyd: Oh, man, I’m sorry. What happened?

    Harry: His head fell off.

    Lloyd: His head fell off?

    Harry: Yeah. He was pretty old.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.5.15