加载字体
在绘制文本之前,您需要有可用的字体,就像任何其他打印文本的程序一样。字体封装在sf::Font类中,提供了三个主要功能:加载字体、从中获取字形(即可视化字符)以及读取其属性。在典型的程序中,您只需要利用第一个功能,即加载字体,让我们先关注这个。
加载字体的最常见方式是从磁盘上的文件中,这是通过loadFromFile函数完成的。
sf::Font font;
if (!font.loadFromFile("arial.ttf"))
{
// error...
}
请注意,SFML不会自动加载您的系统字体,即font.loadFromFile(“Courier New”)不起作用。首先,因为SFML需要文件名,而不是字体名称;其次,因为SFML没有魔法去访问您的系统字体文件夹。如果您想加载字体,您需要将字体文件包含在应用程序中,就像其他所有资源(图像、声音等)一样。
loadFromFile函数有时会在没有明显原因的情况下失败。首先,请检查SFML打印到标准输出的错误消息(请检查控制台)。如果消息是"unable to open file",请确保工作目录(即任何文件路径都相对解释的目录)是您认为的那个:当您从桌面环境运行应用程序时,工作目录是可执行文件夹。(但是,当您从IDE(Visual Studio、Code::Blocks等)启动程序时,工作目录有时可能设置为项目目录。在项目设置中通常可以很容易地更改此设置。)
您还可以从内存(loadFromMemory)或自定义输入流(loadFromStream)加载字体文件。
SFML支持大多数常见的字体格式。完整列表可在API文档中查看。
这就是您需要做的全部。一旦字体加载完毕,您就可以开始绘制文本。
绘制文本
要绘制文本,您将使用sf:: text类。使用起来非常简单:
sf::Text text;
// select the font
text.setFont(font); // font is a sf::Font
// set the string to display
text.setString("Hello world");
// set the character size
text.setCharacterSize(24); // in pixels, not points!
// set the color
text.setFillColor(sf::Color::Red);
// set the text style
text.setStyle(sf::Text::Bold | sf::Text::Underlined);
...
// inside the main loop, between window.clear() and window.display()
window.draw(text);
文本也可以进行变换:它们具有位置、方向和缩放。涉及到的函数与sf::Sprite类和其他SFML实体相同。这些函数在“变换实体”教程中进行了说明。
如何避免非ascii字符的问题?
处理非ASCII字符(例如带重音符号的欧洲字符、阿拉伯字符或中文字符)可能比较棘手。这需要对解释和绘制文本涉及的各种编码有很好的理解。为了避免处理这些编码,有一个简单的解决方案:使用宽字面字符串。
text.setString(L"יטאח");
只需在字符串前添加“L”前缀就可让它工作,这个前缀告诉编译器生成一个宽字符串。在C++中,宽字符串是一种奇怪的类型:标准并没有说明它们的大小(16位?32位?),也没有说明它们使用的编码(UTF-16?UTF-32?)。不过,我们知道,在大多数平台上,它们都将生成Unicode字符串,并且SFML知道如何正确处理它们。
需要注意的是,C++11标准支持使用新的字符类型和前缀来构建UTF-8、UTF-16和UTF-32字符串字面量,但SFML尚未支持它们。
显而易见的是,您还必须确保所使用的字体包含您要绘制的字符。事实上,字体并不包含所有可能的字符的字形(Unicode标准有超过100,000个字符!),例如,一个阿拉伯字体将无法显示日语文本。
创建自己的文本类
如果 sf::Text 太受限,或者您想对预渲染的字形做其他事情,那么 sf::Font 提供了您需要的一切。
您可以检索一个特定大小的纹理,其中包含所有预渲染的字形:
const sf::Texture& texture = font.getTexture(characterSize);
需要注意的是,当请求字形时,它们才会被添加到纹理中。有这么多字符(记住,超过100000),当你加载字体时,它们不能全部生成。相反,当您调用 getGlyph 函数时,它们会在运行时动态渲染。
如果要对字体纹理进行有意义的操作,则必须获取其中包含的字形的纹理坐标:
sf::Glyph glyph = font.getGlyph(character, characterSize, bold);
其中,character 是要获取字形的字符的 UTF-32 代码。还必须指定字符大小,以及要使用粗体或常规版本的字形。
sf::Glyph 结构包含三个成员:
- textureRect 包含字形在纹理中的纹理坐标
- bounds 包含字形的边界矩形,有助于将其相对于文本的基线定位
- advance 是要应用的水平偏移量,以获取文本中下一个字形的起始位置
你还可以获取字体的其他一些指标,比如两个字符之间的紧缩度或者行间距(总是针对某个字符大小):
int lineSpacing = font.getLineSpacing(characterSize);
int kerning = font.getKerning(character1, character2, characterSize);