Microsoft XNA Game Studio 3.0 Unleashed
Creating a 3D Game
Creating the CrosshairTo 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 CameraNow 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. SummaryIn 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
|
|