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.
Hi, I think that I solved the problem with camera delay. I just added this line: graphics.PreferMultiSampling = true; and camera is working fine in full screen too. Thanks for this awesome tutorial!
Hi David,
Thanks a lot for the tutorial. I have a question regarding zooming at a specific spot and camera size.
I am making an RTS and I want the camera to zoom at the point on the screen where the cursor is. I am using a touch interface of windows phone, so I want the camera to zoom at the point where the user has put his fingers and performed the pinch gesture.
In other words, I want the center of the camera to shift to the point where the zoom is being performed.
Another problem is that, I have limited the camera in a rectangle. It works well as long as I am zoomed out and drag the camera around. Say that I drag and reach the top-right corner in the zoomed out state. now when I zoom in, I can no longer see what is present at the top right corner as the camera size remains the same, but it has zoomed in to the center.
I gather that I will need to change the size of the camera as I zoom in. How can this best be done?
Great code, saved me ages and opened up lots of possabilities. Thanks. But I’ve had a problem where if at ANY time I switch rendertarget, then return to the default rendertarget, either in a component, class or in the main loop the screen goes purple. I’ve only had this problem when using the camera and cant figure it out. Any idea?
Forget my previous post, I was switching renderTarget in the Draw call, I’m now doing it in the update loop, creating the texture and then drawing the result it in the draw call. it’s now working as I’d intended. Thanks again though for the great blog!
Great post, helped me figure out the glitch in the code I was using (it was calculating the middle of the screen after transformations were performed, creating a ghost image whenever I zoomed in for one frame)
hey,
thank you for the tutorial (=
but I have a question:
I use a Leveleditor to create the game-levels.
You can choose “blocks” and you can set them somewhere in the editor. Then you save this file and open it while playing the current level in game.
The problem: When you want to zoom / rotate the “blocks” the level starts “flashing” – so there are white lines that are blinking the whole time while you are zooming (on the border of every tile-block)
In an other forum I asked for this and sb. told me to render the level or I should try to create “overlapping tiles” (so that the tiles overlap each other). I tried that but It doesn´t work…
do you know what the problem is.. because I don´t know how to render my level- so maybe there is an other way to solve the problem?!
thank you (=
i am just learning c#/xna!
There is an error:
The name “ViewportWidth” in the current context does not exist.
The name “ViewportHeight” in the current context does not exist.
I noticed that the coordinate system doesn’t change when you zoom in the camera.
I made a tweak and now works: in the function “get_transformation”, change:
“new Vector3(-_pos.X, -_pos.Y, 0))”
in “new Vector3(-_pos.X * Zoom, -_pos.Y * Zoom, 0))”
(notice the *Zoom). You may want to change the code in the example.
@lolxdfly
public Matrix get_transformation(GraphicsDevice graphicsDevice)
{
Viewport viewPort= graphicsDevice.Viewport;
// Name of Graphics device, from the function get_
// rename the ViewportWidth to call the new viewPort.Width; we just added.
Matrix.CreateTranslation(new Vector3(viewPort.Width * 0.5f, viewPort.Height * 0.5f, 0));
Alternatively you do something like
var ViewportWidth = graphicsDevice.Viewport.Width;
Hi can u explain what is this graphic device? in you parameter ing cam.get_transformation? Sorry im just a noooobie in xna
This is awesomely simple and well explained. Thank you!
Glad it was helpful 🙂
Excellent! It works!
I just need the zoom to zoom in on the centre instead of the top-left.
Thank you, David.
Hi, to make the zoom on somewhere else you just need to add an extra property to the camera.
A Vector2 _origin, set the origin to half of the screen size and when constructing the matrix something like
_transform = Matrix Translation (Pos) * Matrix Rotation * Matrix Scale * Matrix Translation (origin)
Hope it helps
This tutorial has been really helpful! I was reading this comment about zooming in on the center and I have a question. I need to do something similar. I basically have a single map texture that I draw on the screen with icon textures on top of that. I am using the camera for pan, rotate, and zoom. When the map hasn’t been panned, the zoom (using scroll wheel in 0.25 zoom increments) goes in and out from the center of the map just how you would want it to. However when the map has been panned, the zooming ultimately cause the map texture to shift until it’s off the screen. What I think I’d like to be able to do is have the zoom be centered around the mouse location. Tried grabbing the mouse location and setting the origin to that but it doesn’t seem like it’s really zooming in on that location. Any advice? I’m relatively new to XNA so this might be a noob question.
Hi David,
Thanks for the great tutorial, you really helped out with my project!
I see in your video that the bottom left text does not zoom with the rest of the game. Could you please show how this can be done? So far I can make the text move next to the camera, but if the Camera.Zoom 1.0f then the text is out of place.
Many thanks in advance for your assistance!
Just draw the text in a second batch but this time with a different camera at position 0,0 and no zoom 🙂
Ah I see! I will look into using multiple batches in my game.
Thanks very much for the prompt reply!
it’s very useful. thanks alot sir
Thank you so so so so mcuh!
I’m learning XNA by creating my own RPG game from scratch and thought that I was “too far” into the process to add camera stuff, thinking I’d need to rewrite a bunch of code. But nope, this tutorial was incredibly simple and straightforward, and I only needed to change one line after writing up a camera class.
Thanks for posting this, it certainly demystified a bunch of XNA stuff for me!
Thanks Works good, i solve my problem of resolution of 1920×1080 whit the zoom. Great!!
so I am getting Null Reference Exception on the draw, any idea why/how to fix?
thank very much