5
votes

I have a working 2D camera in XNA with these guts:

ms = Mouse.GetState();
msv = new Vector2(ms.X, ms.Y);  //screenspace mouse vecor
pos = new Vector2(0, 0); //camera center of view
zoom_center = cursor; //I would like to be able to define the zoom center in world coords
offset = new Vector2(scrnwidth / 2, scrnheight / 2);
transmatrix = Matrix.CreateTranslation(-pos.X, -pos.Y, 0)
    * Matrix.CreateScale(scale, scale, 1)
    * Matrix.CreateTranslation(offset.X, offset.Y, 0);
inverse = Matrix.Invert(transmatrix);
cursor = Vector2.Transform(msv, inverse);  //the mouse position in world coords      

I can move the camera position around and change the zoom level (with other code that I have not pasted here for brevity). The camera always zooms around the center of the screen, but I would like to be able to zoom about an arbitrary zoom point (the cursor in this case), like the indie game dyson http://www.youtube.com/watch?v=YiwjjCMqnpg&feature=player_detailpage#t=144s

I have tried all the combinations that make sense to me, but am completely stuck.

2

2 Answers

4
votes

I have done it, this is the code and the explanations:

Get the mouse coods in camera space:

 msx = Mouse.X - Viewport.Width/2;
 msy = Mouse.Y - Viewport.Height/2;

Get distances from camera to mouse in camera space

 var width = CameraTranslation.X - msX;
 var height = CameraTranslaton.Y + msY;

Get the offset produced by the new zoom and then substract it to camera position

 CameraTranslation.X -= width * (1-newZoom/_zoom);
 CameraTranslation.Y -= height * (1-newZoom/_zoom);

 _zoom = newZoom;
3
votes

You actually won't be zooming around the cursor. You'll be zooming on a point defined on the line between your previous zoom center and the cursor. As you zoom in further and further that point will approach the cursor position. Also it should move to the cursor faster if it's a larger zoom increase. You're zoom_center should be defined as something like this (Note: I introduced new variables and added a line where you reassign pos to zoom_center).

zoom_center = pos = cursor + (scale_prior_to_zoom/scale) * (pos - cursor);

Hopefully this works as I only tried on paper, so you may need to change this a bit... but it should get you at least pointed in the right direction. Here's the backing math that I did with just the X dimension.

cursor = (1,1)

pos = (5,5)

Zooming in Position should be closer to cursor

scale = 2 scale_prior_to_zoom= 1

posX = 1 + (1/2)(5 - 1)

posX = 1 + 0.5 * 4

posX = 1 + 2

posX = 3

Zooming Out Position should be farther from the cursor

scale = 1 scale_prior_to_zoom = 2

posX = 1 + (2/1)(5 - 1)

posX = 1 + 2 * 4

posX = 1 + 8

posX = 9