Libgdx游戏开发系列教程(4)——显示中文文字

news2025/4/8 10:49:29

目录

2种方法优缺点

BitmapFont

FreeTypeFont 

方法1 使用BitmapFont 

1.下载hiero工具

 2.生成fnt文件

3.代码使用 

测试效果 

方法2 使用FreeType 

1.依赖引入

2.代码使用 

测试效果

使用疑问点


这里主要介绍关于在Libgdx显示文字的2种方法

本文代码示例采用kotlin代码进行讲解,且需要有libgdx入门基础

2种方法优缺点

BitmapFont

优势:

  1. 易于操作和使用,简单快速实现文本渲染。
  2. 资源消耗相对较低,速度较快。
  3. 支持利用工具生成位图字体,可以实现自定义字体样式。

缺点:

  1. 缩放时可能导致文字变得模糊。
  2. 不支持平滑缩放,容易出现锯齿效果。
  3. 需要手动创建位图字体文件,可能需要额外的工作量。
FreeTypeFont 

优势:

  1. 支持平滑缩放和旋转,渲染效果更加平滑、清晰。
  2. 支持各种字体格式,更加灵活。
  3. 可以在运行时动态加载字体文件。

缺点:

  1. 资源消耗相对较高,速度较BitmapFont略慢。
  2. 对于大型字体或文本,内存占用可能较高。
  3. 需要额外的库(FreeType)支持,增加了依赖

方法1 使用BitmapFont 

1.下载hiero工具

在Hiero - libGDX页面中下载hiero工具,如下图

下载下来的是一个jar文件,通过java -jar命令打开即可(我自己用的是JDK8,看软件页面应该是使用的swing相关组件,高版本java环境应该也是能够打开)

软件界面下图所示

 2.生成fnt文件

我们需要使用hiero工具生成一个fnt文件(从翻译上来说是一个字体的位图文件,即每个字符都是一个位图)

左侧我们可以选择系统ttf字体或者从文件中选择一个ttf文件来生成(以及进行一些字体大小,加粗或斜体设置)

可以看到渲染方式有三种,具体官方解释如下:

  • FreeType: 通常质量最高。它充分利用了提示,这意味着小字体可以很好地呈现。该gamma设置控制抗锯齿程度。该mono设置禁用所有字体平滑。不支持其他效果,但可以使用填充和通过 Photoshop 或其他工具应用的效果来渲染字形。Hiero 使用 gdx-freetype,因此生成的位图字体将与 gdx-freetype 即时渲染的字体完全匹配。
  • Java: 此字体渲染为字形提供了矢量轮廓,允许应用各种效果,例如阴影、轮廓等。小尺寸的输出通常很模糊,但大尺寸的输出质量很好。
  • Natvie: 操作系统原生渲染最为简单。它不提供紧密贴合的边界,因此字形会占用更多的图集空间。

在右边的输入框中,输入相关文字,点击file菜单栏即可生成fnt文件,如下图所示

有大佬提及使用过程中会有失真的过程,可以看着调整page width和page height属性,调大些 

3.代码使用 
package com.arthurlumertz.taplixic

import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.ApplicationListener
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.SpriteBatch

class BitmapFontTest : ApplicationAdapter() {
    lateinit var batch: SpriteBatch
    lateinit var font: BitmapFont

    override fun create() {
		//读取fnt和png文件生成BitmapFont对象
        font = BitmapFont(Gdx.files.internal("testfont/myfont.fnt"), Gdx.files.internal("testfont/myfont.png"), false)
        font!!.setColor(0.5f, 0.4f, 0.6f, 1f) // 设置颜色

        batch = SpriteBatch()
    }
    
    override fun render() {
        Gdx.gl.glClearColor(1f, 1f, 1f, 1f)
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)

		//绘制操作
        batch!!.begin()
		//这里可以采用\n实现换行效果,
        font!!.draw(batch, "我的\n下一行", 200f, 160f)
        batch!!.end()
    }

    override fun dispose() {
		//资源释放
        batch!!.dispose()
        font!!.dispose()
    }
}

需要注意:

  1. 绘制文字需要考虑y坐标
  2. 文字必须是你之前在工具里输入里的文字,如果不是的话,显示就会出现口这种特殊字符
  3. 使用\n可以实现换行效果

比如你设置要在(0,0)处绘制文字,文字底部就会被遮挡了,如下图效果

测试效果 

启动游戏的代码,不用多解释了

package com.arthurlumertz.taplixic;

import com.badlogic.gdx.backends.lwjgl3.*;

public class DesktopLauncher {

	public static void main (String[] arg) {
		Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
		config.setWindowedMode(960, 540);
		config.setForegroundFPS(60);
		new Lwjgl3Application(new BitmapFontTest(), config);
	}

}

上图显示口这种字符就是我的生成fnt文件里没有定义的文字 

方法2 使用FreeType 

此方法最终也是使用上述的BitmapFont对象来进行绘制

只是使用FreeTypeFontGenerator类将ttf生成了BitmapFont对象,之后的使用方法与上述绘制方法一致

我这里是去系统里随便找了个ttf字体文件来使用

1.依赖引入

在主工程中加入依赖:

api "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"

对应在desktop平台的build.gradle加入依赖

api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"

如果是Android平台,则加入下面依赖:

api "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
        natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a"
        natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a"
        natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86"
        natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64"

如果你之前创建libgdx项目的时候勾选了FreeType,就会自动生成对应的依赖了

2.代码使用 
package com.arthurlumertz.taplixic

import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.ApplicationListener
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter

class FreetypeFontTest : ApplicationAdapter() {
    lateinit var batch: SpriteBatch

    // 系统字体
    lateinit var systemFont: BitmapFont

    lateinit var generator: FreeTypeFontGenerator

    override fun create() {
        batch = SpriteBatch()
		//自己找的一个ttf文件
        generator = FreeTypeFontGenerator(Gdx.files.internal("testfont/cnfont.ttf"))

        // 字体参数设置
        val parameter = FreeTypeFontParameter()
        parameter.size = 18

		//需要使用的文字,里面不能有相同
        //parameter.characters = FreeTypeFontGenerator.DEFAULT_CHARS + "你好我的" // 默认字体需要提前设置好字符
		
		//chinese.txt文件内容里是我上面提到的三千个汉字文本
        parameter.characters = FreeTypeFontGenerator.DEFAULT_CHARS + Gdx.files.internal("testfont/chinese.txt").readString()

		//生成BitmapFont对象
        systemFont = generator!!.generateFont(parameter)
    }


    override fun render() {
        batch!!.begin()

        systemFont!!.draw(batch, "你好的我的,这是一个数据哈哈\n下一行数据", 0f, 300f)

        batch!!.end()
    }

    override fun dispose() {
        batch!!.dispose()
        systemFont!!.dispose()
        generator!!.dispose()
    }
}

注意点:

parameter.characters设置的字符不能有重复,因为代码逻辑是要根据字符作为key找对应的index(个人的简单理解)

比如说你要展示文本: '今天天气真好',你的parameter.characters应该设置为今天气真好

上面例子我是直接用了三千的文字进行生成,但这样做法会消耗很大内存,官方不太推荐

看官方说用到什么文字就创建什么文字,但这样来不是会麻烦?还得处理下将重复字给过滤掉?这里我暂且持保留意见吧

补充FreeTypeFontParameter可设置属性:

/** The size in pixels */
public int size = 16;
/** Foreground color (required for non-black borders) */
public Color color = Color.WHITE;
/** Border width in pixels, 0 to disable */
public float borderWidth = 0;
/** Border color; only used if borderWidth > 0 */
public Color borderColor = Color.BLACK;
/** true for straight (mitered), false for rounded borders */
public boolean borderStraight = false;
/** Offset of text shadow on X axis in pixels, 0 to disable */
public int shadowOffsetX = 0;
/** Offset of text shadow on Y axis in pixels, 0 to disable */
public int shadowOffsetY = 0;
/** Shadow color; only used if shadowOffset > 0 */
public Color shadowColor = new Color(0, 0, 0, 0.75f);
/** The characters the font should contain */
public String characters = DEFAULT_CHARS;
/** Whether the font should include kerning */
public boolean kerning = true;
/** The optional PixmapPacker to use */
public PixmapPacker packer = null;
/** Whether to flip the font vertically */
public boolean flip = false;
/** Whether or not to generate mip maps for the resulting texture */
public boolean genMipMaps = false;
/** Minification filter */
public TextureFilter minFilter = TextureFilter.Nearest;
/** Magnification filter */
public TextureFilter magFilter = TextureFilter.Nearest;

测试效果

启动游戏的代码,应该不用多解释了

package com.arthurlumertz.taplixic;

import com.badlogic.gdx.backends.lwjgl3.*;

public class DesktopLauncher {

	public static void main (String[] arg) {
		Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
		config.setWindowedMode(960, 540);
		config.setForegroundFPS(60);
		new Lwjgl3Application(new FreetypeFontTest(), config);
	}
}

使用疑问点

下面是一下疑问点的记录,还没有具体研究得到答案,先记录着吧,或者有路过的大佬可以在评论区解答下?

  1. FreeType每次都得生成对应字符,不能直接使用单例,一次性生成要使用的字符,之后直接使用吗?
  2. 关于语言国际化的要求应该如何实现?感觉有些偏题,应该是可以使用类似的一个资源文件来自行管理,由用户选择对应的语言就加载文本,参考杀戮尖塔的语言更改,应该是需要重启游戏才能解决,不能动态实现
  3. BitmapFont会有失真的缺点,应该如何解决优化,有无优化方法?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2310079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自然语言处理:朴素贝叶斯

介绍 大家好,博主又来和大家分享自然语言处理领域的知识了。按照博主的分享规划,本次分享的核心主题本应是自然语言处理中的文本分类。然而,在对分享内容进行细致梳理时,我察觉到其中包含几个至关重要的知识点,即朴素…

UltraScale系列FPGA实现SDI转PCIE3.0采集卡,基于UltraScale GTH+XDMA架构,提供工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目本博已有的 SDI 编解码方案我已有的PCIE方案本博客方案的PCIE2.0版本本博客方案的RIFFA版本 3、详细设计方案设计原理框图SDI 输入设备LMH1219RTWR 均衡器UltraScale …

Linux系列:如何调试 malloc 的底层源码

一:背景 1. 讲故事 上一篇我们聊过 C# 调用 C 的 malloc 代码来演示heap的内存泄露问题,但要想深入研究得必须把 malloc 的实现库 libc.so 给调试起来,大家都知道在Linux 上 libc 和 Windows 的 Win32 API 是一个层级的,由于 Li…

深入 PipeWire

简介 随着它的成熟,PipeWire 项目正在慢慢地变得流行。它的文档依然相对稀少,但正在逐渐增长。然而,让项目外部的人尝试用他们自己的语言来理解和解释它总是一个好主意,重申想法,从他们自己的角度来看待它。 在之前的…

20250304笔记-阅读论文

文章目录 前言一、寻找论文1.1寻找有代码的论文方法一:浏览器扩展1.1.1使用流程 方法二:使用Papers with Code 1.2大量搜索代码 二、阅读论文所用软件 三、引用文献格式总结 前言 一、寻找论文 1.1寻找有代码的论文 方法一:浏览器扩展 浏览…

线程POSIX信号量/基于环形队列的⽣产消费模型

一,POSIX线程信号量 信号量的本质就是一个计数器,也是对资源的预定机制,POSIX信号量和SystemV信号量作⽤相同,都是⽤于同步操作,达到⽆冲突的访问共享资源⽬的。但 POSIX可以⽤于线程间同步。 1,初始化信…

C# 类库打包dll文件

目录 前言操作流程注意事项 前言 在C#中,有多种方式可以对代码进行加密,以保护源代码不被轻易查看或修改,这篇文章主要介绍将C# cs类文件加密为dll文件的方式进行保护。 操作流程 在 Visual Studio 中,选择“创建新项目”。 选…

DELL EMC Unity存储如何让控制器进入service mode和退出service mode

近期遇到好几个关于DELL EMC unity (VNXe)存储系统挂掉的案例,都是很后期才寻找支持到我们这里,然后再看问题,已经变得很复杂,几乎都是从一个相对简单的问题搞成了一锅粥甚至最后丢数据的情况。 为此&…

【微知】如何通过mlxlink查看Mellanox网卡和光模块相关的信息?( mlxlink -d 01:00.0 -m)

背景 通过mlxlink可以查看Mellanox网卡的一些链路信息和硬件信息,也可以查看所插入的光模块的一些信息。 兄弟篇通过ethtool查看的方法:如何查看Mellanox网卡上的光模块的信息? 命令 mlxlink -d 01:00.0 -mman手册介绍: 如果…

Linux系列:如何用 C#调用 C方法造成内存泄露

一个简单的非托管内存泄露 1. 构建 so 文件 在 Windows 平台上我们会通过 MSVC 编译器将 C代码编译出一个成品 .dll,在 Linux 上通常会借助 gcc 将 c 编译成 .so 文件,这个.so 全称 Shared Object,为了方便讲解,先上一段简单的代码…

C# 数据类型相关

分类 按照数据复杂程度 按照数据存储 类型转换 隐式转换 隐式转换无法完成由精度高的数据类型向精度低的数据类型转换 显式转换 又称为强制类型转换,显示转换不一定总是成功,且转换过程中可能出现数据丢失 int num 666;float result (float)num; …

使用Word时无法粘贴,弹出错误提示:运行时错误‘53‘:文件未找到:MathPage.WLL

报错说明 使用Word时无法粘贴,粘贴时弹出提示如下: 一般出现这种情况时,我想你是刚装完MathType不久,博主装的是MathType7版本,出现了这个问题。 出现这个问题的原因是"mathpage.wll"这个文件在Office的插…

玩转python: 深度解析Python高阶函数及推导式

1 高阶函数:工程化编程的基石 1.1 高阶函数基础概念 高阶函数(Higher-Order Function)是函数式编程范式的核心要素,指能够接受函数作为参数或返回函数作为结果的函数。在Python中,这类函数构成了数据处理的基础架构&…

Linux第五讲----gcc与g++,makefile/make

1.代码编译 1.1预处理 我们通过vim编辑完文件之后,想看一下运行结果这时我们便可以试用gcc编译C语言,g编译c. 编译代码: 上述两种方法均可,code.c是我的c语言文件,mycode是我给编译后产生的二进制文件起的名&#x…

ubuntu22.04下Meshlab打开obj文件闪退——使用Appimage并放入收藏夹中

文章目录 ubuntu22.04下Meshlab打开obj文件闪退,查了下是meshlab的apt没做好。 官网下载:https://www.meshlab.net/#download 赋予权限 sudo chmod a+x MeshLab2023.12-linux.AppImage 双击运行即可 打开权限——下面操作是放在桌面上的 创建桌面快捷方式 # 在 ~/desktop (…

MAVEN的环境配置

在下载好maven后或解压maven安装包后进行环境配置 1.在用户环境变量中 新建一个MAVEN_HOME 地址为MAVEN目录 注:地址为解压后maven文件的根目录!!! 2.在系统环境变量的path中添加该变量 %MAVEN_HOME%\bin 3. 测试maven安装是否成…

强化学习无痛上手笔记第1课

文章目录 Markov Decision ProcessDefinitionRelated Concepts Policy for MDP AgentDefinitionJudgement for PolicyValue FunctionsTD formula for value functionsRelation of V and QPolicy CriterionPolicy Improvement TheoremOptimal PolicyReinforcement Learning Fund…

智能设备上的 AI 移植与部署:新趋势与实践案例

1. 引言:智能设备如何运行 AI? 随着人工智能(AI)技术的快速发展,AI 计算已经从云端走向边缘,嵌入到智能设备中,如智能手机、智能摄像头、机器人、自动驾驶汽车等。这种本地化 AI 计算能够减少延…

【USRP】NVIDIA Sionna:用于 6G 物理层研究的开源库

目录 Sionna:用于 6G 物理层研究的开源库主要特点实现6G研究的民主化支持 5G、6G 等模块化、可扩展、可伸缩快速启动您的研究 好处原生人工智能支持综合研究平台开放生态系统 安装笔记使用 pip 安装基于Docker的安装从源代码安装“你好世界!”探索锡奥纳…

LLM大型语言模型(一)

1. 什么是 LLM? LLM(大型语言模型)是一种神经网络,专门用于理解、生成并对人类文本作出响应。这些模型是深度神经网络,通常训练于海量文本数据上,有时甚至覆盖了整个互联网的公开文本。 LLM 中的 “大” …