# 2D Camera Transformation

This topic is 1843 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi!

I'm having some trouble getting my head around the behavior of a popular 2D camera class with the following method:

[CODE]
public Matrix GetTransformation()
{
Matrix viewMatrix =
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 viewMatrix;
}
[/CODE]

The issue I am facing is that after a camera rotation, all applied camera movement (using _pos variable) is performed on the rotated axis, not the true world axis (up,down,left,right).

To help visually check this I draw an "axis" sprite that just shows a standard x,y "graph paper" axis. When there is no camera rotation and I move the camera up/down/left/right it follows the screen axis perfectly. After I apply a rotation to the camera and try to move up/down/left/right the camera follows the rotated axis rather than the world axis.

Please can somebody help me to understand why this is happening, and perhaps show me how to still move the camera according to the world's X,Y axis?

I can post screen capture's if needed as I'm sure I'm not explaining this properly.

Thanks,
Mark.

##### Share on other sites
Have a think about it, CreateRotationZ actually doesn't know what you want to rotate around, take a look at how monoxna has implemented this function:

[source lang="csharp"]public static Matrix CreateRotationZ(float radians)
{
Matrix returnMatrix = Matrix.Identity;

returnMatrix.M21 = -returnMatrix.M12;
returnMatrix.M22 = returnMatrix.M11;

return returnMatrix;
}[/source]

In order to get the desired result out of multiplication with matrices, you need to make sure that you multiply matrices in the right order. So:

- multiplying a translation by a scale will enlarge or shrink that translation.
- multiplying a translation by a rotation will in effect rotate that translation around 0,0,0.
- multiplying a rotation by a translation will move that rotation to the desired position.
- multiplying a rotation by a scale alone will literally multiply that scale.

and so on, and so on, so you can see why the order in which you multiply matrices is important. Another thing to note is that I don't see you using the Matrix.CreateLookAt method, from your code it looks like you are trying to create a view matrix, you can certainly create a view matrix other ways but you are probably better using the CreateLookAt method as it's built to do the job.

Hope this helps.

Aimee

##### Share on other sites
Hi Aimee

Thanks for the info. The reason I have not used createlookat is because I want to learn more about the methodologies including the matrix multiplication.

So if one wanted to rotate the view around the center of the current camera view, how would one accomplished that as an example?

Thanks,
Mark.

##### Share on other sites
I think the fact they are called matrices is what trips most people up, they make themselves out to be a magical thing that can do all sorts, but really they are just a grid of numbers that provide a way of manipulating coordinates (why can't people just describe em like that). In mathematics you could describe a matrix having certain components preform things like carrying out a cosine calculation for example, but as it's more difficult to express that in programming, classes like the Matrix class provide special methods like CreateRotateZ and CreateLookAt that basically do those individual operations on certain components.

Take the example I showed in my last post, CreateRotateZ manually creates a standard identity matrix, performs Cos and Sin operations on two of the components, flips the sign of the input M12 component for the M22 output component, and passes the original value for M11 to the output M22. This in effect means a Matrix is actually not a magical piece of awesomeness, rather just a holder of a grid of information that needs manipulating.

Same again goes for multiplication, this is literally just an operator that has been created for the Matrix class that multiplies each component and returns the result, here have a look at how monoxna did it:

[source lang="csharp"]public static Matrix operator *(Matrix matrix1, Matrix matrix2)
{
Matrix result;

result.M11 = matrix1.M11 * matrix2.M11 + matrix1.M12 * matrix2.M21 + matrix1.M13 * matrix2.M31 + matrix1.M14 * matrix2.M41;
result.M12 = matrix1.M11 * matrix2.M12 + matrix1.M12 * matrix2.M22 + matrix1.M13 * matrix2.M32 + matrix1.M14 * matrix2.M42;
result.M13 = matrix1.M11 * matrix2.M13 + matrix1.M12 * matrix2.M23 + matrix1.M13 * matrix2.M33 + matrix1.M14 * matrix2.M43;
result.M14 = matrix1.M11 * matrix2.M14 + matrix1.M12 * matrix2.M24 + matrix1.M13 * matrix2.M34 + matrix1.M14 * matrix2.M44;

result.M21 = matrix1.M21 * matrix2.M11 + matrix1.M22 * matrix2.M21 + matrix1.M23 * matrix2.M31 + matrix1.M24 * matrix2.M41;
result.M22 = matrix1.M21 * matrix2.M12 + matrix1.M22 * matrix2.M22 + matrix1.M23 * matrix2.M32 + matrix1.M24 * matrix2.M42;
result.M23 = matrix1.M21 * matrix2.M13 + matrix1.M22 * matrix2.M23 + matrix1.M23 * matrix2.M33 + matrix1.M24 * matrix2.M43;
result.M24 = matrix1.M21 * matrix2.M14 + matrix1.M22 * matrix2.M24 + matrix1.M23 * matrix2.M34 + matrix1.M24 * matrix2.M44;

result.M31 = matrix1.M31 * matrix2.M11 + matrix1.M32 * matrix2.M21 + matrix1.M33 * matrix2.M31 + matrix1.M34 * matrix2.M41;
result.M32 = matrix1.M31 * matrix2.M12 + matrix1.M32 * matrix2.M22 + matrix1.M33 * matrix2.M32 + matrix1.M34 * matrix2.M42;
result.M33 = matrix1.M31 * matrix2.M13 + matrix1.M32 * matrix2.M23 + matrix1.M33 * matrix2.M33 + matrix1.M34 * matrix2.M43;
result.M34 = matrix1.M31 * matrix2.M14 + matrix1.M32 * matrix2.M24 + matrix1.M33 * matrix2.M34 + matrix1.M34 * matrix2.M44;

result.M41 = matrix1.M41 * matrix2.M11 + matrix1.M42 * matrix2.M21 + matrix1.M43 * matrix2.M31 + matrix1.M44 * matrix2.M41;
result.M42 = matrix1.M41 * matrix2.M12 + matrix1.M42 * matrix2.M22 + matrix1.M43 * matrix2.M32 + matrix1.M44 * matrix2.M42;
result.M43 = matrix1.M41 * matrix2.M13 + matrix1.M42 * matrix2.M23 + matrix1.M43 * matrix2.M33 + matrix1.M44 * matrix2.M43;
result.M44 = matrix1.M41 * matrix2.M14 + matrix1.M42 * matrix2.M24 + matrix1.M43 * matrix2.M34 + matrix1.M44 * matrix2.M44;

return result;
}[/source]
Ok there is a fair bit going on there, but again it's just a function that sets each component of the Matrix by using standard mathematics. Now the reason why I mentioned using the CreateLookAt method is because that specific method has been designed to make life easier for creating view matrices, also I doubt anyone want's to rewrite that method above every time they go to make a computer game lol.

Personally I am someone who needs to delve into the inner workings of something sometimes in order for me to understand why it works and how to use it, and if you are like me, I'd reccommend studying it, because just looking at the source code for monoxna is a eye opening way to learn this stuff. On the other hand there is lots more information out there about how to best use them, for further reading I'd suggest taking a read of the excellent extra reading tutorials provided by Riemer here: [url="http://www.riemers.net/eng/ExtraReading/matrices_geometrical.php"]http://www.riemers.net/eng/ExtraReading/matrices_geometrical.php[/url]

Aimee

##### Share on other sites

Hi Aimee

I have taken what you said on board and looked into the correct ordering for matrix transformations, I fully understand the concept of Matrices and their application to coordinates but I think I am having trouble grasping how they should be correctly used for transformations.

I have modified the method to become:

public Matrix GetTransformation()
{
Matrix viewMatrix = Matrix.CreateTranslation(-_viewportWidth / 2, -_viewportHeight / 2, 0) * //translate to origin
Matrix.CreateRotationZ(Rotation) * //apply the rotation
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) * //apply the scale
Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) * //apply the positional movement
Matrix.CreateTranslation(_viewportWidth / 2, _viewportHeight / 2, 0); //translate back to the position
return viewMatrix;
} 

My next step is to make the rotation happen around the center of the viewport as opposed to the center of the world (0, 0)

Thanks, Mark.

##### Share on other sites

I think you are mixing up the view and projection matrix leading to a misunderstanding of why we use world/view/projection in the first place. World is the theoretical transform of the vertices in world space, view can be seen as the camera, it converts world coordinates into ones relative to a camera, now here's the important part; the view matrix doesn't have any knowledge of the size of the view port, that is the job of the projection matrix.

The projection matrix is responsible for converting the view position of a vertex onto screen coordinates, In xna the process only works if you follow this order of transformation.

##### Share on other sites

I have been reading up on the correct ordering for matrix transformations and the creation of the view matrix. I would just like to understand how to model a camera that rotates around a specific point (centre of the viewport perhaps) that you can still adjust the position in non-rotated x,y coordinates that correspond to screen coordinates.

If someone has some sample code that would be great - I have looked over many many posts and examples all of which suffer from the same problem that I am facing.

Thanks,

Mark.

##### Share on other sites
if (Position != position || Scale != scale || Origin != origin || Rotation != rotation)
{
scale = Scale;
origin = Origin;
rotation = Rotation;
position = Position;
transform = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0)) *
Matrix.CreateRotationZ(Rotation) *
Matrix.CreateScale(new Vector3(Scale, 1)) *
Matrix.CreateTranslation(new Vector3(-Origin, 0));
}

This is exactly what I use, it works fine. Rotates around the center, zooms, translates, everything you wish for

EDIT: Origin is just your screen center. so in a 800 * 600 window, it is 400,300 vector.

##### Share on other sites

Hi Morphex

That's pretty much the exact code that I use. Don't you find that when you try to re-position the camera left/right/up/down that it always moves along the already-rotated axis? If there is no rotation the camera will move exactly as I want, if you apply any rotation the camera will move on a non-screen aligned axis.

Thanks,

Mark.

##### Share on other sites
Thats what it is supposed to do, if you don't want it like that you will need to change the order of transforms. So you want to rotate the screen but the movement to be Horizontal no matter the rotation?

If thats what you want, I can try and throw some code later to you. Edited by Morphex

##### Share on other sites
Exactly, I have tried applying the rotation before the transform but it will always rotate around (0,0). I want to always rotate around the current screen center (camera.position.x + viewport.width * 0.5, camera.position.y + viewport.height * 0.5) if that makes any sense?

So the camera movement will go according to screen axis, the rotation will always happen at the current camera focus (center of the screen).

Thanks,
Mark. Edited by mwkenna

##### Share on other sites

I still think you are getting a bit confused with view and projection matrices, so I've put a bit of an example together for you that demonstrates how I would normally perform camera rotation in a 2D game:

Note that in this example I disregard the SpriteBatch class to demonstrate another way of drawing sprites (by using the centre of the sprite instead of the top left as origin) which I believe will help you understand things a bit more. Also I create an orthographic projection matrix which means you don't need to figure out where the centre of the screen is because it is placed always at (0,0,0).

Again hope this helps, and if you still get stuck, let me know.

Aimee

##### Share on other sites

Well, the quicker way I can think of is to move the camera.X and Y in a trignometric fashion, so that it moves left and right even when rotated.

##### Share on other sites

Thanks Aimee - the solution seems to do what, I will be taking apart the code in detail and learning what each part does.

Morphex - Do you think you could also post that sample code with the trigonometric positioning - I'm guessing that's still using the spritebatch right?

Thanks,

Mark.

##### Share on other sites

Hi Aimee

I've been looking over your code (which is really well put together) but it's made me try to jump too far forward in my learning process.

Is it possible to alter the code so that it still uses the SpriteBatch which, as I understand it, does not require the use of a projection matrix at all?

Thanks,

Mark.

##### Share on other sites

Hi Mark, No matter what mechanism you use in XNA, you will always require a projection matrix to draw things. If no projection matrix is used then you can't properly place things onto the screen because like I have mentioned before that is the job of the projection matrix.

The SpriteBatch sets up a lot of things for you behind the scenes (including a projection matrix), however the question you started this thread with requires a more comprehensive understanding of how matrices work. In my example I did not use a SpriteBatch because using one actually makes things more complicated, the projection matrix by default sets the origin into the top left corner, which makes rotating the camera needlessly more difficult than it needs to be.

I promise you that if you take a bit more time to try and understand what I've said over the last few replies that you'll see why I've gone into so much depth, It's not just as simple as dropping a SpriteBatch in there, messing about with a matrix and expecting everything to be perfect.

Slow down, learn the mathematics, be patient and don't try to force things. A lot of people never get anywhere because they "want to know how to do stuff RIGHT NOW and sod everything that looks to difficult". It's likely there is a smart programmer here who'd just throw you a quick answer that'd see you off again using the SpriteBatch, but you would never learn anything, and you'd be right back here in a few weeks asking us to fix a problem with the sprite batch that you don't know how to fix because something went wrong and you never learnt the fundamentals in the first place.

Aimee

##### Share on other sites

Hi Aimee

Thanks for the advice, I am actually trying to slow it down a notch. I'm working through a book which only deals with the SpriteBatch (for 2D) - I am on transformations and how to model a simple camera, but the rotation/translation in the camera seemed wrong to me (non-axis aligned) so I thought I would post to see if there was a way to "fix" it.