I try to draw simple text in my android game on libgdx, but it\'s look sharp. How to make text look smooth in different resolutions? My Code:
Bitmap fonts are textures and if you want to make smaller textures look smoother when you are resizing them to bigger sizes you need to make sure you use the right texture filter.
This blog post deals with such issues
Now declare the font:
BitmapFont font;
Now in create method:
font = new BitmapFont(Gdx.files.internal("data/100.fnt"), false); // 100 is the font name you can give your font any name
In render:
font.setscale(.2f);
font.draw(batch, "whatever you want to write", x,y);
In general you don't get sharp text because you are designing your game for a certain resolution and when you move to a different device, Libgdx scales everything to match the new resolution. Even with linear filtering scaling is bad on text because round corners are easily distorted. In a perfect world you would create the content dynamically at runtime according to the number of pixels available to you and not a single automatic scale would be used.
This is the approach I'm using: Building everything for small screen (480 x 320), and when you open it on a bigger resolution, I load the BitmapFont with a higher size and apply and inverse scale to the one that Libgdx will later do automatically.
Here's an example to make things clearer:
public static float SCALE;
public static final int VIRTUAL_WIDTH = 320;
public static final int VIRTUAL_HEIGHT = 480;
public void loadFont(){
// how much bigger is the real device screen, compared to the defined viewport
Screen.SCALE = 1.0f * Gdx.graphics.getWidth() / Screen.VIRTUAL_WIDTH ;
// prevents unwanted downscale on devices with resolution SMALLER than 320x480
if (Screen.SCALE<1)
Screen.SCALE = 1;
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("data/Roboto-Regular.ttf"));
// 12 is the size i want to give for the font on all devices
// bigger font textures = better results
labelFont = generator.generateFont((int) (12 * SCALE));
// aplly the inverse scale of what Libgdx will do at runtime
labelFont.setScale((float) (1.0 / SCALE));
// the resulting font scale is: 1.0 / SCALE * SCALE = 1
//Apply Linear filtering; best choice to keep everything looking sharp
labelFont.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
}
In scene2d, if you want apply antialiasing to all your labels, put this on constructor of your first screen:
skin.getFont("default-font").getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
This is the first screen in my game:
...
public class MainMenuScreen implements Screen {
public MainMenuScreen() {
...
skin.getFont("default-font").getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
}
}
Font name is in ui.json file, check for BitmapFont and Label$LabelStyle section:
"com.badlogic.gdx.graphics.g2d.BitmapFont": {
"default-font": {
"file": "default.fnt"
}
},
"com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle": {
"default": {
"font": "default-font",
"fontColor": "white",
}
},
You should definitly have a quick look on custom font shaders and/or DistanceField-Fonts. They're easy to understand and similarly easy to implement:
https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
DistanceFieldFonts stay smooth, even when you upscale them:
One solution is to use the FreeType extension to libgdx, as described here. This allows you to generate a bitmap font on the fly from a .ttf font. Typically you would do this at startup time once you know the target resolution.
Here's an example:
int viewportHeight;
BitmapFont titleFont;
BitmapFont textFont;
private void createFonts() {
FileHandle fontFile = Gdx.files.internal("data/Roboto-Bold.ttf");
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(fontFile);
FreeTypeFontParameter parameter = new FreeTypeFontParameter();
parameter.size = 12;
textFont = generator.generateFont(parameter);
parameter.size = 24;
titleFont = generator.generateFont(parameter);
generator.dispose();
}