2

I am rather experiences libgdx developer but I struggle with one issue for some time so I decided to ask here.

I use FillViewport, TiledMap, Scene2d and OrtographicCamera. I want the camera to follow my player instance but there are bounds defined (equal to mapsize). It means that camera will never ever leave outside of map, so when player comes to an end of the map camera stops following and he goes to the edge of the screen itself. Maybe sounds complicated but it's simple and I am sure that you know what I mean, it's used in every game.

I calculated 4 values:

minCameraX = camera.viewportWidth / 2;
minCameraY = camera.viewportHeight / 2;
maxCameraX = mapSize.x camera.viewportWidth / 2;
maxCameraY = mapSize.y - camera.viewportHeight / 2;

I removed not necessary stuff like unit conversion, camera.zoom etc. Then I set the camera position like this:

camera.position.set(Math.min(maxCameraX, Math.max(posX, minCameraX)), Math.min(maxCameraY, Math.max(posY, minCameraY)), 0);

(posX, posY is player position) which is basically setting camera to player position but if it's to high or too low it sets it to max or min defined before in right axis. (I also tries MathUtils.clamp() and it works the same.

Everything is perfect until now. Problem occures when aspect ratio changes. By default I use 1280x768 but my phone has 1280x720. Because of that bottom and top edges of the screen are cut off because of the way how FillViewport works. Because of that part of my map is cut off.

I tried to modify maximums and minimums, calculate differences in ratio and adding them to calculations, changing camera size, different viewports and some other stuff but without success.

Can you guys help? Thanks

Mateusz Gaweł
  • 673
  • 1
  • 8
  • 22
  • What exactly is the problem? Does your camera bounds mechanism not work anymore, or is the problem that parts of the screen get cut off? – noone Nov 21 '16 at 11:39
  • The second one. Top and bottom edges are cut so I don't see map end frame for example. Of course I know that that's the way how FillViewport works but it should be posssible to modify camera bounds to fix it – Mateusz Gaweł Nov 21 '16 at 12:14
  • I'm not sure if it works in this case, but you could try adding `getTopGutterHeight()` and `getBottomGutterHeight()` to your minCameraY and maxCameraY (or maybe the other way around and substract instead of add?) – noone Nov 21 '16 at 12:31
  • definitely gonna try it in the evening. I will let you know – Mateusz Gaweł Nov 21 '16 at 12:43
  • @noone - doesn't that work only for FitViewport? – m.antkowicz Nov 21 '16 at 12:52
  • @m.antkowicz: I think "mathematically" the "black bars" (gutter) in a FitViewport might be the same as the "cut off parts" in a Fillviewport. `getTopGutterHeight()` returns `Gdx.graphics.getHeight() - (screenY + screenHeight)` which is the difference between the viewport's height and the actual device height = the cut off part. – noone Nov 21 '16 at 12:58
  • My advice is to never, ever use FillViewport. It creates situations like this where you have to do tedious calculations to deal with something simple, which defeats the whole purpose of using a Viewport manager at all. Use ExtendViewport! It's the right choice in 90% of cases. It ensures that you never have to calculate if something that needs to be visible might be off-screen. – Tenfour04 Nov 21 '16 at 13:10

3 Answers3

0

I tried solution of noone and Tenfour04 from comments above. Both are not perfect but I am satisified enough i guess:

noone:

camera.position.x = MathUtils.clamp(camera.position.x, screenWidth/2 + leftGutter, UnitConverter.toBox2dUnits(mapSize.x) - screenWidth/2 + rightGutter);
camera.position.y = MathUtils.clamp(camera.position.y, screenHeight/2 + bottomGutter, UnitConverter.toBox2dUnits(mapSize.y) - screenHeight/2 - topGutter);

It worked however only for small spectrum of resolutions. For strange resolutions where aspect ratio is much different than default one I've seen white stripes after border. It means that whole border was printer and some part of the world outside of border. I don't know why

Tenfour04: I changed viewport to ExtendViewport. Nothing is cut off but in different aspect ratios I also can see world outside of borders.

Solution for both is to clear screen with same color as the border is and background of level separatly which gave satisfying effect in both cases.

It stil has some limitations. As border is part of the world (tiled blocks) it's ok when it has same color. In case border has different colors, rendering one color outside of borders won't be a solution.

Thanks noone and Tenfour04 and I am still opened for suggestions:)

Here are some screenshots: https://www.dropbox.com/sh/00h947wkzo73zxa/AAADHehAF4WI8aJ8bu4YzB9Va?dl=0

Mateusz Gaweł
  • 673
  • 1
  • 8
  • 22
  • So long as the world is at least as big as a screen, I'm not sure how you could end up seeing beyond the borders, assuming you're using the camera clamping you showed in your question. What I do is use virtual width and height that are a 4:3 resolution, and then make sure the world has enough to cover at least a 16:9 version of that. For example, if your virtual size is 400x300, then your world needs to be at least 534 wide to ensure you never see the void. You can add extra scenery tiles to ensure this on small stages. – Tenfour04 Nov 21 '16 at 20:57
  • I added some screenshot to visualize the problem – Mateusz Gaweł Nov 21 '16 at 22:11
0

Why don't you use FitViewport instead of FullViewport? That way it won't cut off your screen, right?

John
  • 1,440
  • 1
  • 11
  • 18
0

It is a little bit late, but I have a solution for you without compromises!

Here width and height are world size in pixels. I use this code with FillViewport and everything works excellent!

float playerX = player.getBody().getPosition().x*PPM;
float playerY = player.getBody().getPosition().y*PPM;

float visibleW =  viewport.getWorldWidth()/2 + (float)viewport.getScreenX()/(float)viewport.getScreenWidth()*viewport.getWorldWidth();//half of world visible
float visibleH = viewport.getWorldHeight()/2 + (float)viewport.getScreenY()/(float)viewport.getScreenHeight()*viewport.getWorldHeight();

float cameraPosx=0;
float cameraPosy=0;

if(playerX<visibleW){
    cameraPosx = visibleW;
}
else if(playerX>width-visibleW){
    cameraPosx = width-visibleW;
}
else{
    cameraPosx = playerX;
}

if(playerY<visibleH){
    cameraPosy = visibleH;
}
else if(playerY>height-visibleH){
    cameraPosy = height-visibleH;
}
else{
    cameraPosy = playerY;
}

camera.position.set(cameraPosx,cameraPosy,0);

camera.update();
demongolem
  • 9,474
  • 36
  • 90
  • 105