34

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:

private BitmapFont font;

font = new BitmapFont();

font.scale((ppuX*0.02f));

font.draw(spb, "Score:", width/2-ppuX*2f, height-0.5f*ppuY);
StarsSky
  • 6,721
  • 6
  • 38
  • 63
JustOneMan
  • 231
  • 1
  • 9
  • 34
  • 1
    Check out [this](http://www.badlogicgames.com/wordpress/?p=2300) blog post. – Zach Latta Feb 03 '13 at 17:24
  • 1
    Don't scale but create appropriate font for each. Also ppu is bad. Every tutorial that uses pixel conversion does it wrong. Draw at whatever size you decide your objects are and tell the camera how much of your game world to draw. – Madmenyo May 15 '16 at 13:51

10 Answers10

53
font.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);

This gets the texture used in a BitmapFont and changes its filtering to bilinear, allowing higher resulting image quality while both up- and downscaling it at the cost of slightly slower (the difference is usually not noticeable) GPU rendering.

bluepuppy
  • 539
  • 4
  • 2
29

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();
}
R Hyde
  • 10,301
  • 1
  • 32
  • 28
  • `generateFont(int)` is now deprecated. What method should we use instead to generate the font? – auroranil Dec 12 '14 at 21:57
  • 1
    @user824294 just edit the answer, just need to be acceptada, by the author or some moderator but you can look at this https://github.com/libgdx/libgdx/wiki/Gdx-freetype – Angel Angel Dec 15 '14 at 20:45
24

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:

enter image description here

TheWhiteLlama
  • 1,276
  • 1
  • 18
  • 31
  • 8
    not absolutely correct. You can color the font in the shader. You might have wanted to say that you can only use a single color at once. But even that can be overcome with a more complex shader or an additional texture. – TheWhiteLlama Jul 05 '14 at 15:41
  • How can I do it using skin? – Alex K Aug 11 '14 at 15:45
  • @AlexKapustian , did u get any solution for that... Actually the problem is how can anyone use this Distance Field Font feature in already existing Widgets in Scene2d.ui – phoenisx May 13 '17 at 07:06
  • It is not the most immediate solution because it requires some effort to study the guide, but it is worth it! The text is displayed beautifully! – user2342558 Jan 13 '21 at 14:04
12
  • Create a .fnt file using hiero which is provided by libgdx website.
  • Set the size of font to 150; it will create a .fnt file and a .png file.
  • Copy both files into your assets folder.
  • 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);
    
Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
sandeep kundliya
  • 963
  • 8
  • 13
  • 1
    Required some effort to do everything, but all worked with 1st try... was worth it. Thank you for this. Note that the "hiero" must be executed from the source, there was no pre build jar available. Hiero instructions here: https://github.com/libgdx/libgdx/wiki/Hiero – Ville Myrskyneva Mar 12 '15 at 18:15
6

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);
}
sheitan
  • 1,076
  • 12
  • 27
  • 1
    Thank You! This approach makes the font significantly better than just doing Linear filter. And seems a lot easier than implementing Distance Field. – Phuah Yee Keat Jul 09 '14 at 15:16
2

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

Glogo
  • 2,694
  • 2
  • 23
  • 23
2

With many things deprecated after the update, this is what's working for me:

public void regenerateFonts(OrthographicCamera cam, Game game) {
    int size = 18;

    if (cam != null && game != null) {
        // camera and game are provided, recalculate sizes
        float ratioX = cam.viewportWidth / game.getW();
        float ratioY = cam.viewportHeight / game.getH();
        System.out.println("Ratio: [" + ratioX + ":" + ratioY + "]");

        size *= ratioY;
    }

    // font parameters for this size
    FreeTypeFontParameter params = new FreeTypeFontParameter();
    params.flip = true; // if your cam is flipped
    params.characters = LETTERS; // your String containing all letters you need
    params.size = size;
    params.magFilter = TextureFilter.Linear; // used for resizing quality
    params.minFilter = TextureFilter.Linear; // also

    // Lato Light generator
    FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/Lato-Light.ttf"));

    // make the font
    fontLatoLight = generator.generateFont(params);
    generator.dispose(); // dispose to avoid memory leaks
}

And when you want to render it on the screen:

// text rendering
fontLatoLight.setColor(Color.WHITE); // set color here (has other overloads too)
fontLatoLight.draw(batch, "Hello World!", xCoord, yCoord);
milosmns
  • 3,595
  • 4
  • 36
  • 48
  • Note that somehow it still looks kinda bad when resizing fonts.. Do comment if you have a better filter (or other) configuration for font resizing – milosmns Aug 10 '14 at 12:53
2

My Solution for smooth text with Libgdx

I use BitmapFont and I generate 3 different size same fonts using Hiero tool example Arial 16 , Arial 32, Arial 64

I put them in my assets file and use (load) only one of them depeding on the size of screen

if(Gdx.graphics.getWidth() < (480*3)/2)
        {
            textGametFont = BitmapFont(Gdx.files.internal(nameFont+16+".fnt"),
                    Gdx.files.internal(nameFont+16+".png"), false);
        }else
        {
            if(Gdx.graphics.getWidth() < (3*920)/2)
            {

                textGametFont = new BitmapFont(Gdx.files.internal(nameFont+32+".fnt"),
                        Gdx.files.internal(nameFont+32+".png"), false);
            }else
            {
                textGametFont = new BitmapFont(Gdx.files.internal(nameFont+64+".fnt"),
                        Gdx.files.internal(nameFont+64+".png"), false);
            }
        }

then I use this line of code to higher result quality of down and up Scaling

textGametFont.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);

scale the image

to handle the size of the font for all type of resolution of device I use those two functions

public static float xTrans(float x)
{
    return x*Gdx.graphics.width/(YourModel.SCREEN_WIDTH);
}

public static float yTrans(float y)
{
    return y*Gdx.graphics.height/YourModel.SCREEN_Height;
}

the model screen resolution that i use is

SCREEN_WIDTH = 480

SCREEN_HEIGHT = 320

Set the scale to the font

textGametFont.setScale((xtrans(yourScale)+ ytrans(yourScale))/2f);

and finally draw your text

textGametFont.draw(batch, "WINNER !!", xTrans(250), yTrans(236));

Hope this was clear and helpful !!!

Netero
  • 3,761
  • 2
  • 29
  • 29
1
private BitmapFont font;

font = new BitmapFont();

font.scale((ppuX*0.02f));

font.draw(spb, "Score:", width/2-ppuX*2f, height-0.5f*ppuY);

    Check out [this](http://www.badlogicgames.com/wordpress/?p=2300) blog post.

??? This just explains how to use the .scale() method which I'm stating is deprecated in the current release.

0

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",
    }
  },
Andreybeta
  • 430
  • 5
  • 7