I have developed a game on my phone which has 1080x1920(portrait) resolution. And I set my fitviewport according to it. It looks like this on my phone:
But on any other phone or on desktop camera has wrong positioning. It looks like this on other devices:
I have used fitviewport in other projects before but never encountered that problem. May it be because of portrait mode?
EDIT
Related code is here:
//in create
camera = new Orthographiccamera();
camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeigth());
viewport = new FitViewport(1080,1920,camera);
//in resize
viewport.update(width,height);
//before starting batch
batch.setProjectionMatrix(camera.combined);
You have two contradicting statements in your code: camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeigth()). This will set the camera's viewport size to the display size and then center the camera.
On the other hand, you want to use a FitViewport with a fixed size of 1080x1920.
If your screen size is exactly 1080x1920, there's no problem, because the center will be the same. If the screen size differs from the one you use in FitViewport, then setToOrtho() will set the camera's center to a position that's not the center of 1080x1920 and that's why you notice the offset.
Using viewport.update(width, height, true) will correct this. The last parameter will center the camera correctly and override what happened in setToOrtho().
Let the viewport manage your camera and remove the call of camera.setToOrth().
Related
So I am having a little hard time understanding how ortographic cameras work in libgdx.
what I want is to have a camera that will only render things within a square while having another camera set the bounds for my whole screen.
So here, I was able to do what I wanted on the whole screen for the game pad. But, the thing you see on the top right is the background map of the game and i want to render the parts only fall within the red square you see here. How do I achieve that?
Are cameras supposed to do that or do I need to figure out a way to do it manually? I am really confused as to how cameras and projection matrices work.
Here on this screen, The red square and the green pad on the left are being drawn using the projection matrix of my screen camera. The map(top right) is drawn using my map cam.
Map cam is a view port of 400x400 but as you can see , the tiles are rectangular and that isnt the aspect ration i want. If someone can briefly explain how cameras work , I'd greatly appreciate it.
The reason I am not posting my code here is because I feel like I need to understand how camera mechanics work to even code it properly so I want to address that issue first.
Following #Tenfour04's advice worked perfectly. In case anyone wonders what I wanted to achieve. Here's a picture.
A camera alone cannot crop off part of the screen. For that you need to use glViewport. There is already a Viewport class in Libgdx that can do that for you. You will need two orthographic cameras (one for the map and one for the GUI), but the viewport can create its own.
private Viewport viewport;
//in create:
viewport = new FitViewport(400, 400);
//in resize:
viewport.update(width, height);
//in render:
viewport.getCamera().position.set(/*...move your map camera as needed*/);
viewport.apply(); //viewport cropped to your red square
batch.setProjectionMatrix(viewport.getCamera().combined);
batch.begin();
//draw map
batch.end();
//return to full screen viewport
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.setProjectionMatrix(yourGUICamera.combined);
batch.begin();
//draw gui
batch.end();
What happens, is the camera will fit itself to the size of the screen. In order to change this, you would want to use a FrameBuffer. The frame buffer will constrain the camera into the desired size, then can be drawn as a texture.
Create the frame buffer with the dimensions being in pixels.
//Initialize the buffer
FrameBuffer fbo = new FrameBuffer(Format.RGB565, width, helght, false);
Render the world within the buffer.
fbo.begin();
//Draw the world here
fbo.end();
Draw the buffer to the screen with a batch.
batch.begin();
batch.draw(fbo.getColorBufferTexture(), x, y);
batch.end();
Here is the code:
http://hastebin.com/ugufakeyap.java
When I run this, the sprite "aspectRatios" is not displayed on the screen. But when I pause the program by pressing my home button and then resuming the program, it displays the sprite onto the screen. Why is this? It's really annoying.
you need to call camera.update(); after changing position. My best guess is that libGDX automatically updates the camera on resume.
public void create () {
batch = new SpriteBatch();
txt = new Texture(Gdx.files.internal("test2.png"));
aspectRatios = new Sprite(txt);
aspectRatios.setPosition(0,0);
//aspectRatios.setSize(100,100);
camera = new OrthographicCamera();
viewport = new StretchViewport(1080,1920,camera);
camera.position.set(camera.viewportWidth/2,camera.viewportHeight/2,0);
camera.update();
viewport.apply();
//batch.setProjectionMatrix(camera.combined);
Gdx.input.setInputProcessor(this);
}
You would normally update the batch projection matrix there as well but you do it every frame in the render method anyway.
Some people helped me on the libGDX irc channel.
It turns out that I wasn't calling
viewport.update(width, height, centerCamera);
(Thanks to Xoppa)
And (according to ASneakyFox):
"you have to assume that the android device isnt going to be conveniently the size you want, itll call resize when the app opens up or changes orientation so you can modify your layout to be the dimensions of the screen (ie change the viewport)"
Here is the updated version of the code:
http://hastebin.com/olivopocix.java
Hell All & thanks for reading,
I recently started working on an 2D Android/Desktop project and have become stuck trying to display my sprites in the way i want.
I have a background Sprite that is 144(w) by 160(h) that I want to be able to position other sprites onto the screen relative to points on the background sprite.
I think I understand that if I create a camera/viewport that is 144 x 160 I would be able to position my sprites on the background sprite using the co-ordinates based on the 144 x 160 of the background sprite. This will work across the different screen resolutions found on mobile devices but will stretch the background sprite despite experimenting with the different viewport types (FillViewport, FitViewport etc..).
What I want to achieve is to have my background sprite to maintain it ratio across different screen resolutions and to be able to place other sprites over the background sprite. The placing of sprite need to work across different resolutions.
Apologies if my explanation is confusing or makes no sense. I would add some image to help explain but I reputation to add any to the post. However I think the TLTR question is "What is the correct way to display sprites on multiple screen resolutions while keeping a correct ratios and scaling to the screen size and position of sprite in a way that works across multiple resolutions?"
Thank, All Questions Welcome
A FitViewport would do what you described (maintain aspect ratio), but you will have black bars on some devices. Based on the code you posted on the libgdx forum, I see that you forgot to update the viewport in the resize method, so it is not behaving as designed.
However, for a static camera game like what you described, I think the best solution would be to plan your game around a certain area that is always visible on any device, for example, the box from (0,0) to (144,160). Then use an ExtendViewport with width and height of 144 and 160. After you update the viewport in resize, you can move the camera to be centered on the rectangle like this:
private static final float GAME_WIDTH = 144;
private static final float GAME_HEIGHT = 160;
public void create(){
//...
viewport = new ExtendViewport(GAME_WIDTH, GAME_HEIGHT);
//...
}
public void resize(int width, int height){
viewport.update(width, height, false); //centering by putting true here would put (0,0) at bottom left of screen, but then the game rectangle would be off center
//manually center the center of your game box
Camera camera = viewport.getCamera();
camera.position.x = GAME_WIDTH /2;
camera.position.y = GAME_HEIGHT/2;
camera.update();
}
Now your 144x160 box is centered on the screen as it would be with FitViewport, but you are not locked into having black bars, because you can draw extra background outside the 144x160 area using whatever method you like.
In your case 144:160 is a wider portrait aspect ratio than any screen out there, so you wouldn't need to worry about ever filling in area to the sides of your game rectangle. The narrowest aspect ratio of any phone or tablet seems to be 9:16, so you can do the math to see how much extra background above and below the game rectangle should be drawn to avoid black showing through on any device.
In this case it works out to 48 units above and below the rectangle that you would want to fill in:
144 pixels wide at 9:16 would be 256 tall.
(256 - 160) / 2 = 48
EDIT: I see from your post on the libgdx forum that you want the game area stuck at the top of the screen and the remainder of the area to be used for game controls. In that case, I would change the resize method like this, since you want to have the game area's top edge aligned with the top edge of the screen. You can also calculate where the bottom of the controls area will be on the Y axis. (The top will be at Y=0.)
public void resize(int width, int height){
viewport.update(width, height, false);
//align game box's top edge to top of screen
Camera camera = viewport.getCamera();
camera.position.x = GAME_WIDTH /2;
camera.position.y = GAME_HEIGHT - viewport.getWorldHeight()/2;
camera.update();
controlsBottomY = GAME_HEIGHT - viewport.getWorldHeight();
}
I'm not sure how you plan to do your controls, but they would need to fit in the box (0, controlsBottomY) to (GAME_WIDTH, 0). Keep in mind that there are some phones with aspect ratios as small as 3:4 (although rare now). So with your 0.9 aspect ratio, on a 3:4 phone only the bottom 17% of the screen would be available for controls. Which might be fine if it's just a couple of buttons, but would probably be problematic if you have a virtual joystick.
Hopefully the title isn't too confusing, but I am new to LibGDX and after hours of searching on Google I finally got the camera and viewport to work, it now properly scales to the screen. There's still one problem, while the Y coordinate at 0 is correct, the X coordinate at 0 seems to be 1/a few pixels off, I am bad at describing, so here's a picture: Link to image
Like I said, I'm new to LibGDX and I am guessing it's a pretty obvious mistake, but here's the code I'm using:
I use these variables:
public static final int WORLD_WIDTH = 480;
public static final int WORLD_HEIGHT = 800;
OrthographicCamera cam;
Viewport viewport;
SpriteBatch batch;
Texture img;
I have this in the create():
cam = new OrthographicCamera(WORLD_WIDTH, WORLD_HEIGHT);
cam.setToOrtho(false, WORLD_WIDTH, WORLD_HEIGHT);
viewport = new FitViewport(WORLD_WIDTH, WORLD_HEIGHT);
img = new Texture("badlogic.jpg");
I have this in the render():
batch.setProjectionMatrix(cam.combined); //Important
And further down in render() I have this code:
batch.begin();
batch.draw(img, 0, 0);
batch.end();
And I have this in resize():
viewport.update(width, height);
Which gives the result from the picture.
Is there a solution to this problem, do I need to alter a part of the code, add some new lines or is there a smarter way to do a camera/viewport?
I'm looking forward to your answers, thanks in advance.
Cheers!
What you are experiencing is the way it is supposed to be. You are working with a FitViewport and a "virtual" resolution. A FitViewport keeps the aspect ratio of the given viewport size, while scaling it up or down to fill your game window. If your window does not have the same aspect ratio like the virtual resolution, FitViewport will lead to parts of your window being "empty" (that means having only the glClear-color).
Have a look at the Viewports wiki page, where you can see this behaviour in a more extreme way.
If you don't want this, you can try it with the "opposite" FillViewport (no empty areas, but some parts may be cut off because they are "outside" of your window), a ScreenViewport (good for UI), or an ExtendViewport.
About a year ago I developed a full LibGDX game tutorial and provided code that was reusable for any game you'd want to create. I included a dynamic Camera which handled the aspect ratio and resolution perfectly. You can take a look at the code for the camera here and determine how it is similar to yours and if there are any things you can do to improve your camera.
I have a 480x800 resolution and I'm using the following code to set it:
cam = new OrthographicCamera();
cam.setToOrtho(false, VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
viewport = new FitViewport(VIRTUAL_WIDTH, VIRTUAL_HEIGHT, cam);
nothing strange further in my code but my issue is that the origin point (0,0) is not on the very bottom but something like 20 pixels upper.
How can I fix it?
#Tenfour04 should be right here, but i try to explain it a bit:
The FitViewport is there to guarantee, that the virtual aspect ratio (VIRTUAL_HEIGHT/VIRTUAL_WIDTH) is the same as the aspect ratio on screen. Therefore, if your "real" aspect ratio (of the screen) is different, then the virtual one, black borders are shown, to create a "window" which matches the virtual aspect ratio.
Therefore the P(0/0) is not the bottom left corner of the screen, but the bottom left corner of the new "window".
If thats not what you want, there are other Viewports, which may fit your needs.
The StretchViewport for example supports virtual screen sizes and simply stretches the image to fit the screens real aspect ratio. This could be better in your case, but in some other cases the stretched images just don't look good...
FitViewport does not guarantee that 0,0 will be in the corner when you center it. All other existing Viewport subclasses do, so use one of those. And make sure you are calling update on the viewport in the resize method of you Game or Screen subclass.
Edit:
Actually, FillViewport also doesn't guarantee 0,0 in the corner, because 0,0 could be cropped off.