如何在libgdx中绘制平滑文本?

34

我尝试在我的Android游戏上用libgdx绘制简单的文本,但它看起来很模糊。如何使文本在不同分辨率下看起来更加平滑?我的代码:

private BitmapFont font;

font = new BitmapFont();

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

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

1
请查看这篇博客文章。 - Zach Latta
1
不要缩放,而是为每个字体创建适当的大小。此外,ppu很糟糕。每个使用像素转换的教程都做错了。在你决定对象大小后以任何尺寸绘制,并告诉相机绘制多少游戏世界。 - Madmenyo
10个回答

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

这将获取BitmapFont中使用的纹理,并将其过滤更改为双线性,从而在上下缩放时以稍微慢一些(通常不会有明显差异)的GPU渲染成本换取更高的图像质量。


20
Add some explanation, please. - falsetru
2
他已经将线性纹理过滤应用于字体纹理。似乎在某些设备上有意义 - 在我的情况下,这是Nexus 7(第一代),这个解决方案帮助了我。 - Viacheslav
如何启用字体纹理生成mipmap?目前,字母显示为黑色方块。 - Neerkoli

29
一种解决方案是使用FreeType扩展到libgdx,如此处所述。这允许您从.ttf字体文件动态生成位图字体。通常,您会在启动时执行此操作,一旦确定目标分辨率即可。
以下是一个示例:
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();
}

"generateFont(int)"现在已经被弃用。我们应该使用哪种方法来生成字体? - auroranil
1
@user824294 只需编辑答案,然后被作者或某个管理员接受即可,但您可以查看此链接 https://github.com/libgdx/libgdx/wiki/Gdx-freetype - Angel Angel

24

8
不完全正确。您可以在着色器中为字体涂上颜色。您可能想表达的是一次只能使用单一颜色,但是即使如此,也可以通过更复杂的着色器或其他纹理来克服这个限制。 - TheWhiteLlama
我可以如何使用皮肤来完成它? - Alex K
@AlexKapustian,你有没有找到解决方法...实际上问题是如何在Scene2d.ui中已经存在的小部件中使用这个距离场字体特性。 - phoenisx
这不是最直接的解决方案,因为需要花费一些时间来学习指南,但它是值得的!文本显示得非常漂亮! - user2342558

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);
    

1
虽然需要一些努力才能完成所有事情,但第一次尝试就成功了...这是值得的。谢谢你。请注意,“hiero”必须从源代码中执行,没有预构建的jar可用。Hiero说明在此处:https://github.com/libgdx/libgdx/wiki/Hiero - Ville Myrskyneva

6
通常情况下,由于你为特定分辨率设计游戏,当你切换到不同的设备时,Libgdx会缩放所有内容以匹配新的分辨率,因此你无法获得清晰的文本。即使使用线性过滤,文本的缩放也会导致圆形角易失真。在完美的世界里,你应该根据可用像素数在运行时动态创建内容,而不是使用单一的自动缩放。
我采用的方法是:将所有内容构建为小屏幕(480 x 320),当你在更大的分辨率上打开时,我使用更高的大小加载BitmapFont,并对Libgdx自动执行的比例应用反比例缩放。
以下是一个示例,以使事情更加清晰:
    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);
}

1
谢谢!这种方法使字体比仅使用线性滤波要好得多。而且似乎比实现距离场要容易得多。 - Phuah Yee Keat

2

使用Libgdx实现平滑文本的解决方案

我使用BitmapFont,并使用Hiero工具生成了三种不同大小的相同字体,例如Arial 16、Arial 32和Arial 64。

我将它们放在我的资源文件中,并根据屏幕大小仅加载其中一个。

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);
            }
        }

然后我使用这行代码来提高缩放的结果质量(包括缩小和放大)

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

缩放图片

为了处理设备各种分辨率的字体大小,我使用以下两个函数:

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;
}

我使用的模型屏幕分辨率为

SCREEN_WIDTH = 480

SCREEN_HEIGHT = 320

将字体设置为所需比例

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

最后,绘制您的文本。

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

希望这个清晰且有帮助!!!

2

在更新后许多东西都被弃用了,以下是目前对我有效的方法:

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
}

当您想在屏幕上呈现它时:

// text rendering
fontLatoLight.setColor(Color.WHITE); // set color here (has other overloads too)
fontLatoLight.draw(batch, "Hello World!", xCoord, yCoord);

请注意,当调整字体大小时,它仍然看起来有点糟糕。如果您有更好的字体调整过滤器(或其他)配置,请评论。 - milosmns

2

位图字体是纹理,如果您想将较小的纹理调整大小以使其在更大的尺寸下看起来更加平滑,您需要确保使用正确的纹理过滤器。

这篇博客文章涉及此类问题


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.

??? 这只是解释如何使用.scale()方法,我声明在当前版本中已被废弃。

这段话只是解释如何使用已经在当前版本中被废弃的.scale()方法。


0
scene2d 中,如果你想对所有的标签应用抗锯齿,将以下代码放在你的第一个屏幕的构造函数中:
skin.getFont("default-font").getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);

这是我游戏中的第一个屏幕:

...
public class MainMenuScreen implements Screen {    
    public MainMenuScreen() {
        ...
        skin.getFont("default-font").getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
    }
}

字体名称在 ui.json 文件中,检查 BitmapFontLabel$LabelStyle 部分:

"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",
    }
  },

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接