Question

I'm trying to create an First Person View camera in XNA 4.0. I got the camera working and my scene renders fine. My movement with the W, A, S, D works fine as well but I cannot figure out why my Mouse rotations are messed up.

When I try to look Up with the camera, it tilts my entire world and looking Left & Right does the same thing and both look in the same direction. Eventually, my entire world flips :S.

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 Brandon
{
    public class FPC : GameComponent
    {   
        private KeyboardState OldKeyboardState;
        private MouseState OldMouseState;

        private Vector3 Target = Vector3.Zero;
        private Vector3 UpVector = Vector3.Up;
        private Vector3 Position = new Vector3(0.0f, 0.0f, 1.0f);
        private Vector2 Velocity = Vector2.Zero;
        private Matrix View = Matrix.Identity;
        private Matrix Projection = Matrix.Identity;
        private BasicEffect Effects = null;
        private float Speed = 5.0f;
        private float WalkingSpeed = 1.0f;
        private float RotationSpeed = 0.1f;
        private float AngleX = 0.0f;
        private float AngleY = 0.0f;

        public BasicEffect Effect
        {
            get { return this.Effects; }
        }

        public FPC(Game game) : base(game)
        {
            Mouse.SetPosition(Game.GraphicsDevice.Viewport.Width / 2, Game.GraphicsDevice.Viewport.Height / 2);
            Game.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
            this.Effects = new BasicEffect(Game.GraphicsDevice);
            this.OldMouseState = Mouse.GetState();
        }

        public override void Initialize()
        {
            base.Initialize();
        }

        public void LookAt(float FOV_Degrees, float NearPlaneDistance, float FarPlaneDistance, Vector3 Position, Vector3 Target, Vector3 UpVector)
        {
            this.Position = Position;
            this.Target = Target;
            this.UpVector = UpVector;
            this.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(FOV_Degrees), Game.GraphicsDevice.Viewport.AspectRatio, NearPlaneDistance, FarPlaneDistance);
            this.View = Matrix.CreateLookAt(Position, Target, UpVector);
        }

        private void UpdateView(Matrix rotationMatrix)
        {
            Vector3 finalTarget = this.Position + Vector3.Transform(this.Target, rotationMatrix);
            Vector3 finalUp = Vector3.Transform(this.UpVector, rotationMatrix);
            this.View = Matrix.CreateLookAt(this.Position, finalTarget, finalUp);
        }

        private void ProcessInput(GameTime gameTime)
        {
            bool isWalking = false;
            KeyboardState CurrentKeyboardState = Keyboard.GetState();

            Vector3 pos = Vector3.Zero;
            if (CurrentKeyboardState.IsKeyDown(Keys.W)) pos.Z -= 1.0f;
            if (CurrentKeyboardState.IsKeyDown(Keys.S)) pos.Z += 1.0f;
            if (CurrentKeyboardState.IsKeyDown(Keys.A)) pos.X -= 1.0f;
            if (CurrentKeyboardState.IsKeyDown(Keys.D)) pos.X += 1.0f;
            if (CurrentKeyboardState.IsKeyDown(Keys.LeftShift)) isWalking = true;
            this.OldKeyboardState = CurrentKeyboardState;

            if (pos != Vector3.Zero)
            {
                pos.Normalize(); //So we don't move faster diagonally
                pos *= (float)gameTime.ElapsedGameTime.TotalSeconds * (isWalking ? this.WalkingSpeed : this.Speed); //Smooth movement
            }

            this.ProcessMouseInput(gameTime);

            Matrix rotationMatrix = Matrix.CreateRotationX(this.AngleX) * Matrix.CreateRotationY(this.AngleY);
            this.Position += (isWalking ? this.WalkingSpeed : this.Speed) * Vector3.Transform(pos, rotationMatrix);
            this.UpdateView(rotationMatrix);
        }


        //Rotate the camera using the mouse.
        private void ProcessMouseInput(GameTime gameTime)
        {
            float amount = (float)gameTime.ElapsedGameTime.TotalSeconds;
            MouseState mouse = Mouse.GetState();
            if (mouse != this.OldMouseState)
            {
                int xDist = mouse.X - (Game.GraphicsDevice.Viewport.Width / 2);
                int yDist = mouse.Y - (Game.GraphicsDevice.Viewport.Height / 2);
                this.AngleX -= RotationSpeed * xDist * amount;
                this.AngleY -= RotationSpeed * yDist * amount;
            }

            Mouse.SetPosition(Game.GraphicsDevice.Viewport.Width / 2, Game.GraphicsDevice.Viewport.Height / 2);
            this.OldMouseState = mouse;
        }

        //Return a matrix for use with rendering hands holding weapons.
        public Matrix ModelWorldMatrix(float xOffset, float yOffset, float zOffset, float scale)
        {
            Vector3 ModelPos = this.Position;
            ModelPos += this.Target * zOffset;
            ModelPos += Vector3.UnitY * yOffset;
            ModelPos += Vector3.UnitX * xOffset;

            return Matrix.CreateScale(scale) * Matrix.CreateRotationX(MathHelper.ToRadians(this.AngleX)) * Matrix.CreateRotationY(MathHelper.ToRadians(this.AngleY)) * Matrix.CreateTranslation(ModelPos);
        }

        public override void Update(GameTime gameTime)
        {
            ProcessInput(gameTime);
            this.Effects.View = View;
            this.Effects.Projection = Projection;
            this.Effects.World = Matrix.Identity;
            base.Update(gameTime);
        }
    }
}

I've tried all sorts of sample code and followed all tutorials I could find but none of them have the same problems I have. They either don't work or they lag a ton!

Any ideas what I'm doing wrong?

Was it helpful?

Solution

You initialize this.Target to 0,0,0. This value never changes.

then later in your UpdateView method, you calculate finalTarget by transforming this.Target by a matrix representing a rotation only.

The result of

Vector3.Transform(this.Target, rotationMatrix) is essentially the same as

Vector3.Transform(Vector3.Zero, rotationMatrix).

Essentialy, that always results in (0,0,0) which should place finalTarget at the same position as this.Position except for floating point error that will give very small values to each component which factor in to cause your issue.

Try starting off by giving this.Target the value of 0,0,-1 (which causes the initial look direction to be the same as what your current code is trying to do, but gives the Vector3.Transform() something to work with) and you should be ok.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top