6
\$\begingroup\$

I am developping a Android smartphone game in LibGDX, and I am encountering issues about resolution managing. Most notably, I have no clue how to handle that in LibGDX. For now, I am developping based on my Nexus 5 screen, but it will certainly not (down)scale well to devices with lower density screens.

On the web, most data are about LibGDX < 1.0.0 and is totally irrelevant. The few things I have found talks about the Viewport classes, but it is poorly explained ; the game is either horribly stretch or way too small on high-definition devices.

At this point, I have understood that I need to define a "virtual screen" of some unit (lets say meters) and then place my elements based on this unit. But there is no fragment of clue on how to do that the right way in LibGDX ; when using Scene2D, for example, buttons are totally misshaped, with letters out of the box, buttons clearly overstretched and such, or ultra small based on the Viewport class used.

In the same way, using multiple asset size is not clear ; how to load them based on pixel density, how to force them to be there at the good size ? Etc... This stuff can make anyone crazy at start.

What I want to know is : is there somewhere some good, updated to recent libGDX, demos, examples or tutorial to the proper way to handle resolution, screen ratio and such in recent libGDX ? The libGDX viewport example, while a good start, are still unclear to me, and don't show the multiple asset size thing.

Thanks for any help, it is highly needed.

\$\endgroup\$
  • \$\begingroup\$ Hi and welcome to our site. You may find these questions/answers useful: Resolution independence in libGDX and resolution-independence with raster graphics. FYI, I use large assets, develop my games on a fixed size, and scale up or down (and letterbox) to fit the device screen. \$\endgroup\$ – ashes999 Jul 30 '14 at 10:01
  • \$\begingroup\$ I have previously seen those links. However, they are only theorical. While useful, they are missing concrete implementation in recent LibGDX (in the first link, the camera answer seems to not work properly - and claims 800x480 is the best Android screen) \$\endgroup\$ – Kamalen Aug 5 '14 at 7:34
  • \$\begingroup\$ Are you interested in using my approach? For example, you develop your game for 1280x960 and scale (plus letterbox) it to the current device (eg. 320x240). \$\endgroup\$ – ashes999 Aug 5 '14 at 10:11
  • \$\begingroup\$ From what I have seen from two test devices, "streching" high quality assets to a lower screen has poor quality. I prefer to aim at multiple assets size for different resolutions. \$\endgroup\$ – Kamalen Aug 5 '14 at 10:19
4
\$\begingroup\$

Unfortunately, there is no magic: the only good way is to implement resolution independence yourself. While libgdx may be of some help, it's not going to solve your every problem.

For my NoThree puzzle game (see profile for link), I drew my graphics in Inkscape and I exported several resolutions (144x144, 72x72, 48x48) for each texture (the font is also packed in several sizes).

At loadtime, depending on the resolution, you should select the size you need:

int [] sizes = { 48, 72, 144 };
int size = 144;
for (int i = sizes.length - 1; i >= 0; --i) {
    if (sizes[i] * 10 > Math.min(Gdx.graphics.getHeight(), Gdx.graphics.getWidth())) {
        size = sizes[i];
    }
}

After this, you load the texture of size "size". The example above is for when you need about 10 sprites to fit the screen. You should choose font size similarly. You also want to make sure the textures are antialiased.

In the resize() method, I calculate the actual sizes and positions of all graphic elements. Here's an example:

    float pad = Math.min(width, height) / 32.0f;
    // have this amount of space between graphic elements (in pixels)
    if (landscape) {
        boardSize = Math.min(height - 2.0f * pad, width - 4 * pad - 2 * textWidth);
        // calculate the size of the game board (in pixels) making sure
        // the board fits in the screen along with the side text/buttons/etc
    } else {
        boardSize = Math.min(height - 6.0f * pad - 6.0f * textHeight, width);
    }

    if (landscape) {
        float startX = (width - boardSize) / 2;
        float startY = height - pad - boardSize;
        gameBoard.setPosition(startX, startY); // set the position of the game board
    } else {
        ...
    }

In render(), I would then simply call gameBoard.draw(). The gameBoard itself is an Actor that renders/keeps track of the game world.

To summarise, load textures of appropriate size (depending on the size of the screen and how many elements you need to fit on that screen), in resize() recompute the actual positions/sizes of the graphic elements and in render simply call their draw method.

\$\endgroup\$
3
\$\begingroup\$

I'm studying this book, and here what I learned so far:

You need to set a OrthographicCamera passing width and height of your screen, If you want pixel-perfect manipulation, pass the screen resolution, but for your problem you could just pass a size in 'meters'

camera = new OrthographicCamera(5.0f, 5.0f);

In your render method, you need to update the camera and set a projectionMatrix

camera.position.x = x;
camera.position.y = y;
camera.zoom = zoom;
camera.update();
batch.setProjectionMatrix(camera.combined);

Then you can draw everything on the screen based in this generic 'meter'

public class Rock extends AbstractGameObject{
    private void init(){
        dimension.set(1,1.5f); // in 'meters'
        regEdge = Assets.instance.rock.edge;
        regMiddle = Assets.instance.rock.middle;

        // initial size
        setLenght(1); // in 'meter' too
    }
}

and the draw method

    batch.draw(
                reg.getTexture(),
                position.x + relX, position.y + relY,
                origin.x, origin.y, 
                dimension.x / 4, dimension.y,
                scale.x, scale.y,
                rotation,
                reg.getRegionX(), reg.getRegionY(),
                reg.getRegionWidth(), reg.getRegionHeight(),
                false, false
                );

The code and explanation and the explanation is oversimplified due my own limitations, but I hope it can be somewhat useful

\$\endgroup\$
  • \$\begingroup\$ I don't think this is working anymore with my LibGDX 1.2, as the 1080p background I am using is reduced to a 5x5 pixel image, then streched again using this code. \$\endgroup\$ – Kamalen Aug 5 '14 at 7:30
1
\$\begingroup\$

I'm still rookie about libgdx and I currently started to port my android game to iOS via libgdx. In the android version I have drawable folders (assets) for different screen densities and resolutions and the loading of the proper ones works automatically (android knows which folder to access based on the device specs).

What I read everywhere for libgdx is that people use Viewports and Camera to handle different resolutions. This maybe a good case for most games, but it seems like overhead to implement for my game which fits entirely on the device screen (it's a card game).

I decided to give up Viewport and Camera and try to implement the android scheme with direct batch drawing on the screen. So what I currently try to do is this:

1) have 4 folders with different quality assets: mdpi, hdpi, xhdpi, xxhdpi. I borrowed the names from android.

assets/mdpi/ is for resolutions like >= 320x480
assets/hdpi/ >= 480x800
assets/xhdpi/ >= 720x1280
assets/xxhdpi/ >= 1080x1920

I still haven't decided what to do for tablets, but it will be something similar.

2) when game is starting make a simple check for these resolutions (if,else,switch) and manually load the corresponding textures

3) directly draw things on screen with batch and calucalations based on the real pixel dimensions of the textures and the real pixel dimensions of the screen. For example, if I want to center an image on screen I will calculate it's coordinates like this:

TextureRegion myImage = myAtlas.findRegion('myImage');
int myImageX = (Gdx.graphics.getWidth() - myImage.getRegionWidth()) / 2;
int myImageY = (Gdx.graphics.getHeight() - myImage.getRegionHeight()) / 2;

4) a nice consequence of not using Viewport and Camera and doing things directly with screen pixel coordinates is that all libgdx event coordinates (touch, mouse, etc.) can be used directly to tell what object is clicked and there is no need to unproject, project and translate coordinates in any way. This seems like a benefit as it's more simple to think about positions, because there are no separate coordinate systems to switch between, and constantly keep converting and imagining in your mind what should happen and when it will happen on screen.

I realise that this is super simple solution that may not prove viable in the future. However for the moment while I still learn about the framework, I decided to give it a try and see if it may prove worthy for my game.

These are some links that may be useful in regard to android and ios:

http://www.iosres.com/

http://developer.android.com/guide/practices/screens_support.html

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.