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, 0)) * 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 spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState, 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.
Bad Behavior has blocked 45 access attempts in the last 7 days.
54 Responses to XNA Camera 2d with zoom and rotation
Mike
October 23rd, 2009 at 6:00
This looks great, but I am either too new to understand it, or I am missing something, How do I invoke the changes by either using the keyboard or controller. I am trying to pass a new vector2 to cam.move, trying to set _pos… just cant get it. any help appreciated. Thanks
Mike
October 23rd, 2009 at 7:50
I posted the other response and just wanted to let you know that I got it figured out. I was trying to set the position the wrong way… I still have a problem with get:set: and stuff like that. But thank you so much for the camera option you have here. works great!
^DJ_Link^
October 23rd, 2009 at 7:56
For using get:set you have to do something like
for using the move
I hope this helped
Alexander
October 23rd, 2009 at 8:40
Nice camera! how can i make rotation and scaling around cameras center point? not around left top corner?
william
November 13th, 2009 at 17:19
1rst: Great tutorial and really helpfull source code.
However there are some things i would like to point out to anyone that may be getting a bit confused:
(alexander: look toward bottom of this post for your answer)
First (i did not realize this) the camera is separte from what you are rendering, thus whatever you put on the screen should be in real co-ordinates. I ran into a problem with a tilemap because i was setting the tiles to always being drawn starting at 0,0 and was wondering why when i moved the camera it wasnt showing the rest of the rather large map. What i did was draw the sprites as they would be in their world coordinates. This made it accurate as the camera moves around the map not the map moving around the camera!
Also pay particular attention to this line.
See somthing odd here. The code is taking zoom and multiplying it by itself 3x! This was throwing all my calcluations off. The first thing i did was set that line above to:
Now your zoom level is accurate to how much you are actually zooming.
Now lets concentrate on the last part of code:
This is basically going to adjust for the camera’s movement. (which with normal cameras would just be – posx and – posy right? Well think about what that assumes. It assumes your origin is at the top left of the screen (0,0).
However this piece of code puts your camera origin at the center of the screen! Think of it with camera position of 0,0 with a screen size of 800width and 600height:
Here at no camera movement the origin of the camera is at the center of the screen. This means that if you are drawing your tiles (or whatever it is) starting at position 0,0 then you will see that tile in the upper left part of the screen (rather than in the middle)!
Also dont forget to account for zooming so:
(That will make sure that when you zoom the camera stays at the center point)
Here is a note on zooming that took me a bit to figure out that you might find helpful in calculating things.
Assuming width of tiles are 32, then how large or small is the tile enlarged when zoomed.
well you can (zoom * 32) this will tell you the size of the current tile if it is expanded or minimized
For instance
zoom tile width
.9 28.8
1.0 32
1.1 35.2
If you are handling camera movement where you want the camera to stop if your character is past a certain point (so that you dont
see the black areas of the screen) all you need to do is keep track of a seperate x and y value (that is the character movement) and
when the x passes the center point the appropriate camera value needs to pick up and start updating as well, otherwise it needs to be set at that barrier.
For instance in my game the barrier is 400 x and 300 y.
So when my char.x is < 400 then my camera should be stuck at 400. Else update camera x with char.x –(this will effectively pick up the camera when my char passes a certain point).
Hopefully this will make a little bit of sense. I am still working through the whole camera thing myself.
2D Camera Issue | Xbox360 News
November 16th, 2009 at 6:53
[...] I followed the tutorial on http://www.david-amador.com/2009/10/xna-camera-2d-with-zoom-and-rotation/ to achieve a camera that follows my player sprite with zoom in/out [...]
2D Camera Issue | Xbox360 News
November 16th, 2009 at 6:53
[...] I followed the tutorial on http://www.david-amador.com/2009/10/xna-camera-2d-with-zoom-and-rotation/ to achieve a camera that follows my player sprite with zoom in/out [...]
Adam
November 22nd, 2009 at 14:35
Hi,
Very nice solution! Congrat.
Alain
November 25th, 2009 at 10:21
How would you apply this when you would want to have 2 camera’s to follow 2 separate players?
Alain
November 25th, 2009 at 10:32
Nevermind I already figured it out. You can just draw the “scene” twice and place the second camera where you want it.
^DJ_Link^
November 25th, 2009 at 11:49
Exactly. If you want to do a splitscreen for instance just draw the scene twice on different viewports.
I got this result using the camera with viewports http://www.youtube.com/watch?v=w0bELIEC0Q8
Anonymous Coward
December 7th, 2009 at 9:12
Now I got it! You just made my day. Thanks.
Ko9
December 14th, 2009 at 19:49
Very nice, saved me a bit of time implementing this myself.. There are a few errors that I needed to fix first, but all of them very superficial. Thanks!
André
December 29th, 2009 at 22:34
Hi, David! Nice tutorial. But can you correct the first block of code?
Use
_pos = Vector2.Zero;
Instead
_pos = new Vector2.Zero;
Thanks!!!! =)
^DJ_Link^
December 29th, 2009 at 22:50
Hi André. Thanks for the tip, you are right Vector2.Zero is static, no need for new before.
Regards =)
Zms
January 12th, 2010 at 22:29
Nice solution!
How do i get the world co-ordinates from the mouse cursor when using the camera?
^DJ_Link^
January 13th, 2010 at 18:14
Just add the camera position to the mouse position, do a function like this
Let me know if you run into any trouble.
Zms
January 14th, 2010 at 2:22
Thank you! Works great…
Craig
February 11th, 2010 at 21:29
Hey man!
I’ve been trying for hours to get a working camera. But I’m really lost and really new to programming ^^
Just wondering why when I copy the code in there’s instantly errors. Is this too advanced for me or what?
thanks.
The errors are “Rotation” Viewportwidth & Viewportheight, and
if (_zoom < 0.1f) _zoom = 0.1f;
^DJ_Link^
February 11th, 2010 at 21:44
Actually you are right, there is something wrong with that function. try replacing the get_transform function with something like this instead
Tell me how it went
Craig
February 12th, 2010 at 17:37
Okay, I changed that function and ran the program with no problems(no errors
)
But whenever I move my little sprite he still runs off the screen and I can’t move the screen across the x-axis.
Is there a way to make this camera code follow my Sprite?
or move the camera.
thanks.
Craig
February 12th, 2010 at 17:49
Going to rewrite.
Getting myself confused.
When I try to put this above spritebatch begin I get errors
Camera2d cam = new Camera2d();
cam.Pos = Vector2(500.0f,200.0f);
I changed the Camera2d to Camera2D and it changed colour, but the there was errors with the spritebatch below it.
spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
SpriteSortMode.Immediate,
SaveStateMode.SaveState,
cam.get_transformation);
Also there is an error with Vector2, really confused lol
any ideas?
^DJ_Link^
February 12th, 2010 at 18:30
Yes there is. You can move the camera by changing the variable
cam.Pos to your character position.
Each time you move your character update the camera as well
^DJ_Link^
February 12th, 2010 at 18:32
My bad again,
cam.Pos = Vector2(500.0f,200.0f);
should be
cam.Pos = new Vector2(500.0f,200.0f);
Craig
February 13th, 2010 at 17:39
could you give an example
of how I update the camera to my sprites position.
i know how to move the camera from its current location, which is center screen thanks to previous feedback.
my sprite is called mMainCharacterSprite
thanks, and sorry for my lack of knowledge
^DJ_Link^
February 13th, 2010 at 17:42
Does your Sprite has a member Vector2 to save it’s position? Where are you saving the mMainCharacterSprite position?
just equal the camera.pos to that vector2
schnabel
February 14th, 2010 at 13:20
the get_mouse_vpos() method doesn’t work with zooming. Any idea how to fix this? I just can’t get it to work.
^DJ_Link^
February 14th, 2010 at 15:16
I’m not interely sure if this will work but maybe multiplying the mouse position with Zoom before adding the camera position
I’ve only considered Zoom when clicking a rectangle for testing if is inside and I simply multiply it’s size per Zoom so I’m guessing it should work with the mouse position as well. Let me know
Medic
February 14th, 2010 at 17:19
Craing something like
cam.pos = player.body.positin
in your update method
Craig
February 14th, 2010 at 17:19
ok thanks, got it working. Thanks for your help dude. I
schnabel
February 15th, 2010 at 11:37
Thank you for your help still doesnt work. How did you do the “clickinsiderectangle” method? i create a rectangle from the virtual position of the mouse and ask if it intersects another rectangle. I also always have to add the middle of my screen as a Vector2(for example 800,600 in my case) to the x,y position of the rectangle I want to click to get valid coordinates. Maybe I have to multiply the zoom with this offset? Tried it…doesn’t do it.
Pretty confused why this isn’t working.
Erik
February 21st, 2010 at 11:28
This camera works fine for me
except this camera moves in the world : )) Is there any option that camera position will be fixed and world will be moving around it ? : )
For example i got object in middle of the screen with fixed position (viewPort.Width/2, viewPort.Height/2) and the rest of the world is moving around this object, depending on keyboard input
)
This camera changes the position of my object in world : )
^DJ_Link^
February 21st, 2010 at 14:35
The camera being fixed and the rest moves is kind of the point that I wanted to avoid when I built this class. What makes sense is to move a camera in in world. Not translate everything to be on the camera. The camera itself does not change the position of anything except for itself. The Transformation Matrix simply moves the world to that specific position, draws it and that’s it. Your objects still keep their positions.
If you want to make Fix objects and still move the rest of the stuff you can, let’s take GUI objects for instance, they are always fixed you simply draw it in two batches. One of them with the camera transformation draws everything that moves, the other one, the gui stuff is drawn without that transformation
Hope this helps
Erik
February 21st, 2010 at 14:54
I used to check object.IsVisible with if object.position.x >viewPortRect.Width return false …. because my camera i used before was still keeping the followed “sprite” (my spaceship for instance) in its initializated Position : ) and moving were all the objects around
Now i had to check trough object.position.X> viewPortRect.Width + cam.pos.X – viewPortRect.Width/2.
The only problem was i had to repair my code on .. lots of places.. not very good solution though.
)
I already extended my math functions so it works fine with the camera
Thanks for help and swift answer
Drej
March 3rd, 2010 at 15:01
Hi there,
Thank you very much for this great tutorial, it helped me a great deal and got me working on my project again. I just have a short question which may be too general to ask here but I’ll do it anyway: How would I go about making the screen rotate smoothly? If I just set the _rotation variable to a certain value the screen instantly snaps to the new position, and something like
for(float i=0f; i < rotation_amount; i += 0.0001f)
{ _rotation += 0.0001f }
doesn't work either as it is too fast, no matter how small the increments are. Can anyone point me in the right direction?
^DJ_Link^
March 3rd, 2010 at 15:15
You could update the rotation on the Update pump, something like camera._rotation+=0.001f; on every update until the value is what you want.
bluehair
March 10th, 2010 at 19:07
Hey, great tutorial I found it very handy.
Though I have a problem, I am not sure how to solve, to determine if a sprite is displayed within the 2D viewport, when the camera has been rotated. Bascially I want an indicator to show the angle of an object only when the object moves offscreen.
David Tomaž
March 10th, 2010 at 20:38
If anyone has a problem getting World – mouse coordinates, use the following way of transforming your matrix and calculating the right coordinates (it works with zooming).
Transformation :
I hope this helps you.
Timing Problems and Solutions « Varcade
March 14th, 2010 at 13:50
[...] added a 2D Camera to the game. This allows zooming, panning, and rotation of everything in the game word. I'm not [...]
Ratboy
March 17th, 2010 at 12:47
Hey I was wondering why this doesn’t work:
Basically there is an offset which is the camera XY but I’ve dumbed it down for testing.
^DJ_Link^
March 17th, 2010 at 13:02
First of all, don’t ever ever do this spriteBatch.Draw(this.Content.Load(“test1″),
Load it to a Texture2D and pass that variable to the spriteBatch
Second, what are you trying with Matrix.CreateTranslation(new Vector3(-16 + offset, -16 + offset, 0)) ??
What exactly is not working?
Craig
March 26th, 2010 at 0:08
Hey David, I have everything working to a good standard now.
I have to Zoom function working to a certain degree, I can put a fixed floating point number on it – But I was wondering if I could create it zooming in/out by keypress
I thought it was something like this
if (s.IsKeyDown(Keys.P))
{
_zoom++;
}
But im not entirely sure where I would put this, doesnt seem to working in my main game code . Any Ideas, maybe even a simpler way because on your youtube video I see that you zoom in and out.
^DJ_Link^
March 26th, 2010 at 0:47
Add this function to the Camera Zoom Class
Then, iside your Update(Gametime gametime) on the Game class do something like this:
Hope it helps. Let me know if you run into any issue. I’m writing this from memory and I can be wrong on some syntax. Cheers
Craig
March 27th, 2010 at 19:02
function void IncrementZoom(float amount)
{
Zoom = Zoom + amount;
}
im getting an error with the void saying its not valid.
the rest seems fine.
ive never used function before either.
thanks
^DJ_Link^
March 27th, 2010 at 19:45
Ups, mistyped, just write
public void IncrementZoom(float amount)
DennisBenson
April 20th, 2010 at 3:46
Hey David
Thanks for this tutorial!
I have some comments:
1) public class Camera2d : Object2d
I assume that you extend it because it is essentially a 2d object.. are there any special properties to Object2d?
2) I assume the rest of the scripts go into the same class (except for the last one).
What’s up with this line? set { _zoom = value; if (_zoom < 0.1f) _zoom = 0.1f; }
I’m trying to understand this, but judging from the comment, I guess ‘%lt;’ somehow translates into <
3) Rotation does not exist in current context.. of course, use get and set.
Would you need to do _rotation%=360? Or will it appropriate that automatically?
4) ViewportWidth ViewportHeight: I'm still kinda new to XNA. It doesn't exist in the current context.
Am I supposed to initialize a Viewport in Game1.cs?
David Amador
April 20th, 2010 at 9:15
Hi there Dennis.
1) Yes it’s basically an Object2D, no need to extend it though. It was a mistake of mine copying it from my engine where Object2D is the class that has the Vector2 Position, Rotation and Scale since, but I moved them to Camera2D in this example. Just remove that inheritance.
2) That line is to prevent zooms smaller than 0.1f (you can change to another value. The symbol is
<but wordpress bust have replaced it with weird stuff. It’s fixed now.
3) I’ve added the Get Set for Rotation property. Should work properly. Oh and on a side note, rotations in XNA are measured in Radians, not degrees, so if you want a 180º you should do Rotation = Math.PI, use the function MathHelper.ToRadians() to convert. Something like
Rotation = MathHelp.ToRadians(90); MathHelper is part of XNA Framework
4)In my example I’m abstracting from here you can get Viewport properties, but you can get it by accessing GraphicsDevice.Viewport.Width and GraphicsDevice.Viewport.Height inside your Game1 class.
Someone
April 25th, 2010 at 20:07
Thanks a lot.
I was looking for something like this for a whole day.
Mike
April 28th, 2010 at 23:45
Great tutorial, it was very helpful!
I have one question if anyone could answer it, I’m trying to figure out a way to translate the sprites world co-ordinates into co-ordinates related to my viewport. My goal is to have the camera start to zoom out as the player approachs the edge of the screen.
Any suggestions? thanks !
David Amador
April 29th, 2010 at 0:39
if you need to know the sprite position relative to the camera just subtract the camera position to the sprite position. Something like
Mike
April 29th, 2010 at 3:07
Wow, I was way over complicating things ! Thanks !
The code that I ended up needed was
(playerPosition – cameraPosition) * cameraZoom
Nab
May 7th, 2010 at 8:18
when I rotate the camera then change the position of the camera (e.g. Position.X) then the camera will NOT go to the right only. instead it will go the World’s right (e.g. when rotating clock wise that will be Bottom right).
how can I make it go to the camera’s right while still keeping the rotation around the camera’s center ?
( I noticed that if i multiply the rotation Matrix first it will move to the camera’s right, but that will make the rotation around the Origin of the world, instead the center of the camera.)
and Thank you for the great article.
DMitsuki
May 16th, 2010 at 7:05
Let’s say you have 50 to the right of whatever you are doing. So that would mean to move to the right 50 you would have to do x += 50. This works, but if you rotate everything it will no longer go 50 to the right, instead it will go 50 to the old right, or original right. To fix this you have to apply the rotation to the x as well. If you have 50, imagine that the new hypotenuse of a triangle. Now, the center of the screen is the pivot point, and you need to apply the math according to the x position. Let’s say you rotated the screen 25 degree’s. That means that in your right triangle of rotation you have the angle 25 degree’s, 90, and whatever is left.
Now, all you have to do is simply soh cah toa, in this case you have to do soh and cah, so y -= sin(25)/50, and x += cos(25)/50, or something around that.
Eric
July 6th, 2010 at 22:06
Excellent tutorial
Worked first time