字符显示

LibGDX中有两种显示文字的方式:第一种是通过贴图和映射配置文件渲染文字,这种方式需要使用LibGDX提供的Hiero工具来制作所需的文字png贴图和fnt文字映射文件;第二种是使用FreeType扩展来输出ttf格式的字体文件中定义的字体。

本篇笔记我们分别对两种方式进行介绍。

使用贴图渲染文字

使用贴图渲染文字,需要png格式的贴图和fnt格式的文字映射文件,然后将其加载到内存中,我们也只能将贴图中有的文字显示到屏幕上。

使用Hiero工具

Hiero是LibGDX提供的用于制作字体贴图和映射文件的工具,使用非常简单,这里我们简单介绍下。

我们可以在LibGDX官网的下载页面中,找到这个工具。

https://libgdx.badlogicgames.com/old-site/releases/

它是一个可执行jar,下载下来直接运行即可。

我们在Sample Text输入框中,输入我们所需文本包含的全部文字,然后选择字体、特效、颜色、间距等等样式,工具会自动帮我们生成所需的png和fnt文件。

注:颜色这里建议只选择灰度颜色,具体到游戏中,我们再手动指定。否则字体自带的颜色和我们在Java代码中设定的颜色是相加的,不好控制。

操作界面非常简单,这里就不多说了,制作好字体文件后,点击左上角保存即可,我们可以将其放入资源文件夹中。

单行显示和多行显示

显示贴图文字需要用到BitmapFont这个类,它和Sprite的使用方式类似。

package com.mygdx.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class MyGdxGame extends ApplicationAdapter {

    private SpriteBatch batch;
    private BitmapFont font;

    @Override
    public void create() {

        batch = new SpriteBatch();

        FileHandle fontPngHandle = Gdx.files.internal("font/mybmfont.png");
        FileHandle fontFntHandle = Gdx.files.internal("font/mybmfont.fnt");

        font = new BitmapFont(fontFntHandle, fontPngHandle, false);
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batch.begin();
        font.draw(batch, "Hello, world!", 0, 32);
        font.setColor(Color.BLACK);
        batch.end();
    }
}

实例化BitmapFont需要贴图字体png和fnt的文件资源,在render()函数中,我们像Sprite一样绘制BitmapFont对象即可。

补充:其实png文件的参数可以省略,BitmapFont生成时会自动寻找对应的png文件,如果文字较多,Hiero可能生成多个png文件。

注意:我们直到绘制精灵时,精灵坐标和场景的坐标原点都是左下角,但BitmapFont的坐标是左上角(这个和Sprite不同,比较奇葩),上面代码中,font.draw()最后两个参数是相对场景坐标的偏移,我们的字体大小是32px,这里为了将文字显示在左下角,因此设定偏移量为0, 32

显示文字还涉及一个换行问题,实际上这里BitmapFont已经帮我们把这些逻辑封装好了,我们只需要在文字中间插入\n即可实现换行显示。

注:一些参考资料中,换行显示调用的是drawMultiLine函数,这个API在新版本中已经废弃了,统一使用draw即可。

font.draw(batch, "Hello,\nworld!", 0, 64);

贴图字体的局限性

贴图字体的绘制效率是最高的,但使用起来其实比较复杂,我们需要单独制作贴图和映射文件,而且以图片的形式来渲染文字,调节大小还需要指定缩放比例,不是很方便。

如果我们使用的是中文,这里就出现了一个更致命的问题:我们不得不把中文所有汉字全都导入Hiero做成贴图字体。如果是小游戏还好,如果是RPG等复杂类型的游戏,文本量会非常大,这个就不太合适了。

使用fnt字体文件渲染文字

FreeType是一个LibGDX扩展,不包含在默认创建的工程中。它主要解决的问题是手动制作贴图字体非常麻烦,因此能够基于一个ttf字体文件来生成BitmapFont对象。基于FreeType渲染文字的代码中,仅在初始化阶段和加载贴图字体不同,生成的结果还是BitmapFont

引入FreeType,我们可以在初始化项目时引入该扩展:

或者手动在Gradle中引入依赖,这里注意该扩展包含一些Native的代码(动态链接库),因此需要在CoreDesktopAndroid等工程中分别引入,比较麻烦。

Core工程:

project(":core") {
    apply plugin: "java"


    dependencies {
        ...
        compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
    }
}

Desktop工程:

project(":desktop") {
    apply plugin: "java"


    dependencies {
        compile project(":core")
        ...
        compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
    }
}

其它种类工程添加类似依赖即可。

示例代码

下面代码中,我们引入一个ttf字体文件(这里以Windows自带的华文行楷字体为例),然后显示文本你好,世界!

package com.mygdx.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;

public class MyGdxGame extends ApplicationAdapter {

    private SpriteBatch batch;
    private BitmapFont font;

    @Override
    public void create() {

        batch = new SpriteBatch();

        FileHandle fontFntHandle = Gdx.files.internal("font/stxingka.ttf");
        FreeTypeFontGenerator generator = new FreeTypeFontGenerator(fontFntHandle);
        FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
        parameter.characters = FreeTypeFontGenerator.DEFAULT_CHARS + "你好世界,!";
        parameter.size = 32;
        parameter.color = Color.WHITE;
        font = generator.generateFont(parameter);
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batch.begin();
        font.draw(batch, "你好,世界!", 0, 32);
        font.setColor(Color.BLACK);
        batch.end();
    }
}

注意:即使使用ttf字体文件,我们还是需要把用到的字符在parameter.characters中指定以下,我们游戏一般对国际化的要求是非常高的,一般至少也有汉语、英语两种语言版本,因此文本都是提取出来存储的,而不是在Java代码中硬编码的,指定字体所需文本可以通过扫描这些文本库来实现。

LibGDX巨坑之FreeType字体的局限性

LibGDX中对FreeType的封装,虽然解决了静态固定中文文本的显示,但如果是用户随意输入的文本呢?这个就是巨坑了,LibGDX至今也根本没考虑中文的问题,也没封装FreeType动态增加文字渲染的方法,FreeType依然仅支持静态初始化中文文本。如果要处理用户的中文输入,恐怕还得使用贴图字体,并把所有常用汉字导入!!!

针对这个问题,有以下几种偏门的解决方法:

  1. 扩展BitmapFont,使其支持动态添加字符渲染而不用全部重新渲染,这个写起来非常困难,而且会导致Scene2D不兼容,需要连带着改不少东西,反正我是放弃了,但理论上依然是最理想的方案
  2. 把所有汉字都做成贴图字体,相对方法2简单太多,内存占用方面并没有明显的升高,游戏体验和方法2差不多,只是开发者做起来麻烦,而且可能做一套字体还满足不了界面需求
  3. 放弃LibGDX,改用Unity、Unreal等不这么坑的游戏引擎

以下做法经过实际测试是不行的:

  1. 每次改变文本都触发重新生成BitmapFont,这个方法是比较慢的,即使只有十几个字,全部重新生成一次也会耗时100ms以上,CPU占用率较高,该方法也不能在非渲染线程调用
  2. 只加载所有汉字,然后用FreeType生成BitmapFont,这个把所有汉字用FreeType渲染一遍实在太慢了,在PC上有30s多,根本不可行
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap