Viewport / Camera Calculation in 2D Game

we have a 2D game with some sprites and tiles and some kind of camera/viewport, that "moves" around the scene. so far so good, if we wouldn't had some special behaviour for your camera/viewport translation.

normally you could stick the camera to your player figure and center it, resulting in a very cheap, undergraduate, translation equation, like :

vec_translation -/+= speed (depending in what keys are pressed. WASD as default.)

buuuuuuuuuut, we want our player figure be able to actually reach the bounds, when the viewport/camera has reached a maximum translation.

we came up with the following solution (only keys a and d are the shown here, the rest is just adaption of calculation or maybe YOUR super-cool and elegant solution :) ):

if(keys[A])
{
playerX -= speed;

if(playerScreenX <= width / 2 && tx < 0)
{
playerScreenX = width / 2;
tx += speed;
}
else if(playerScreenX <= width / 2 && (tx) >= 0)
{
playerScreenX -= speed;
tx = 0;

if(playerScreenX < 0) playerScreenX = 0;
}
else if(playerScreenX >= width / 2 && (tx) < 0)
{
playerScreenX -= speed;
}
}
if(keys[D])
{
playerX += speed;

if(playerScreenX >= width / 2 && (-tx + width) < sceneWidth)
{
playerScreenX = width / 2;
tx -= speed;
}
if(playerScreenX >= width / 2 && (-tx + width) >= sceneWidth)
{
playerScreenX += speed;
tx = -(sceneWidth - width);
if(playerScreenX >= width - player.width) playerScreenX = width - player.width;
}
if(playerScreenX <= width / 2 && (-tx + width) < sceneWidth)
{
playerScreenX += speed;
}
}


i think the code is rather self explaining:

keys is a flag container for currently active keys, playerX/-Y is the position of the player according to world origin, tx/ty are the translation components vital to background / npc / item offset calculation, playerOnScreenX/-Y is the actual position of the player figure (sprite) on screen and width/height are the dimensions of the camera/viewport.

this all looks quite nice and works well, but there is a very small and nasty calculation error, which in turn sums up to some visible effect.

let's consider following piece of code:

if(playerScreenX <= width / 2 && tx < 0)
{
playerScreenX = width / 2;
tx += speed;
}


it can be translated into plain english as : if the x position of your player figure on screen is less or equal the half of your display / camera / viewport size AND there is enough space left LEFT of your viewport/camera then set players x position on screen to width half, increase translation (because we subtract the translation from something we want to move). easy, right?! doing this will create a small delta between playerX and playerScreenX.

after so much talking, my question appears now here at the bottom of this document:

how do I stick the calculation of my player-on-screen to the actual position of the player AND having a viewport that is not always centered aroung the players figure?

here is a small test-case in processing:

http://pastebin.com/bFaTauaa

You flip it around.

Right now, your player influences the camera. When you press the A key, the playerScreenX variable changes.

In terms of the camera, that means that when you press the A key, it moves the view. Logically, you are now having a hard time trying to separate the two views (world and camera).

A variable that holds the screen position of an object is a good start, but I would extend it further:

• Every object has a position in world space.

• The camera has a transformation matrix holding its position in world space and its rotation.

• To place an object on the screen, you do this: m_ScreenPosition = GetCamera()->Transform(m_WorldPosition);

• To move the camera, you give it a new position (the position of the player in world space).

How does this solve the problem at hand?

In this system, the player can move to any location, even invalid ones. It doesn't matter, because his render location is determined by the camera.

The camera knows about the bounds of the level and how large its viewport is. When it receives an invalid position (too far on the left or too far on the right) it can turn that into a valid position.

For example, let's say our level is 1000x1000 pixels, our viewport is 640x480 (with edges of 160 and 160) and our player is at (200, 200).

After applying our camera transform, we get a position in screenspace: (200, 200). Cool. Let's move the player to (700, 200). This doesn't fit in our viewport (which extends until 640-160 = 480), so we have to move the camera:

x = player_pos.x - (screen_width.x - edge.x - edge.x);


Now our new position for the camera is 380.

Bonus: You are now transforming the player with the camera, instead of the camera with the player. What does this mean? Now your camera can rotate, scale, skew, whatever and it doesn't affect the rest of the game in the slightest! You can even add more camera's, from different angles, or put the camera on rails to show off a cool feature.

For a game I was working on I used a fairly simple process you can probably make use of in this case, if I have understood the problem that is.

I wanted the camera to track the player, but I also didn't want to show the areas outside of the 2D world. So what I did was define a collision rectangle for the world, a rectangle for the camera and a rectangle for the player. Then, after moving the player (and therefore the camera tracking it) I would test that the camera rectangle and the player rectangle were completely within the world rectangle. If they weren't I would move them before drawing.

On top of this, I added an extra detail to prevent the camera collision with it's boundaries being too harsh while also giving it a fairly natural movement. What I did was simply store an average of where I want the camera to be and put the camera there, rather than specifically where the player is. So I would work out, for my game, I always want the camera to be 5 meters ahead of my player. So I would take the player position plus 5 * normalised direction of the player movement vector. I would add this to my rolling average which takes 50 samples in a ring buffer and I would put the camera at the average position. This causes the camera to always slightly lag behind the player giving it a natural smoothe movement witout require complex sping physics. It also mean that, when I work out the desired position, I could check if this would put the camera out of bounds and change the position appropriately.

• thanks for your answer originaldemon! this seems to be a quite fair approach, as it circumvents the effort to extra calculate another position variable for the player's sprite. i will give it a try and post a result if i will succeed! (i edited this post, since i don't have enough badges to directly comment on the first answer. sorry for that. this post should appear right after originaldemon)
– user17147
Jun 15, 2012 at 11:56