Oxy
Oxy

Reputation: 194

Get the offset produced by zoom relative to the mouse

I'm trying to implement a 2D game which features zoom to the mouse. It's all organized like this:

The game objects have an arbitrary position that is not affected by the printing zoom or the offset. (And as is arbitrary, I'd chosen to start each level with the (0,0) at the top left, equal to the used by the rendering engine)

The game assets are pixel perfect and required to stay as it on zoom ins. So I used an integer to represent zoom level, and the zoom scale is 2^zoom.

Each object is rendered on screen by a function that uses its private locations and takes the zoom level (an integer) and the two integers indicating the offset.

It prints adding the offset to it's coordinates and multiplying it all by 2^zoom.

The main loop basically does all the input handling, then the update of all the internal states and then renders. (as usual) And all the mess will have to be inside the update section, as the render will only accept a zoom level and the x and y offset amounts.

I tried to implement it as seen on the first answer of this other question: C++ Zoom into the centre of the screen in 2D coordinates and taking as coordinates the position of the mouse instead of the center of the screen.

The main headache comes from trying to get the offset that has to be fed to the render function correctly.

The relevant function code currently looks like this:

void PlayState::updateOffset(bool zoomIn, int zoomLevel, int MouseX, int MouseY){

float finalX, finalY, sF, scaledPosition0x;
float scaledPosition0y, scaledPosition1x, scaledPosition1y;

sF = pow(2,zoomLevel);

if(zoomIn){
    scaledPosition0x = MouseX*pow(2,zoomLevel-1)+xOffset;
    scaledPosition0y = MouseY*pow(2,zoomLevel-1)+yOffset;
}else{
    scaledPosition0x = MouseX*pow(2,zoomLevel+1)+xOffset;
    scaledPosition0y = MouseY*pow(2,zoomLevel+1)+yOffset;
} 

scaledPosition1x = MouseX*sF+xOffset;
scaledPosition1y = MouseY*sF+yOffset;

finalX = (scaledPosition0x-scaledPosition1x)/sF;
finalY = (scaledPosition0y-scaledPosition1y)/sF;

incrementOffset(finalX, finalY);}

The increment offset function just checks if the offset is too large and corrects it. As you can see, it looks like the code in the answer of the linked question. It looks like a copy paste of the code mentioned, but I have tried to implement many (a 'long long' many) other ways, even some alterations upon this one, and none works closer to the expected result.

It currently works wonders as long as you keep your mouse at 0,0, or scroll pointing anywhere from zoom level 0 to -1 or to 1 and back to 0 without moving the mouse. Any other combination of zoom level/position will render aberrant results.

I'm looking forward your answers. I'll be happy to provide any other detail required.

Upvotes: 1

Views: 898

Answers (1)

Oxy
Oxy

Reputation: 194

SOLVED

I'll post the answer that worked to me in case anyone has the same problem.

The quid of the question resides in the way the offset is stored (un-scaled).

It was a little strange because the zoom was done in a different part of the code, so what we would usually do with matrices (undo - operate - redo) was ineffective. But if gave me insight of the path to follow.

As this shows, the first that has to be done is undo the operations. Aka find where the mouse is on the untouched scene (without zoom nor offset).

Mouse position in original scene will be OMP. Mouse position in screen will be MP. Z0 will be the zoom applied the last frame. Off0 will be the offset we had on the last frame.

OMP = (MP-Off0*Z0)/Z0

Then we do the inverse for the new zoom, so we look at where would the zoom end up if we unzoomed right now*, and the difference between the later and the former will give us the deviation.

Off1 = MP/Z1 - OMP

*: if we don't undo the offset, the current offset will be automatically kept, so later we will change one for the other and won't need to add/substract from the previous offseet

Upvotes: 1

Related Questions