小游戏和GUI编程(3) | 基于 SFML 的字符阵
1. 简介
使用 EasyX 图形库时, 官方第一个例子是字符阵。 EasyX 不开源, 也不能跨平台, API 陈旧, API 是 C 而不是 C++。 现在使用 SFML 来实现字符阵, 克服 EasyX 的这些问题。
SFML 的 API 不如 EasyX 那么简单, 稍微复杂是因为功能更强大。 主要关注这么几个功能点: 使用 SFML 时怎样渲染文字? 怎样更新屏幕来营造字符阵的效果?
SFML 版本为 2.6.1, 原始的 EasyX 代码在这里 char-matrix, 对应的 raylib 代码在这里.
2. SFML 绘制文字
2.1 加载字体
需要先加载字体, SFML 不会扫描系统字体, 传入的是字体文件的路径。
使用 sf::Font
类, 主要用它的 loadFromFile()
函数。
sf::Font font;const std::string asset_dir = "../Resources";if (!font.loadFromFile(asset_dir + "/SourceHanSansCN-Regular.otf")){printf("Error: font not found\n");return 1;}
2.2 绘制文字
使用 sf::Text
类, 它继承自 Drawable 类和 Transformable 类, 因此可以使用
class SFML_GRAPHICS_API Text : public Drawable, public Transformable
{
public:void setFont(const Font& font); // 设置字体void setString(const String& string); // 设置文本内容void setCharacterSize(unsigned int size); // 设置字符大小void setFillColor(const Color& color); // 设置字体颜色...
};class SFML_GRAPHICS_API Transformable
{
public:void setPosition(float x, float y); // 设置位置...
};
根据上述 api, 能够创建 “Hello World” 的文本, 设置它为绿色, 在屏幕中央显示:
关键代码
window.clear();// draw the matrix heresf::Text text;text.setFont(font);text.setString("Hello, World");text.setCharacterSize(42); // in pixelstext.setFillColor(sf::Color::Green);sf::FloatRect bbox = text.getGlobalBounds();text.setPosition(win_width / 2 - bbox.width/2, win_height / 2 - bbox.height/2);window.draw(text);window.display();
3. 字符阵列
这一小节, 分析字符阵列的原理, 然后在前一节的基础绘制代码基础上进行实现。
3.1 在随机位置显示三个随机字母
int x = (rand() % 80) * 8; // [0, 640] 范围内的随机数, 间距是8
int y = (rand() % 20) * 24; // [0, 480] 范围内的随机数, 间距是24
int c = (rand() % 26) + 'a'; // [97, 122] 范围内的随机数, 也就是随机小写字母
sf::Text text;
text.setFont(font);
text.setString(std::string(1, c));
text.setCharacterSize(26); // in pixels
text.setFillColor(sf::Color::Green);
text.setPosition(x, y);
window.draw(text);
3.2 擦除一个像素行
通过绘制一个和背景颜色一样(黑色)的矩形来做到。
// 画线, 擦掉一个像素行
sf::RectangleShape line(sf::Vector2f(win_width, 2));
line.setFillColor(sf::Color::Black);
line.setPosition(0, line_index);
line_index = (line_index + 1) % win_height; // line_index 初始值为0
window.draw(line);
其中 RectangleShape 类继承自 Shape 类, 因此能调用 setFillColor()
, setPosition()
等函数:
class SFML_GRAPHICS_API RectangleShape : public Shape
{...
};
3.3 确保擦除效果
和常规不一样的地方是, 需要保持前一帧的绘制内容。
因此需要去掉 window.clear()
的调用。
3.4 完整代码和效果
#include <SFML/Graphics.hpp>int main()
{constexpr int win_width = 640;constexpr int win_height = 480;sf::VideoMode videomode(win_width, win_height);const std::string title = "Char Matrix SFML";sf::RenderWindow window(videomode, title);window.setFramerateLimit(60);sf::Font font;const std::string asset_dir = "../Resources";if (!font.loadFromFile(asset_dir + "/Courier-12.ttf")){printf("Error: font not found\n");return 1;}int line_index = 0;srand((unsigned)time(NULL));while (window.isOpen()){sf::Event event;while (window.pollEvent(event)){if (event.type == sf::Event::Closed) { window.close(); }}// draw the matrix hereif (0){window.clear();sf::Text text;text.setFont(font);text.setString("Hello, World");text.setCharacterSize(42); // in pixelstext.setFillColor(sf::Color::Green);sf::FloatRect bbox = text.getGlobalBounds();text.setPosition(win_width / 2 - bbox.width/2, win_height / 2 - bbox.height/2);window.draw(text);}if (1){ for (int i = 0; i < 3; i++){int x = (rand() % 80) * 8; // [0, 640] 范围内的随机数, 间距是8int y = (rand() % 20) * 24; // [0, 480] 范围内的随机数, 间距是24int c = (rand() % 26) + 'a'; // [97, 122] 范围内的随机数, 也就是随机小写字母sf::Text text;text.setFont(font);text.setString(std::string(1, c));text.setCharacterSize(26); // in pixelstext.setFillColor(sf::Color::Green);text.setPosition(x, y);window.draw(text);}// 画线, 擦掉一个像素行sf::RectangleShape line(sf::Vector2f(win_width, 2));line.setFillColor(sf::Color::Black);line.setPosition(0, line_index);line_index = (line_index + 1) % win_height;window.draw(line);}window.display();}return 0;
}
4. 总结
通过查看 SFML 文档, 把字符阵的代码翻译到了基于 SFML 的实现, 关键 API 如下:
sf::Font::loadFromFile("xxx.ttf")
加载字体sf::Text
类, 用于设置字体sf::RectangleShape
类, 用于绘制单行矩形- 临时移除了
window.clear()
的调用
References
- https://www.sfml-dev.org/tutorials/2.6/graphics-text.php
- https://docs.easyx.cn/zh-cn/char-matrix
- https://www.cnblogs.com/zjutzz/p/17067313.html