So, I’m making a 3rd person game in XNA. I’ve created a camera that can pitch within constraints and should be able to yaw the full 360.
The class is seen below:
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 Assignment_3_Redesign
{
/// <summary>
/// This is a game component that implements IUpdateable.
/// </summary>
public class Camera : Microsoft.Xna.Framework.GameComponent
{
//Constrain where our camera can wander in the X and Z plane
public Rectangle XZBoundingBox;
//Angular bounds for camera.
float minPitchIncr = -12;
float maxPitchIncr = 38;
float currentPitchIncr = 12;
float currentYawAngle = 0;
//Matrices to pass to effects.
public Matrix view { get; protected set; }
public Matrix projection { get; protected set; }
//Important directional vars.
public Vector3 pos;
public Vector3 look;
private Vector3 up;
//Mouse Input
//X -- yaw; Y-- pitch
private MouseState prevMouseState;
private MouseState tempMS;
private KeyboardState keyboardState;
//Speed to move camera at for translates.
float Speed = 3;
public Camera(Game game, Vector3 pos, Vector3 target, Vector3 up, Rectangle bounding)
: base(game)
{
this.pos = pos;
this.look = (target - pos);
this.look.Normalize();
this.up = up;
this.view = Matrix.CreateLookAt(pos, target, up);
XZBoundingBox = bounding;
this.projection = Matrix.CreatePerspectiveFieldOfView
(MathHelper.PiOver4,
(float)Game.Window.ClientBounds.Width /
(float)Game.Window.ClientBounds.Height,
1, 500);
}
private void CreateLookAt()
{ view = Matrix.CreateLookAt(pos, pos + look, up); }
//Returns current camera direction as vector.
public Vector3 GetCameraDirection
{ get { return look; } }
///
public override void Initialize()
{
// TODO: Add your initialization code here
Mouse.SetPosition(Game.Window.ClientBounds.Width / 2,
Game.Window.ClientBounds.Height / 2);
prevMouseState = Mouse.GetState();
base.Initialize();
}
/// <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)
{
Vector3 tempPos= new Vector3(0.0f,0.0f,0.0f);
//Get keyboard commands
keyboardState = Keyboard.GetState();
/*
//Select object
if (keyboard.IsKeyDown(Keys.D1))
objSelected = 0;
if (keyboard.IsKeyDown(Keys.D2))
objSelected = 1;*/
//Translation
if (keyboardState.IsKeyDown(Keys.A)) //strafe +X (rel)
{
tempPos -= Vector3.Cross(up, look) * Speed;
tempPos.Y = 0.0f;
if (XZBoundingBox.Contains((int)(pos - Vector3.Normalize(tempPos) * Speed).X,
(int)(pos - Vector3.Normalize(tempPos) * Speed).Z))
pos -= Vector3.Normalize(tempPos) * Speed;
}
if (keyboardState.IsKeyDown(Keys.D)) //strafe -X (rel)
{
tempPos -= Vector3.Cross(up, look) * Speed;
tempPos.Y = 0.0f;
if (XZBoundingBox.Contains((int)(pos - Vector3.Normalize(tempPos) * Speed).X,
(int)(pos - Vector3.Normalize(tempPos) * Speed).Z))
pos += Vector3.Normalize(tempPos) * Speed;
}
if (keyboardState.IsKeyDown(Keys.W)) //+Z (rel)
{
tempPos += look * Speed;
tempPos.Y = 0.0f;
if (XZBoundingBox.Contains((int)(pos - Vector3.Normalize(tempPos) * Speed).X,
(int)(pos - Vector3.Normalize(tempPos) * Speed).Z))
pos += Vector3.Normalize(tempPos) * Speed;
}
if (keyboardState.IsKeyDown(Keys.S)) //-Z (rel)
{
tempPos -= look;
tempPos.Y = 0.0f;
if (XZBoundingBox.Contains((int)(pos - Vector3.Normalize(tempPos) * Speed).X,
(int)(pos - Vector3.Normalize(tempPos) * Speed).Z))
pos -= Vector3.Normalize(tempPos) * Speed;
}
//Get mouse commands to control camera direction;
//Yaw rot.
tempMS = Mouse.GetState();
if (tempMS != prevMouseState)
{
currentYawAngle += (tempMS.X - prevMouseState.X) * (-MathHelper.PiOver4 / 150);
currentYawAngle = MathHelper.WrapAngle(currentYawAngle);
if (currentPitchIncr + (tempMS.Y - prevMouseState.Y) < maxPitchIncr &&
currentPitchIncr + (tempMS.Y - prevMouseState.Y) > minPitchIncr)
currentPitchIncr += tempMS.Y - prevMouseState.Y;
if (currentPitchIncr > maxPitchIncr) currentPitchIncr = maxPitchIncr;
else if (currentPitchIncr < minPitchIncr) currentPitchIncr = minPitchIncr;
look = Vector3.Transform(Vector3.Forward,
Matrix.CreateFromYawPitchRoll(currentYawAngle,
currentPitchIncr * (-MathHelper.PiOver4 / 150), 0.0f));
}
//reset prevMouseState
prevMouseState = Mouse.GetState();
base.Update(gameTime);
CreateLookAt();
}
}
}
Now this does what I want … almost. The problem is that when I’m rotating, it won’t go past Pi/-Pi in the yaw. In other words, you can’t do a full 360 spin.
Am I making a mistake in some conditional? (I wouldn’t doubt it… I’ve been staring at it for far too long, playing with it and breaking things.)
Here’s an example declaration:
camera = new Camera(this, new Vector3(0, 20, 40), Vector3.Zero, Vector3.Up,
new Rectangle(-800,-800,1600,1600));
Also any thoughts on the camera class as a whole?
Thanks for the help!
EDIT 1
This is technically for a #classproject. Wanted to mention that, for clarity’s sake. The assignment is open ended and I’m trying to put more time into it because I want to make a solid framework to make WinPhone games…
You can always ‘reset’ the mouse cursor back to the middle of your screen. This way you can have unlimited motion
When using this technique, make sure you hide the original mousepointer, since it looks very bad. If you still need a mousepointer to be visible, just blit your own mousepointer on the mouse coordinates you track yourself.