I have implemented some screens using libGDX that would obviously use the Screen
class provided by the libGDX framework. However, the implementation for these screens works only with pre-defined screen sizes. For example, if the sprite was meant for a 640 x 480 size screen (4:3 Aspect ratio), it won't work as intended on other screen sizes because the sprites go par the screen boundaries and are not scaled to the screen size at all. Moreover, if simple scaling would have been provided by the libGDX, the issue I am facing would have still been there because that would cause the aspect ratio of the game screen to change.
After researching on internet, I came across a blog/forum that had discussed the same issue. I have implemented it and so far it is working fine. But I want to confirm whether this is the best option to achieve this or whether there are better alternatives. Below is the code to show how I am dealing with this legitimate problem.
FORUM LINK: http://www.java-gaming.org/index.php?topic=25685.new
public class SplashScreen implements Screen {
// Aspect Ratio maintenance
private static final int VIRTUAL_WIDTH = 640;
private static final int VIRTUAL_HEIGHT = 480;
private static final float ASPECT_RATIO = (float) VIRTUAL_WIDTH / (float) VIRTUAL_HEIGHT;
private Camera camera;
private Rectangle viewport;
// ------end------
MainGame TempMainGame;
public Texture splashScreen;
public TextureRegion splashScreenRegion;
public SpriteBatch splashScreenSprite;
public SplashScreen(MainGame maingame) {
TempMainGame = maingame;
}
@Override
public void dispose() {
splashScreenSprite.dispose();
splashScreen.dispose();
}
@Override
public void render(float arg0) {
//----Aspect Ratio maintenance
// update camera
camera.update();
camera.apply(Gdx.gl10);
// set viewport
Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
(int) viewport.width, (int) viewport.height);
// clear previous frame
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// DRAW EVERYTHING
//--maintenance end--
splashScreenSprite.begin();
splashScreenSprite.disableBlending();
splashScreenSprite.draw(splashScreenRegion, 0, 0);
splashScreenSprite.end();
}
@Override
public void resize(int width, int height) {
//--Aspect Ratio Maintenance--
// calculate new viewport
float aspectRatio = (float)width/(float)height;
float scale = 1f;
Vector2 crop = new Vector2(0f, 0f);
if(aspectRatio > ASPECT_RATIO) {
scale = (float) height / (float) VIRTUAL_HEIGHT;
crop.x = (width - VIRTUAL_WIDTH * scale) / 2f;
} else if(aspectRatio < ASPECT_RATIO) {
scale = (float) width / (float) VIRTUAL_WIDTH;
crop.y = (height - VIRTUAL_HEIGHT * scale) / 2f;
} else {
scale = (float) width / (float) VIRTUAL_WIDTH;
}
float w = (float) VIRTUAL_WIDTH * scale;
float h = (float) VIRTUAL_HEIGHT * scale;
viewport = new Rectangle(crop.x, crop.y, w, h);
//Maintenance ends here--
}
@Override
public void show() {
camera = new OrthographicCamera(VIRTUAL_WIDTH, VIRTUAL_HEIGHT); //Aspect Ratio Maintenance
splashScreen = new Texture(Gdx.files.internal("images/splashScreen.png"));
splashScreenRegion = new TextureRegion(splashScreen, 0, 0, 640, 480);
splashScreenSprite = new SpriteBatch();
if(Assets.load()) {
this.dispose();
TempMainGame.setScreen(TempMainGame.mainmenu);
}
}
}
UPDATE: I recently came to know that libGDX has some of its own functionality to maintain aspect ratios which I would like to discuss here. While searching the aspect ratio issue across the internet, I came across several forums/developers who had this problem of "How to maintain the aspect ratio on different screen sizes?" One of the solutions that really worked for me was posted above.
Later on when I proceeded with implementing the touchDown()
methods for the screen, I found that due to scaling on resize, the co-ordinates on which I had implemented touchDown()
would change by a great amount. After working with some code to translate the co-ordinates in accordance with the screen resize, I reduced this amount to a great extent but I wasn't successful to maintain them with pin point accuracy. For example, if I had implemented touchDown()
on a texture, resizing the screen would shift the touchListener on the texture region some pixels to the right or left, depending on the resize and this was obviously undesired.
Later on I came to know that the stage class has its own native functionality to maintain the aspect ratio (boolean stretch = false
). Now that I have implemented my screen by using the stage class, the aspect ratio is maintained well by it. However on resize or different screen sizes, the black area that is generated always appears on the right side of the screen; that is the screen is not centered which makes it quite ugly if the black area is substantially large.
Can any community member help me out to resolve this problem?
Since this is one of the most famous questions on libgdx here, I'll give it a little update:
LibGDX v1.0 introduced Viewport
to handle this problem. It is a lot easier to use and the scaling strategy is pluggable, which means a single line can change the behaviour and you can play with it and see which one fits your game the best.
Everything you need to know about it can be found here.