SDL 2 Zooming In and Out

Started by
3 comments, last by null; 7 years, 2 months ago

I recently got into SDL 2 in C++. I've implemented a camera into the game, and was wondering how I would go about zooming. I've tried doing many things, but I won't go into detail because I don't think I'm on the right track at all.

If any of you have implemented zooming in/out into your game, can you specify how you actually did it?

Thanks to everyone who replies.

Advertisement
The trick is to think in terms of coordinate spaces. The screen space is different from the world space. The screen is measured in pixels (px), where +X is to the right, +Y is toward the bottom of the screen, and (0 px,0 px) is the top left corner of the screen. The world is measured in units or meters (m) as defined by a camera. The camera then sets up the world space such that (cam.x m, cam.y m) is the center of the screen, +X is toward the right, and +Y is toward the top of the screen, and 1 m is some number of pixels. Drawing an 1 m sized object at (0, 0) should draw an object of N pixels centered to the screen.


So, now comes the actual transformation. I like to use matrices:
# assuming:  cam = {x: 0,  y: 0,  scale: gfx_w/40}

# these transforms are executed from last to first, because math
mvp =  mat3.identity!
mvp *= mat3.scale      1, -1             # invert the Y axis
mvp *= mat3.translate  gfx_w/2, gfx_h/2  # translate to center of screen
mvp *= mat3.rotate     cam.rotation      # rotate camera
mvp *= mat3.scale      cam.scale         # convert from units to pixels, 1 unit = gfx_w/40 pixels
mvp *= mat3.translate  -cam.x, -cam.y    # make (cam.x, cam.y) the center of the screen

# the result of (mvp * player.pos) transforms the player’s position in world space to screen space
# with respect to the camera— this is typically done in a shader
shader.set     "transform", mvp
graphics.draw  player.shape, player.pos.x, player.pos.y, player.size

Matrices can make these transformations easier to read, though you can carry these transformations out by hand:
float2 center = float2   gfx_w/2, gfx_h/2
float2 inv_y  = float2   1, -1
float2 cam_u  = float2   (cos cam.rotation), (sin cam.rotation)
float2 cam_v  = float2  -(sin cam.rotation), (cos cam.rotation)


float2 pos_cam    = player.pos - cam.pos
float2 pos_px     = (cam_u*pos_cam.x + cam_v*pos_cam.y) * cam.scale
float2 pos_screen = pos_px * inv_y + center
Then, controlling the zoom is just a matter of changing `cam.scale` :^)

The trick is to think in terms of coordinate spaces. The screen space is different from the world space. The screen is measured in pixels (px), where +X is to the right, +Y is toward the bottom of the screen, and (0 px,0 px) is the top left corner of the screen. The world is measured in units or meters (m) as defined by a camera. The camera then sets up the world space such that (cam.x m, cam.y m) is the center of the screen, +X is toward the right, and +Y is toward the top of the screen, and 1 m is some number of pixels. Drawing an 1 m sized object at (0, 0) should draw an object of N pixels centered to the screen.


So, now comes the actual transformation. I like to use matrices:


# assuming:  cam = {x: 0,  y: 0,  scale: gfx_w/40}

# these transforms are executed from last to first, because math
mvp =  mat3.identity!
mvp *= mat3.scale      1, -1             # invert the Y axis
mvp *= mat3.translate  gfx_w/2, gfx_h/2  # translate to center of screen
mvp *= mat3.rotate     cam.rotation      # rotate camera
mvp *= mat3.scale      cam.scale         # convert from units to pixels, 1 unit = gfx_w/40 pixels
mvp *= mat3.translate  -cam.x, -cam.y    # make (cam.x, cam.y) the center of the screen

# the result of (mvp * player.pos) transforms the player’s position in world space to screen space
# with respect to the camera— this is typically done in a shader
shader.set     "transform", mvp
graphics.draw  player.shape, player.pos.x, player.pos.y, player.size

Matrices can make these transformations easier to read, though you can carry these transformations out by hand:

float2 center = float2   gfx_w/2, gfx_h/2
float2 inv_y  = float2   1, -1
float2 cam_u  = float2   (cos cam.rotation), (sin cam.rotation)
float2 cam_v  = float2  -(sin cam.rotation), (cos cam.rotation)


float2 pos_cam    = player.pos - cam.pos
float2 pos_px     = (cam_u*pos_cam.x + cam_v*pos_cam.y) * cam.scale
float2 pos_screen = pos_px * inv_y + center
Then, controlling the zoom is just a matter of changing `cam.scale` :^)

Wow... I mean, I really appreciate the detailed reply and code examples; I just... don't really understand a lot of it.

I mean, here's the way I'm currently doing it (pseudocode):


 SDL_Rect camerRect = { 0, 0, 640, 480 };

in game loop:


        camerRect.x = player1.positionRect.x - 290;        camerRect.y = player1.positionRect.y - 170;


        player1.Draw(renderTarget, camerRect);
        SDL_RenderPresent(renderTarget);
        SDL_RenderClear(renderTarget);


        SDL_RenderCopy(renderTarget, mapTexture, &camerRect, NULL);
player draw method in player class:

 void Player::Draw(SDL_Renderer *renderTarget, SDL_Rect camerRect) {
                SDL_Rect drawingRect = { positionRect.x - camerRect.x, positionRect.y - camerRect.y, positionRect.w * 2, positionRect.h * 2};
                SDL_RenderCopy(renderTarget, texture, &cropRect, &drawingRect);
        }
I don't think the kind of stuff you're talking about applies to how I'm doing it - if it did, I don't really understand it...
Again, I really appreciate the detailed reply. I apologize for my noobness at this, lol.

As far as I remember, that's basically x, y, width, height -- correct?

To zoom, you need to adjust width and height. It will still display on the same window, but a smaller or larger area will be used to fill the window.

So at 2x zoom, you'll probably want something like 0, 0, 320, 240. (2x scale = divide width and height by 2)

That said, you might have to mess around a bit with also offsetting the camera as you zoom in/out, to keep things centered. It's been a while since I used SDL for this, so I don't remember anything more specific right now.

Hope that helps =)

Hello to all my stalkers.

As far as I remember, that's basically x, y, width, height -- correct?

To zoom, you need to adjust width and height. It will still display on the same window, but a smaller or larger area will be used to fill the window.

So at 2x zoom, you'll probably want something like 0, 0, 320, 240. (2x scale = divide width and height by 2)

That said, you might have to mess around a bit with also offsetting the camera as you zoom in/out, to keep things centered. It's been a while since I used SDL for this, so I don't remember anything more specific right now.

Hope that helps =)

Yeah, it really helped with the first part. I'm mad at myself for not thinking of that!

The problem is, this kind of only works with the background image, since the way the player is rendered differs to the way the background is rendered. I'm having a hard time explaining it at the moment, I'll post another reply when I can.

This topic is closed to new replies.

Advertisement