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
publicclass Camera2d
{protectedfloat _zoom;// Camera Zoompublic Matrix _transform;// Matrix Transformpublic Vector2 _pos;// Camera Positionprotectedfloat _rotation;// Camera Rotationpublic 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 zoompublicfloat Zoom
{get{return _zoom;}set{ _zoom =value;if(_zoom < 0.1f) _zoom = 0.1f;}// Negative zoom will flip image}publicfloat Rotation
{get{return _rotation;}set{ _rotation =value;}}// Auxiliary function to move the camerapublicvoid Move(Vector2 amount){
_pos += amount;}// Get set positionpublic 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
When I remove graphics.IsFullScreen = true; camera works fine but then players can see taskbar, buttons for minimize, maximize, close and I don’t like that.//graphics.IsFullScreen = true; Do you have any suggestion how to solve this? Thanks again!
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!
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 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;
Thank you so much for quick reply. My game is running in full screen mode.
graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
graphics.IsFullScreen = true;
When I remove graphics.IsFullScreen = true; camera works fine but then players can see taskbar, buttons for minimize, maximize, close and I don’t like that.//graphics.IsFullScreen = true; Do you have any suggestion how to solve this? Thanks again!
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;