LIBGDX实时绘制字符、实时绘制中文

注意,相比于贴图字体,实时绘制会有一定的失真、模糊

Maven项目依赖:

<properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <gdx.version>1.12.0</gdx.version>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.badlogicgames.gdx/gdx -->
        <dependency>
            <groupId>com.badlogicgames.gdx</groupId>
            <artifactId>gdx</artifactId>
            <version>${gdx.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.badlogicgames.gdx/gdx-backend-lwjgl3 -->
        <dependency>
            <groupId>com.badlogicgames.gdx</groupId>
            <artifactId>gdx-backend-lwjgl3</artifactId>
            <version>${gdx.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.badlogicgames.gdx/gdx-platform -->
        <dependency>
            <groupId>com.badlogicgames.gdx</groupId>
            <artifactId>gdx-platform</artifactId>
            <version>${gdx.version}</version>
            <classifier>natives-desktop</classifier>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.badlogicgames.gdx/gdx-freetype -->
        <dependency>
            <groupId>com.badlogicgames.gdx</groupId>
            <artifactId>gdx-freetype</artifactId>
            <version>${gdx.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.badlogicgames.gdx/gdx-freetype-platform -->
        <dependency>
            <groupId>com.badlogicgames.gdx</groupId>
            <artifactId>gdx-freetype-platform</artifactId>
            <version>${gdx.version}</version>
            <classifier>natives-desktop</classifier>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.9</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.9</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <!-- 编译后保持方法形参名称不变 -->
                    <!--<compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>-->
                </configuration>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>tencent</id>
            <name>tencent</name>
            <layout>default</layout>
            <url>http://mirrors.cloud.tencent.com/nexus/repository/maven-public/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
        <repository>
            <id>nexus</id>
            <name>Nexus</name>
            <layout>default</layout>
            <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
        <repository>
            <id>aliyunmaven</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

重写字体

package top.lingkang.yzcy.common.font;

import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g2d.freetype.FreeType;
import com.badlogic.gdx.graphics.g2d.freetype.FreeType.Face;
import com.badlogic.gdx.graphics.g2d.freetype.FreeType.SizeMetrics;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeBitmapFontData;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.GlyphAndBitmap;
import com.badlogic.gdx.utils.GdxRuntimeException;

import java.lang.reflect.Field;

/**
 * 对BitmapFont对字体进行实时绘制,让文字获取直接从字体中获取
 * 调用方式:
 * <pre>
 *     protected BitmapFont bitmapFont; <br/>
 *     //加载 仿宋字体
 *     FreeTypeFontGenerator generator = new FreeTypeFontGenerator(new FileHandle("C:\\Windows\\Fonts\\simfang.ttf"));
 *     <br/> <br/>
 *     bitmapFont = new SmartBitmapFont(generator,50);
 *
 *     <br/>
 *     bitmapFont.draw(batch, "欢迎", 0, 300);// 左下角起点,向上300个高度
 * </pre>
 */
public class RealBitmapFont extends BitmapFont {

    // 不应该在此对此进行 dispose ,让上层进行 dispose
    private final FreeTypeFontGenerator generator;
    private final FreeTypeBitmapFontData data;
    // 翻转
    private boolean flip;


    public RealBitmapFont(FreeTypeFontGenerator generator, int fontSize) {
        if (generator == null)
            throw new GdxRuntimeException("FreeTypeFontGenerator 不能为空");
        this.generator = generator;
        this.data = new LazyBitmapFontData(generator, fontSize, this);
        try {
            Field f = getClass().getSuperclass().getDeclaredField("data");
            f.setAccessible(true);
            f.set(this, data);
        } catch (Exception e) {
            e.printStackTrace();
        }

        genrateData();
    }

    public RealBitmapFont setLineHeight(int lineHeight) {
        data.lineHeight = lineHeight;
        data.down = -data.lineHeight;
        return this;
    }

    public boolean isFlip() {
        return flip;
    }

    public void setFlip(boolean flip) {
        this.flip = flip;
    }

    private void genrateData() {
        Face face = null;
        try {
            Field field = generator.getClass().getDeclaredField("face");
            field.setAccessible(true);
            face = (Face) field.get(generator);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        // set general font data
        SizeMetrics fontMetrics = face.getSize().getMetrics();

        // Set space glyph.
        Glyph spaceGlyph = data.getGlyph(' ');
        if (spaceGlyph == null) {
            spaceGlyph = new Glyph();
            spaceGlyph.xadvance = (int) data.scaleX;//spaceWidth
            spaceGlyph.id = (int) ' ';
            data.setGlyph(' ', spaceGlyph);
        }
        if (spaceGlyph.width == 0)
            spaceGlyph.width = (int) (spaceGlyph.xadvance + data.padRight);

        // set general font data
        data.flipped = flip;
        data.ascent = FreeType.toInt(fontMetrics.getAscender());
        data.descent = FreeType.toInt(fontMetrics.getDescender());
        data.lineHeight = FreeType.toInt(fontMetrics.getHeight());

        // determine x-height
        for (char xChar : data.xChars) {
            if (!face.loadChar(xChar, FreeType.FT_LOAD_DEFAULT))
                continue;
            data.xHeight = FreeType.toInt(face.getGlyph().getMetrics().getHeight());
            break;
        }
        if (data.xHeight == 0)
            throw new GdxRuntimeException("No x-height character found in font");
        for (char capChar : data.capChars) {
            if (!face.loadChar(capChar, FreeType.FT_LOAD_DEFAULT))
                continue;
            data.capHeight = FreeType.toInt(face.getGlyph().getMetrics().getHeight());
            break;
        }

        // determine cap height
        if (data.capHeight == 1)
            throw new GdxRuntimeException("No cap character found in font");
        data.ascent = data.ascent - data.capHeight;
        data.down = -data.lineHeight;
        if (flip) {
            data.ascent = -data.ascent;
            data.down = -data.down;
        }
    }

    @Override
    public void dispose() {
        setOwnsTexture(true);
        super.dispose();
        data.dispose();
    }

    protected class LazyBitmapFontData extends FreeTypeBitmapFontData {

        private final FreeTypeFontGenerator generator;
        private final int fontSize;
        private final RealBitmapFont font;
        private int page = 1;

        public LazyBitmapFontData(FreeTypeFontGenerator generator, int fontSize, RealBitmapFont lbf) {
            this.generator = generator;
            this.fontSize = fontSize;
            this.font = lbf;
        }

        public Glyph getGlyph(char ch) {
            Glyph glyph = super.getGlyph(ch);
            if (glyph == null)// 找不到时手动绘制
                glyph = generateGlyph(ch);
            return glyph;
        }

        protected Glyph generateGlyph(char ch) {
            GlyphAndBitmap gab = generator.generateGlyphAndBitmap(ch, fontSize, false);
            if (gab == null || gab.bitmap == null)// 未找到文字字符: ch
                return null;

            Pixmap map = gab.bitmap.getPixmap(Format.RGBA8888, getColor(), 1.8f);
            TextureRegion rg = new TextureRegion(new Texture(map));
            map.dispose();

            font.getRegions().add(rg);

            gab.glyph.page = page++;
            super.setGlyph(ch, gab.glyph);
            setGlyphRegion(gab.glyph, rg);
            return gab.glyph;
        }
    }
}

调用

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import org.junit.Test;
import top.lingkang.yzcy.common.font.RealBitmapFont;

/**
 * @author lingkang
 * created by 2023/11/1
 */
public class TestTxt extends ApplicationAdapter {
    @Test
    public void test() {
        Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
        config.setForegroundFPS(30);
        config.setTitle("yzcy");
        config.setWindowedMode(800, 600);
        new Lwjgl3Application(this, config);
    }

    protected SpriteBatch batch;
    protected BitmapFont systemFont;// 系统字体
    protected BitmapFont bitmapFont2;// 动态绘制字体
    //加载 仿宋字体
    FreeTypeFontGenerator generator;

    @Override
    public void create() {
        batch = new SpriteBatch();
        generator = new FreeTypeFontGenerator(Gdx.files.absolute("C:\\Windows\\Fonts\\simfang.ttf"));

        // 默认字体
        FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
        parameter.size = 18;
        parameter.characters = "abcABC$%&* 欢迎"; // 默认字体需要提前设置好字符
        systemFont = generator.generateFont(parameter);


        // 实时绘制字体
        bitmapFont2 = new RealBitmapFont(generator, 18);
        bitmapFont2.setColor(Color.WHITE);
    }

    @Override
    public void render() {
        batch.begin();
        systemFont.draw(batch, "abc ABC $%&* 欢迎", 0, 300);// 左下角起点,向上300个高度
        bitmapFont2.draw(batch, "abc ABC $%&* 欢迎", 0, 250);
        batch.end();
    }

    @Override
    public void dispose() {
        batch.dispose();
        systemFont.dispose();
        bitmapFont2.dispose();
        generator.dispose();
    }
}

效果

image-1699686593435