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

Posted

技术标签:

【中文标题】如何在 libgdx 中绘制平滑的文本?【英文标题】:How to draw smooth text in libgdx? 【发布时间】:2013-01-18 11:02:24 【问题描述】:

我尝试在 libgdx 上的 android 游戏中绘制简单的文本,但它看起来很清晰。如何使文本在不同分辨率下看起来平滑?我的代码:

private BitmapFont font;

font = new BitmapFont();

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

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

【问题讨论】:

查看this 博文。 不要缩放,而是为每个字体创建适当的字体。 ppu也不好。每个使用像素转换的教程都做错了。以您决定对象的大小进行绘制,并告诉相机您的游戏世界要绘制多少。 【参考方案1】:

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

This blog post deals with such issues

【讨论】:

【参考方案2】:

一种解决方案是使用 libgdx 的 FreeType 扩展,如 here 所述。这允许您从 .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) 现在已弃用。我们应该使用什么方法来生成字体? @user824294 只是编辑答案,只需要被作者或一些版主接受,但你可以看看这个github.com/libgdx/libgdx/wiki/Gdx-freetype【参考方案3】:
font.getRegion().getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);

这会获取 BitmapFont 中使用的纹理,并将其过滤更改为双线性,从而在放大和缩小图像质量的同时提高图像质量,但代价是 GPU 渲染速度稍慢(差异通常不明显)。

【讨论】:

请添加一些解释。 他对字体纹理应用了线性纹理过滤。似乎这在某些设备上是有意义的——在我的例子中是 Nexus 7(1 代),这个解决方案对我有帮助。 如何启用字体纹理生成mipmap?现在,字母是黑块。【参考方案4】: 使用 libgdx 网站提供的 hiero 创建一个 .fnt 文件。 设置字体大小为150;它将创建一个 .fnt 文件和一个 .png 文件。 将这两个文件复制到您的资产文件夹中。

现在声明字体:

BitmapFont font;

现在在创建方法中:

font = new BitmapFont(Gdx.files.internal("data/100.fnt"), false); // 100 is the font name you can give your font any name

在渲染中:

font.setscale(.2f);

font.draw(batch, "whatever you want to write", x,y);

【讨论】:

需要付出一些努力才能完成所有工作,但第一次尝试所有工作......都是值得的。这次真是万分感谢。请注意,“hiero”必须从源代码执行,没有可用的预构建 jar。 Hiero 指令在这里:github.com/libgdx/libgdx/wiki/Hiero【参考方案5】:

您绝对应该快速了解自定义字体着色器和/或 DistanceField-Fonts。它们易于理解并且同样易于实施:

https://github.com/libgdx/libgdx/wiki/Distance-field-fonts

DistanceFieldFonts 保持平滑,即使您放大它们:

【讨论】:

不完全正确。您可以在着色器中为字体着色。您可能想说一次只能使用一种颜色。但即便如此,也可以通过更复杂的着色器或额外的纹理来克服。 如何使用皮肤? @AlexKapustian,你有没有得到任何解决方案......实际上问题是任何人都可以在 Scene2d.ui 中现有的小部件中使用这个距离场字体功能 这不是最直接的解决方案,因为它需要一些努力来学习指南,但值得!文字显示精美!【参考方案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);

【讨论】:

谢谢!这种方法使字体明显优于仅使用线性过滤器。而且似乎比实现距离场要容易得多。【参考方案7】:

更新后很多东西都被弃用了,这对我有用:

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

【讨论】:

请注意,在调整字体大小时它看起来仍然有点糟糕。如果您有更好的过滤器(或其他)配置来调整字体大小,请发表评论【参考方案8】:
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() 方法。

【讨论】:

【参考方案9】:

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

我使用 BitmapFont,并使用 Hiero 工具生成 3 种不同大小的相同字体 例如 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;

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

屏幕宽度 = 480

屏幕高度 = 320

将比例设置为字体

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

最后画出你的文字

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

希望这是清晰和有用的!!!

【讨论】:

【参考方案10】:

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

【讨论】:

以上是关于如何在 libgdx 中绘制平滑的文本?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 libgdx 中绘制 Button 类

库GDX 。绘制多行文本

如何在 libgdx 的舞台上绘制位图字体?

LibGDX FreetypeFont 3D

如何在libgdx使用矩形类来绘制矩形边框?

libgdx - 如何在舞台中添加背景图片?