XNA Camera 2d with zoom and rotation

07/01/2011 – By popular request updated to XNA 4.0, xna 3.1 code is still there too

One of the things I keep finding is people asking how to do a simple camera 2d in XNA. Today I decided to contribute with my own solution.

Most of the time the solution given is to have a class camera with a Vector2 position and when drawing the sprite batch to subtract the camera position to the sprite position itself. Although this work from my point of view it’s not elegant and you can’t have neat features like zooming and rotation.  So for my tutorial I’ll do all transformations using a Matrix.
Start off by creating the basic class Camera2d

public class Camera2d
   {
        protected float          _zoom; // Camera Zoom
        public Matrix             _transform; // Matrix Transform
        public Vector2          _pos; // Camera Position
        protected float         _rotation; // Camera Rotation
 
        public Camera2d()
        {
            _zoom = 1.0f;
            _rotation = 0.0f;
            _pos = Vector2.Zero;
        }
 
  }

Now that we have the basic setup onto the variables it’s time to create a couple of functions to manipulate the variables

// Sets and gets zoom
public float Zoom
        {
            get { return _zoom; }
            set { _zoom = value; if (_zoom < 0.1f) _zoom = 0.1f; } // Negative zoom will flip image
        }
 
 public float Rotation
        {
            get {return _rotation; }
            set { _rotation = value; }
        }
 
        // Auxiliary function to move the camera
        public void Move(Vector2 amount)
        {
           _pos += amount;
        }
       // Get set position
        public Vector2 Pos
        {
             get{ return  _pos; }
             set{ _pos = value; }
        }

And now for the function that calculates all the transformations

public Matrix get_transformation(GraphicsDevice graphicsDevice)
        {
            _transform =       // Thanks to o KB o for this solution
              Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *
                                         Matrix.CreateRotationZ(Rotation) *
                                         Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *
                                         Matrix.CreateTranslation(new Vector3(ViewportWidth * 0.5f, ViewportHeight * 0.5f, 0));
            return _transform;
        }

So now how can we use it?
Simple on your sprite batch begin you must add the camera transformation.

Camera2d cam = new Camera2d();
cam.Pos = new Vector2(500.0f,200.0f);
// cam.Zoom = 2.0f // Example of Zoom in
// cam.Zoom = 0.5f // Example of Zoom out
 
//// if using XNA 3.1
spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                        SpriteSortMode.Immediate,
                        SaveStateMode.SaveState,
                        cam.get_transformation(device /*Send the variable that has your graphic device here*/));
 
//// if using XNA 4.0
spriteBatch.Begin(SpriteSortMode.BackToFront,
                        BlendState.AlphaBlend,
                        null,
                        null,
                        null,
                        null,
                        cam.get_transformation(device /*Send the variable that has your graphic device here*/));
 
// Draw Everything
// You can draw everything in their positions since the cam matrix has already done the maths for you 
 
spriteBatch.End(); // Call Sprite Batch End

Found any errors? Please let me know.

185 Comments

  1. Nametaker

    i understand the basic premise of what your saying but technically i am awful at programming (Beginner). Could you write that part of the code as if you were talking to a complete novice? And where it goes, i know update but in game1 update, character update, camera2d update?

    Thanks

  2. nametaker

    please….: )

  3. Casey

    I am trying to make the camera go forward in which ever direction the camera is rotated. This is what I have so far but it doesn’t seem to work right.

    if (newKeyState.IsKeyDown(Keys.Up))
    {
    cam.Move(new Vector2(
    (float)(Math.Cos(cam.Rotation – MathHelper.PiOver2) * 5.0f),
    (float)(Math.Sin(cam.Rotation – MathHelper.PiOver2) * 5.0f)));
    }

    Thanks.

  4. Nametaker

    Thanks for your help but unfortunatly i still cant get it to work. I put the code in the update and most of the phrases it comes up with

    e.g “characterpos does not exist in the current context.”

    I’ll take a look at other tutorials as well to see what is wrong.

  5. Pingback: Create a 2D Camera in XNA GS 4.0 | Code && Thuender

  6. John Reynolds

    Hello! This is an awesome tutorial.

    I’m trying to implement a smooth target change, but I’m having trouble. Can you possible help?

    Here is what I have, though it doesn’t work at all =( (cam just keeps moving on vector)

    else if (target == “guy2”)
    {
    if (cam.Pos != guyVector2)
    {
    cam.Move(guyVector2);
    }
    }

    any ideas?

  7. John Reynolds

    Ok,

    So I’ve gotten this far:

    else if (target == “guy2”)
    {
    if (cam.Pos != guyVector2)
    {
    direction = guyVector2 – cam.Pos;
    direction.Normalize();
    cam.Pos = new Vector2
    (cam.Pos.X + direction.X,cam.Pos.Y + direction.Y);
    }
    }

    This works, but only sort of. It goes to the guy then stops, but the camera “shakes” and looks really weird. I think it’s because it may not be able to get exactly onto the guyVector2, so it kind of goes back and forth at the end.

    Any ideas on how to address this?

  8. The great noob Dev

    Hi ive used this to create a 2d camera in my tile game, but im having problems ‘transforming’ my mouse position. Thing is if i rotate or zoom the camera i cannot seem to compensate for the mouse position. As i zoom or rotate, the mouse x and y positions no longer represent the real world coordinates.
    I really need to get this sorted, can someone please help me out?
    Thanks a lot if you can.

  9. John Reynolds

    Casey,

    I’m having the same issue. I would like to move the camera )or a sprite the camera is attached to) in the direction the camera is rotated.

    Have you had any luck?

  10. Pingback: A 2D Camera | Zuhair Parvez

  11. Dayus

    It’s a nice camera once you get it working but my tiles aren’t correctly aligned if i dont use interger value (i can see some space between some tiles if i use float value) I corrected the situation making sure the matrix return some “integer” number. Also because of that i can’t use the zoom for now as it will cause to see some empty space between my tiles…

    I also had to change the line :
    Matrix.CreateTranslation(new Vector3(ViewportWidth * 0.5f, ViewportHeight * 0.5f, 0));

    to put 0.001f instead of 0.5f cause i wouldn’t see a thing otherwise.

    now i need to find a way to move the camera smoothly when i adjust the position of my player on a slope tile…right now it can go down 10-12 pixel down in one shot hence changing the Y coordinate of the camera and it feel sluggish..

    anyway thanks a lot for this tutorial tought i might go back to normal scrolling…

  12. Brassx

    A modification to the Get mouse position script above allows you to keep the matrix centered and still get the mouse position in world coordinates:

    [code]
    public Vector2 get_mouse_pos()
    {
    float MouseWorldX = (Mouse.GetState().X – graphicsDevice.Viewport.Width * 0.5f + (Game1.Camera.Pos.X) * (float)Math.Pow(Game1.Camera.Zoom, 1)) /
    (float)Math.Pow(Game1.Camera.Zoom, 1);

    float MouseWorldY = ((Mouse.GetState().Y – graphicsDevice.Viewport.Height * 0.5f + (Game1.Camera.Pos.Y) * (float)Math.Pow(Game1.Camera.Zoom, 1))) /
    (float)Math.Pow(Game1.Camera.Zoom, 1);

    return new Vector2(MouseWorldX, MouseWorldY);
    }
    [/code]

  13. Pingback: Resources for XNA indie developers | Indie Games Bundle - indieGamesPack.com

  14. Sigmoid

    Thanks for the tutorial, but I’m having problems. I got a null reference error in the get_transformation method, and it’s beyond me. Does anyone understand this problem?

  15. Nice! I knew about these transformation matrix thing, but I didn’t know exactly how to use them. Thanks David and Tux89!

  16. barjed

    Hey, I am having some severe problems with the camera breaking the drawing of objects on the screen. Things appear and disappear randomly. I described the problem here :

    http://forums.create.msdn.com/forums/p/85582/515490.aspx#515490

    and here:

    http://stackoverflow.com/questions/6486484/2d-camera-breaking-position-of-everything

    I would be very grateful for any help!

  17. Emilio

    Hello! thanks for the camera code, is perfect and work perfectly.
    But I don’t know how implent this in a wp7 game.
    If the camera is tourn off, when I want to detect a click over a rectangle, I do: rect.Contains(click)
    But, when I turn on the camera and I do a zoom in or zoom out I don’t know how know the correct position and dimensions of the rectangle to do the correct “Contains” of the click.
    How can I change this params? (Positions and dimensions of the rectangle)
    I’ve try the code that you posted for the mouse click but it don’t work to me.
    Thanks for all and sorry for my english, i’m spanish.
    Bye!

  18. Daggepagge

    Hi! When using this camera there’s total anarchy in the drawing order: If I draw texture B after texture A, texture A might aswell apear over texture B wich I don’t get..? Why is that? Great work anyhow.. thanks alot!

    protected override void Draw(GameTime gameTime)
            {
                GraphicsDevice.Clear(Color.CornflowerBlue);            
                cam.Pos = new Vector2(CamPos.X, CamPos.Y);
                // cam.Zoom = 2.0f // Example of Zoom in
                // cam.Zoom = 0.5f // Example of Zoom out          
     
                //// if using XNA 4.0
                spriteBatch.Begin(SpriteSortMode.BackToFront,
                                        BlendState.AlphaBlend,
                                        null,
                                        null,
                                        null,
                                        null,
                                        cam.get_transformation(GraphicsDevice /*Send the variable that has your graphic device here*/));            
     
                spriteBatch.Draw(Earth, new Vector2(0, 0), Color.White);           
                hero.Draw(spriteBatch);
     
                // Draw Everything
                // You can draw everything in their positions since the cam matrix has already done the maths for you 
     
                spriteBatch.End(); // Call Sprite Batch End           
     
                base.Draw(gameTime);
            }
  19. Daggepagge

    I changed the SpriteSortMode to immediate and now it seems to work just fine! thank you very much! regards from sweden..

  20. corroded_b

    Great job…as I just copy&pasted the code, I want to contribute a little. For anyone who needs mouse control for the transformations:

    You need those:

    Camera2D cam = new Camera2D();
    MouseState old_mouse;
     
     
    In the "void Update":
     
                if (Mouse.GetState().LeftButton == ButtonState.Pressed)
                {
                    cam.Move(new Vector2((Mouse.GetState().X - old_mouse.X) * -0.5f, (Mouse.GetState().Y - old_mouse.Y) * -0.5f));
                }
     
                if (Mouse.GetState().ScrollWheelValue != old_mouse.ScrollWheelValue)
                {
                    if (cam.Zoom &gt; 1)
                    {
                        cam.Zoom += (Mouse.GetState().ScrollWheelValue - old_mouse.ScrollWheelValue) / 120.0f / 10.0f;
                    }
                    else
                    {
                        cam.Zoom *= 1+(Mouse.GetState().ScrollWheelValue - old_mouse.ScrollWheelValue) / 120.0f / 20.0f;
                    }
                }
                if (Mouse.GetState().RightButton  == ButtonState.Pressed)
                {
                    cam.Rotation += ((Mouse.GetState().X - old_mouse.X) / 500.0f);
                    cam.Rotation += ((Mouse.GetState().Y - old_mouse.Y) / 500.0f);
                }
     
                old_mouse = Mouse.GetState();

    Hope html works here…

  21. Cleber

    Hi people.

    First of all, great tutorial. And great comments to. It´s a nice contribution.

    But I still dont get one thing. This “problem” has been said here. When I rotate my camera and press the button to go to the right, I would like to go to the right of the screen, not to the right of the camera, making my scene go “up”, for example.

    I figure out that I must use the method Move of the camera object with a parameter regarding the rotation of this camera, but I missed out the calculations…

    Any help?

    Thanks a lot.

  22. Thank you so much! I have made several XNA prototypes and always wondered how to use a 2d camera like this. I knew that it HAD to be something with Matrices but I am not really versed in 3d so I’m not used to working with them. From the tutorials I’ve gone through, these calculations look awfully familiar. I might have tried something myself had I realized that SpriteBatch.Begin() took a transformation matrix… go figure.

    Awesome job commenters on the contributions! Special thanks to Tux89 for the code to translate vectors between local and world. Works like a charm. Now, there must be a way to move the camera’s position intuitively based on screen orientation while rotated instead of based on world coordinates.

  23. Pingback: Summer 11 – Day 6Valryon.Blog() | Valryon.Blog()

  24. Gywox
    		public void Move(Vector2 direction)
    		{
    			this.position += direction;
    			this.update = true;
    		}
     
    		public void MoveDown(float amount)
    		{
    			this.position += new Vector2((float)(Math.Cos(-this.rotation + MathHelper.PiOver2) * amount), (float)(Math.Sin(-this.rotation + MathHelper.PiOver2) * amount));
    			this.update = true;
    		}
     
    		public void MoveUp(float amount)
    		{
    			this.position += new Vector2((float)(Math.Cos(-this.rotation - MathHelper.PiOver2) * amount), (float)(Math.Sin(-this.rotation - MathHelper.PiOver2) * amount));
    			this.update = true;
    		}
     
    		public void MoveLeft(float amount)
    		{
    			this.position += new Vector2((float)(Math.Cos(-this.rotation + MathHelper.Pi) * amount), (float)(Math.Sin(-this.rotation + MathHelper.Pi) * amount));
    			this.update = true;
    		}
     
    		public void MoveRight(float amount)
    		{
    			this.position += new Vector2((float)(Math.Cos(-this.rotation) * amount), (float)(Math.Sin(-this.rotation) * amount));
    			this.update = true;
    		}
  25. Marius Bughiu

    Thank you very much David for this nice piece of code.
    Also thanks BrassX for the mouse transformation.
    This was really useful for me.

  26. Steven

    Hi,

    Great code but i am wondering,
    Is there a way to see if an object is in/out sight of the camera??

    Thanks

  27. Pingback: mac

  28. Pingback: XNA 4.0 2D Camera Makes Sprite Jerky | trouble86.com

  29. Pingback: Mouse location is off due to camera | Q&A System

  30. truGamer

    how would i implement side-by-side or over-under stereoscopic 3d modes using this camera?

  31. baker
    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 Learning1
    {
     
        public class Game1 : Microsoft.Xna.Framework.Game
        {
            GraphicsDeviceManager graphics;
     
            SpriteBatch spriteBatch;
     
            KeyboardState newstate;
     
            public class Camera2d
            {
                protected float _zoom; // Camera Zoom
                public Matrix _transform; // Matrix Transform
                public Vector2 _pos; // Camera Position
                protected float _rotation; // Camera Rotation
     
                public Camera2d()
                {
                    _zoom = 1.0f;
                    _rotation = 0.0f;
                    _pos = Vector2.Zero;
                }
     
            }
     
     
            // Sets and gets zoom
            public float Zoom
            {
                get { return _zoom; } // Error
                set { _zoom = value; if (_zoom &lt; 0.1f) _zoom = 0.1f; } //Error Negative zoom will flip image
            }
     
            public float Rotation
            {
                get { return _rotation; }
                set { _rotation = value; }
            }
     
            // Auxiliary function to move the camera
            public void Move(Vector2 amount)
            {
                _pos += amount; // _pos (no ex)tension method to contain argument 
            }
            // Get set position
            public Vector2 Pos
            {
                get { return _pos; }
                set { _pos = value; }
            }
     
            public Matrix get_transformation(GraphicsDevice graphicsDevice)
            {
                _transform =       
                  Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *
                                             Matrix.CreateRotationZ(Rotation) *
                                             Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *
                                             Matrix.CreateTranslation(new Vector3(ViewportWidth * 0.5f, ViewportHeight * 0.5f, 0));
                return _transform;
            }
     
     
     
     
            public Game1()
            {
                graphics = new GraphicsDeviceManager(this);
                Content.RootDirectory = &quot;Content&quot;;
     
            }
     
     
     
     
     
     
            protected override void Initialize()
            {
                // TODO: Add your initialization logic here
     
                base.Initialize();
     
     
            }
     
     
     
            Texture2D myTexture;
            Vector2 Posp = new Vector2();
     
            Vector2 spritePosition = Vector2.Zero;
     
     
            Vector2 spriteSpeed = new Vector2(50.0f, 50.0f);
     
     
            protected override void LoadContent()
            {
     
                spriteBatch = new SpriteBatch(GraphicsDevice);
                myTexture = Content.Load("orange");
     
     
            }
     
     
            protected override void UnloadContent()
            {
     
            }
     
     
            protected override void Update(GameTime gameTime)
            {
     
                KeyboardState newstate = Keyboard.GetState();
     
                if (newstate.IsKeyDown(Keys.Right))
                {                                                  
                    Posp.X += 1;
                }
     
                else if (newstate.IsKeyDown(Keys.Left))
                {
                    Posp.X -= 1;
                }
     
                else if (newstate.IsKeyDown(Keys.Up))
                {
                    Posp.Y -= 1;
                }
     
                else if (newstate.IsKeyDown(Keys.Down))
                {
                    Posp.Y += 1;
                }
     
     
     
            }
     
     
            protected override void Draw(GameTime gameTime)
            {
                GraphicsDevice.Clear(Color.CornflowerBlue);
                Camera2d cam = new Camera2d();
                cam.Pos = new Vector2(500.0f, 200.0f);
     
     
                spriteBatch.Begin(SpriteSortMode.BackToFront,
                                   BlendState.AlphaBlend,
                                   null,
                                   null,
                                   null,
                                   null,
                                   cam.get_transformation(device /*Send the variable that has your graphic device here*/));
                spriteBatch.Draw(myTexture, Pos, Color.White);
                spriteBatch.End();
     
                base.Draw(gameTime);
            }
        }
    }

    hi , i really need help with that as i managed to move the char but when i am applying ur code to create the camera to my char it keep giving me error about the 4 variables ( _Pos , _zoom , _ rotation , _transform) beside the ( cam.get_transformation(device

  32. baker

    oh Nvm , i figured it out as i had lots of embarrising mistakes ( including i had 2 classes in the same main code 🙂 so it i sworking now except i had to move the camera constructor from “Draw” stage cuz that is crashing my pc as it keep creating the camera in every update/sec ….. your code was so helpful .. thank you

  33. WarrenK

    I am obviously asking a dumb question here but is there a link somewhere to the source code that I have missed?

  34. It sais to me the name ViewportWidth and ViewportHeight aren’t declared.

    Help?

  35. Nothing happens =(

    Please send me the source code. If there is, I can’t find him 😀

  36. Cathal

    Hi how would i go about moving the sprites around. so if i place a sprite in the top corner i can navigate the world but that sprite is still in the top corner?

  37. Pingback: Ridiculously simple XNA camera scrolling « Ben Coveney

  38. Eu

    Someone can help me with a method like: public Vector2 ConvertScreenToWorld (Vector2 p)? Mine does not work when have changes in the rotation or zoom.

  39. Eu

    Hi Davide, thanks for the fast answer! I tested the code, and was almost perfect. Only when I change the default zoom the coordinates do not match. See the code:

    OBS: I Split the transformation in World + View + Projection.

    public Matrix getTransformation()
    {
    	return Matrix.Identity * _view * _projection;
    }
    //...
    public void Update()
    {
    	_view = Matrix.CreateTranslation(-_position.X, -_position.Y, 0)
    			* Matrix.CreateRotationZ(_rotation)
    			* Matrix.CreateScale(_zoom, _zoom, 1);
    	_projection = Matrix.CreateTranslation(new Vector3(_graphicsDevice.Viewport.Width * 0.5f, _graphicsDevice.Viewport.Height * 0.5f, 0));			
    }
    //...
    public Vector2 ConvertScreenToWorld (Vector2 p)
    {
    	Matrix CamMatrix = Matrix.CreateTranslation(new Vector3(-_position, 0))
    						* Matrix.CreateRotationZ(_rotation)
    						* Matrix.CreateTranslation(new Vector3(_screenCenter, 0))
    						* Matrix.CreateScale(new Vector3(_zoom, _zoom, 1));
     
    	return Vector2.Transform(p, Matrix.Invert(CamMatrix));
    }

    I took _origin as the center of the screen, correct?
    Anyway…
    In others many tests, the solution was:

    	return Vector2.Transform(p, Matrix.Invert(getTransformation()));

    Something already reviewed by Tux89 in previous post. At least I see a contribution in the view and projection. Thanks and sorry!
    []

  40. Josh

    Any idea why my camera view starts in the middle of the screen? The top half and the left half of my screen are the spritebatch background color. The camera seems to work just fine, it’s just in the wrong position.

  41. Josh Riley

    This post is getting old, but it was still super helpful to me today. I’ve seen this question asked a few times, but I haven’t seen an answer yet.

    How can I get a rectangle to represent whats actually within the game window?

    I tried playing with Viewports, but I don’t think I understand fully how they work?

  42. Pingback: Light mask map and camera for static lights in XNA Platformer | Q&A System

  43. SkarStoker

    thank you very much for your tutorial, served me a lot …

    On the other hand, if need to place the origin of the camera to vector.zero and not in the center screen, change this line when the translation creates matrix

    Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *

    by this other…

    Matrix.CreateTranslation(new Vector3(-_pos.X - (viewport.Width / 2),-_pos.Y - (viewport.Height / 2), 0)) *

    Atte
    SkarStoker.

  44. SkarStoker

    I have only one last question, that algorithm would work better, to create a occlution to the camera (which only draw, so the camera shows) …

  45. Pingback: Too much Minecraft « The Greatest Dane

  46. jake

    Thanks for the tutorial it helps alot..
    have a question.. how would you be able to exclude some objects from been moved with the cam..
    for example i have a background that i want to be able to zoom in and out..
    your sample is making that work just fine. but i also have a control stick that i dont want to move with the camera.
    how would i exclude it if both the background and the control stick are in the same draw method

    ScreenManager.SpriteBatch.Begin(SpriteSortMode.BackToFront, 
                    BlendState.AlphaBlend, 
                    null, 
                    null, 
                    null, 
                    null, 
                    cam.get_transformation(ScreenManager.Game.GraphicsDevice));
     
                // Draw the background
                ScreenManager.SpriteBatch.Draw(background, new Rectangle(0, 0, ScreenManager.Game.GraphicsDevice.Viewport.Width,
                    ScreenManager.Game.GraphicsDevice.Viewport.Height), null, Color.White, 0f, Vector2.Zero, SpriteEffects.None, 1);
     
                // Draw the go button
                ScreenManager.SpriteBatch.Draw(goButton, goButtonPosition, Color.White);
     
                // Draw the pause button
                ScreenManager.SpriteBatch.Draw(pauseButton, pauseButtonPosition, Color.White);
     
                ScreenManager.SpriteBatch.End();
     
                base.Draw(gameTime);
  47. jomo1911

    Hello David!

    Thank you for such a great tutorial. I adopted the camera class to my game, and now I have a question.

    Is it possible to “tell” the camera that the User Interface(UI), and the background should stay fix, and not be influenced by the camera?

  48. Thanks for the tutorial. It has been really helpful. I am creating a Tiled (TMX) Map Editor for the Windows Phone (7/8) using the XNA content pipeline.

  49. TysonBlue

    Thanks alot for this tutorial, i had to make some edits but it worked very well. Thanks alot again.

  50. Koja

    Hi David, nice camera. I have a problem, everything works fine but after few seconds camera starts to lag. I’m getting little delay. I don’t know why this is happening. I have only 4 sprites drawn and I tested this on several computers. Can you please help me?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.