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.
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
please….: )
You should place that code on your Game1 Update function.
Once again remember you should find out which variables correspond to your real situation. Screenwidth and Screenheight is most probably 1280×720 (Pc/Xbox) or 800×480 (WP7)
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.
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.
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?
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?
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.
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?
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…
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]
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?
Nice! I knew about these transformation matrix thing, but I didn’t know exactly how to use them. Thanks David and Tux89!
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!
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!
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!
I changed the SpriteSortMode to immediate and now it seems to work just fine! thank you very much! regards from sweden..
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:
Hope html works here…
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.
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.
Thank you very much David for this nice piece of code.
Also thanks BrassX for the mouse transformation.
This was really useful for me.
Hi,
Great code but i am wondering,
Is there a way to see if an object is in/out sight of the camera??
Thanks
how would i implement side-by-side or over-under stereoscopic 3d modes using this camera?
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
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
I am obviously asking a dumb question here but is there a link somewhere to the source code that I have missed?
Nope, the source is on the Post itself
It sais to me the name ViewportWidth and ViewportHeight aren’t declared.
Help?
That’s because you haven’t declared any vars with that name. That’s just for helping understand the code.
There you should use the vars that contain your Viewport Size
Nothing happens =(
Please send me the source code. If there is, I can’t find him 😀
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?
You can change the sprite position when using SpriteBatch::Draw, the camera position is only used pass the Matrix to SpriteBatch::Begin
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.
I don’t have any code around with that but from the top of my head I think it goes something like
CamMatrix = Matrix::CreateTranslation(Vector3(-pos, 0)) *
Matrix::CreateRotationZ(_rotation) *
Matrix::CreateTranslation(Vector3(_origin, 0))*
Matrix::CreateScale(Vector3(_zoom, _zoom, 1));
TransformedMousePos = Vector2::Transform(MousePos,Matrix::Invert(CamMatrix));
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.
I took _origin as the center of the screen, correct?
Anyway…
In others many tests, the solution was:
Something already reviewed by Tux89 in previous post. At least I see a contribution in the view and projection. Thanks and sorry!
[]
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.
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?
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
by this other…
Atte
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) …
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
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?
Sure. Just create 2 cameras. One for the moving part and another one for GUI and all other static stuff. Then make 2 draws, one for UI stuff and another for the moving parts each with it’s corresponding camera
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.
Thanks alot for this tutorial, i had to make some edits but it worked very well. Thanks alot again.
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?
It’s the first time I hear about this. I’ve used that camera code even with thousandths of sprites. Do you have that problem if you deactivate the camera? If you do then the problem is somewhere else