Upcoming Events
Southwest Gaming Expo
11/20 - 11/22 @ Dallas, TX

Workshop on Network and Systems Support for Games (NetGames 2009)
11/23 - 11/25 @ Paris, France

ICIDS 2009 Interactive Storytelling
12/9 - 12/11 @ Guimarães, Portugal

Global Game Jam
1/29 - 1/31  

More events...


Quick Stats
6851 people currently visiting GDNet.
2341 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!



Link to us

Link to us

  Intel sponsors gamedev.net search:   

Microsoft XNA Game Studio 3.0 Unleashed
Creating a 3D Game


Creating the Crosshair

To allow better aiming, we need to add a crosshair to our screen. To start, we need to add the following texture to our member field list of the PlayingState class:
private Texture2D crossHair;
Inside our Draw method we need to add the following code:
OurGame.SpriteBatch.Begin();
if (OurGame.DisplayCrosshair)
{
   OurGame.SpriteBatch.Draw(crossHair, new Rectangle(
     (GraphicsDevice.Viewport.Width - crossHair.Width) / 2,
     (GraphicsDevice.Viewport.Height - crossHair.Height) / 2,
     crossHair.Width, crossHair.Height), Color.White);
}
OurGame.SpriteBatch.End();
We actually populate the crosshair texture inside our LoadContent method:
protected override void LoadContent()
{
   crossHair = Content.Load<Texture2D>(@”Textures\crosshair”);
   base.LoadContent();
}
The texture can be found in the usual place. Later, we are going to provide an option to turn on and off the crosshair, so we need to create the public Boolean DisplayCrosshair field in our TunnelVision game code. It should be initialized to true. Now, we can more easily see where we are aiming!

Creating the Game-Specific Camera

Now we are going to add a new camera directly to our game. We are not going to add this to the XELibrary because it is a special camera that most likely will not be reused. The purpose of this new camera is to handle input a little differently and to restrict movement. The code for the new TunnelVisionCamera.cs file is as follows:
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using XELibrary;

namespace TunnelVision
{
   public partial class TunnelVisionCamera : Camera
   {
   private float spinLeft = 0;
   private float spinRight = 0;
   private float spinDown = 0;
   private float spinUp = 0;
   
   private float spinLeftChange = 0;
   private float spinRightChange = 0;
   private float spinDownChange = 0;
   private float spinUpChange = 0;
   
   public TunnelVisionCamera(Game game) : base(game) {}
   
   public override void Update(GameTime gameTime)
   {
      if (!UpdateInput)
      return;
      
      float timeDelta = (float)gameTime.ElapsedGameTime.TotalSeconds;
      
      if (input.KeyboardState.IsKeyDown(Keys.Left))
         spinLeftChange += .1f;
      else
         spinLeftChange -= .1f;
      spinLeftChange = MathHelper.Clamp(spinLeftChange, 0, 1);
      spinLeft = spinLeftChange;
      if (input.GamePads[playerIndex].ThumbSticks.Left.X < 0)
         spinLeft = -input.GamePads[playerIndex].ThumbSticks.Left.X;
      if (spinLeft > 0)
         cameraYaw += (Utility.PowerCurve(spinLeft) * SpinRate * timeDelta);
      
      if (input.KeyboardState.IsKeyDown(Keys.Right))
         spinRightChange += .1f;
      else
         spinRightChange -= .1f;
      spinRightChange = MathHelper.Clamp(spinRightChange, 0, 1);
      spinRight = spinRightChange;
      if (input.GamePads[playerIndex].ThumbSticks.Left.X > 0)
         spinRight = input.GamePads[playerIndex].ThumbSticks.Left.X;
      if (spinRight > 0)
         cameraYaw -= (Utility.PowerCurve(spinRight) * SpinRate * timeDelta);
      
      if (input.KeyboardState.IsKeyDown(Keys.Down))
         spinDownChange += .1f;
      else
         spinDownChange -= .1f;
      spinDownChange = MathHelper.Clamp(spinDownChange, 0, 1);
      spinDown = spinDownChange;
      if (input.GamePads[playerIndex].ThumbSticks.Left.Y < 0)
         spinDown = -input.GamePads[playerIndex].ThumbSticks.Left.Y;
      if (spinDown > 0)
         cameraPitch -= (Utility.PowerCurve(spinDown) * SpinRate *
             timeDelta);
      
      if (input.KeyboardState.IsKeyDown(Keys.Up))
         spinUpChange += .1f;
      else
         spinUpChange -= .1f;
      spinUpChange = MathHelper.Clamp(spinUpChange, 0, 1);
      spinUp = spinUpChange;
      if (input.GamePads[playerIndex].ThumbSticks.Left.Y > 0)
         spinUp = input.GamePads[playerIndex].ThumbSticks.Left.Y;
      if (spinUp > 0)
         cameraPitch += (Utility.PowerCurve(spinUp) * SpinRate *
             timeDelta);
      
      //reset camera angle if needed
      if (cameraYaw > 80)
         cameraYaw = 80;
      else if (cameraYaw < -80)
         cameraYaw = -80;
      
      //keep camera from rotating a full 90 degrees in either direction
      if (cameraPitch > 89)
         cameraPitch = 89;
      if (cameraPitch < -89)
         cameraPitch = -89;
      
      Matrix rotationMatrix;
      Vector3 transformedReference;
      
      Matrix.CreateRotationY(MathHelper.ToRadians(cameraYaw),
         out rotationMatrix);
      
      //add in pitch to the rotation
      rotationMatrix = Matrix.CreateRotationX(
         MathHelper.ToRadians(cameraPitch)) * rotationMatrix;
      
      // Create a vector pointing the direction the camera is facing.
      Vector3.Transform(ref cameraReference, ref rotationMatrix,
         out transformedReference);
      // Calculate the position the camera is looking at.
      Vector3.Add(ref cameraPosition, ref transformedReference,
         out cameraTarget);
      
      Matrix.CreateLookAt(ref cameraPosition, ref cameraTarget,
         ref cameraUpVector, out view);
      }
   }
}
This is very similar to the base object’s Update method, except that we are restricting movement. We only allow the camera to move 80 degrees left or right. The pitch did not change. Instead of going through the entire class, line by line, we’ll just look at the section of code that handles if the user rotated to the left and infer how the rest of the movements work:
if (input.KeyboardState.IsKeyDown(Keys.Left))
   spinLeftChange += .1f;
else
   spinLeftChange -= .1f;
spinLeftChange = MathHelper.Clamp(spinLeftChange, 0, 1);
spinLeft = spinLeftChange;
if (input.GamePads[playerIndex].ThumbSticks.Left.X < 0)
   spinLeft = -input.GamePads[playerIndex].ThumbSticks.Left.X;
if (spinLeft > 0)
   cameraYaw += (Utility.PowerCurve(spinLeft) * SpinRate * timeDelta);
During game play, the keyboard movement was too fast and too jerky. To solve this, we build up our spin left value. In the base class, it simply gets set to 1. Here, we are adding 10% each frame and clamping the results to 1. Now we can tap the keyboard to have more precise control over targeting our enemies. If the game pad is used to rotate the camera, we use a new helper method to produce a curve in the movement. In the Utility.cs file in the XELibrary we can add the following code
Private const float power = 3;
public static float PowerCurve(float value)
{
   return ((float)Math.Pow(Math.Abs(value), power) * Math.Sign(value));
}
The PowerCurve helper method provides a curve we can apply to the values our thumbstick produces. Instead of strictly using the value of the thumbstick, we are making the low values lower, which gives us more control. Now as we barely move the thumbstick, the camera will barely move. And when we move the controller to target our enemies, we have more precise control.

We need to modify our Camera object in the XELibrary. The following public member field needs to be created:

public bool UpdateInput = true;
The following condition needs to be added at the very top of the Update method:
if (!UpdateInput)
{
   base.Update(gameTime);
   return;
}
We also want to change the modifier for the cameraReference, cameraTarget, cameraUpVector, view, cameraYaw, and cameraPitch fields to be protected instead of private. The private const spinRate and moveRate should be changed to public and no longer be a const because we need to be able to set them anywhere.

To use this new camera, we need to change our TunnelVision game class to use TunnelVisionCamera instead of Camera. We also need to modify our PlayingState class to use the new UpdateMethod Boolean property. Inside the StateChanged method we need to add the following code to the first branch of our condition:

OurGame.Camera.UpdateInput = false;
We need to do the opposite in the else of our if condition:
OurGame.Camera.UpdateInput = true;
Inside the constructor of PlayingState, we need to add the following code to set the move rate and spin rate of our camera:
OurGame.Camera.MoveRate = 10;
OurGame.Camera.SpinRate = 60;
We have improved our input handling and have restricted movement on our camera so we only rotate 80 degrees to the left or right instead of the full 360 degrees.

Summary

In this chapter, we have laid the foundation for creating our game. We have put into place all the game logic. There were not really any new concepts in this chapter, but by putting what you learned in previous chapters into practice, we should be well on our way to creating our own masterpieces. The next two chapters are spent updating our game states and UI enhancements.

Reproduced from the book Microsoft XNA Game Studio 3.0 Unleashed. Copyright? 2009. Reproduced by permission of Pearson Education, Inc., 800 East 96th Street, Indianapolis, IN 46240. Written permission from Pearson Education, Inc. is required for all other uses



Contents
  Creating the Tunnel Vision Game
  Creating the Game Logic Part 1
  Creating the Game Logic Part 2
  Creating the Crosshair & Camera

  Printable version
  Discuss this article