pedja
pedja

Reputation: 3413

handle aspect ratio in libgdx android

I am developing game with libgdx and i got stuck with aspect ratio on different devices.
After a lot of thinking i figured that following is best solution for the problem:

I want to have camera always fixed to 16:9 aspect ratio and draw everything using that camera
If a device aspect is for example 4:3 i want to show only part of the view, not to strech it.

it should look something like this

aspect ratio

blue is virtual screen(camera viewport) and red is device screen(visible area to 4:3 devices)
If for example device screen is also 16:9 full view should be visible.

Problem is i don't know how to achieve this.

Upvotes: 2

Views: 2238

Answers (3)

Nolesh
Nolesh

Reputation: 7018

I have written a function which works with different sizes of screen and images. The image might be zoomed and translated if it's possible.

 public void transformAndDrawImage(SpriteBatch spriteBatch, Texture background, float scl, float offsetX, float offseY){
    float width;
    float height;

    float _offsetX;
    float _offsetY;

    float bh2sh = 1f*background.getHeight()/Gdx.graphics.getHeight();
    float bw2sw = 1f*background.getWidth()/Gdx.graphics.getWidth();
    float aspectRatio = 1f*background.getHeight()/background.getWidth();

    if(bh2sh>bw2sw){                    
        width = background.getWidth() / bw2sw * scl;
        height = width * aspectRatio;
    }
    else{
        height = background.getHeight() / bh2sh * scl;
        width = height / aspectRatio;                   
    }           

    _offsetX = (-width+Gdx.graphics.getWidth())/2;  
    _offsetX += offsetX;
    if(_offsetX-Gdx.graphics.getWidth()<=-width) _offsetX=-width+Gdx.graphics.getWidth();
    if(_offsetX>0) _offsetX=0;

    _offsetY = (-height+Gdx.graphics.getHeight())/2;
    _offsetY += offseY;
    if(_offsetY-Gdx.graphics.getHeight()<=-height) _offsetY=-height+Gdx.graphics.getHeight();
    if(_offsetY>0) _offsetY=0;

    spriteBatch.draw(background, _offsetX, _offsetY, width, height);
}

How to use it? It's easy:

@Override
public void render(float delta) {

   Gdx.graphics.getGL10().glClearColor( 0.1f, 0.1f, 0.1f, 1 );
    Gdx.graphics.getGL10().glClear( GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT );

    spriteBatch.setProjectionMatrix(cam.combined);                      
    spriteBatch.begin();    

    //zoom the image by 20%
    float zoom = 1.2f; //Must be not less than 1.0

    //translating the image depends on a few parameters such as zooming, aspect ratio of screen and image 
    offsetX+=0.1f; //offset the image to the left, if it's possible 
    offsetY+=0.1f; //offset the image to the bottom, if it's possible 

    transformAndDrawImage(spriteBatch, background, zoom, offsetX, offsetY);

    //draw something else...
    spriteBatch.end();

}

Upvotes: 0

ffcm
ffcm

Reputation: 207

The newer versions of libGDX provides a wrapper for glViewport called Viewport.

Camera camera = new OrthographicCamera();

//the viewport object will handle camera's attributes
//the aspect provided (worldWidth/worldHeight) will be kept
Viewport viewport = new FitViewport(worldWidth, worldHeight, camera);     

//your resize method:
public void resize(int width, int height)
{
    //notice that the method receives the entire screen size
    //the last argument tells the viewport to center the camera in the screen
    viewport.update(width, height, true);
}

Upvotes: 1

cavpollo
cavpollo

Reputation: 4318

I've done this for portrait screens, leaving some blank spaces at the top and bottom. As you can see in the following picture, the game stays at a 4:3 ratio and leaves whatever is leftover blank.

Portrait example of game keeping aspect ratio

The content is always centered, and keeps its aspect ratio by not stretching the content unevenly. So here is some sample code Im using to achieve it.

public static final float worldW = 3;
public static final float worldH = 4;
public static OrthographicCamera camera;

...

//CALCULATING THE SCREENSIZE
float tempCalc = Gdx.graphics.getHeight() * GdxGame.worldW / Gdx.graphics.getWidth();
if(tempCalc < GdxGame.worldH){          
    //Adjust width
    camera.viewportHeight = GdxGame.worldH;
    camera.viewportWidth = Gdx.graphics.getWidth() * GdxGame.worldH / Gdx.graphics.getHeight();

    worldWDiff = camera.viewportWidth - GdxGame.worldW;
}else{
    //Adjust Height
    camera.viewportHeight = tempCalc;
    camera.viewportWidth = GdxGame.worldW;

    worldHDiff = camera.viewportHeight - GdxGame.worldH;
}
camera.position.set(camera.viewportWidth/2f - worldWDiff/2f, camera.viewportHeight/2f  - worldHDiff/2f, 0f);
camera.zoom = 1f;
camera.update();

I'm sure im not proposing the perfect solution, but you can play with the values on how the camera position and viewports are calculated so you can achieve the desired effect. Better than nothing I guess.

Also, Clash Of The Olympians Developers talk about how to achieve something like it and still make it look good on different devices (it is really interesting, but there is no code though): Our solution to handle multiple screen sizes in Android - Part one

Upvotes: 1

Related Questions