SFML2.6 窗口模块--打开和管理SFML窗口

介绍

这个教程仅解释如何打开和管理窗口。绘制东西超出了SFML-Window模块的范围:它由SFML-Graphics模块处理。但是,窗口管理仍然完全相同,因此阅读此教程在任何情况下都非常重要。

窗口的创建

SFML中的窗口由sf::Window类定义。可以在构建时直接创建和打开窗口:

#include <SFML/Window.hpp>

int main()
{
    sf::Window window(sf::VideoMode(800, 600), "My window");

    ...

    return 0;
}

第一个参数,即视频模式,定义了窗口的大小(内部大小,不包括标题栏和边框)。在这里,我们创建一个大小为800x600像素的窗口。 SFML的sf :: VideoMode类有一些有趣的静态函数,可以获取桌面分辨率或全屏模式下的有效视频模式列表。不要犹豫,看一下它的文档。

第二个参数是窗口的标题。

这个构造函数接受一个可选的第三个参数:样式,允许你选择你想要的装饰和功能。你可以使用以下样式的任何组合:

sf::Style::None没有任何装饰(例如,对splash窗口有用);此样式不能与其他样式组合
sf::Style::Titlebar窗口有一个标题栏
sf::Style::Resize窗口可以调整大小并具有最大化按钮
sf::Style::Close窗口具有关闭按钮
sf::Style::Fullscreen窗口以全屏模式显示;此样式不能与其他样式组合,需要有效的视频模式
sf::Style::Default默认样式,等同于 Titlebar | Resize|Close

还有第四个可选参数,用于定义OpenGL特定的选项,这在专门讲解OpenGL的教程中有详细介绍。

如果你想在创建sf::Window实例之后创建窗口,或者使用不同的视频模式或标题重新创建窗口,可以使用create函数。它与构造函数接受完全相同的参数。

#include <SFML/Window.hpp>

int main()
{
    sf::Window window;
    window.create(sf::VideoMode(800, 600), "My window");

    ...

    return 0;
}

让窗口焕发生机

如果你尝试在“…”处放置空代码来执行上述代码,你几乎不会看到任何东西。首先,程序会立即结束。其次,因为没有事件处理–所以即使你在这段代码中添加了一个无尽的循环,你也会看到一个无法移动、调整大小或关闭的死窗口。

让我们添加一些代码,使这个程序更有趣一些:

#include <SFML/Window.hpp>

int main()
{
    sf::Window window(sf::VideoMode(800, 600), "My window");

    // run the program as long as the window is open
    while (window.isOpen())
    {
        // check all the window's events that were triggered since the last iteration of the loop
        sf::Event event;
        while (window.pollEvent(event))
        {
            // "close requested" event: we close the window
            if (event.type == sf::Event::Closed)
                window.close();
        }
    }

    return 0;
}

上面的代码将打开一个窗口,并在用户关闭它时结束。让我们详细看看它是如何工作的。

首先,我们添加了一个循环,确保应用程序在窗口关闭之前会被刷新/更新。大多数(如果不是全部)SFML程序都会有这种循环,有时被称为主循环或游戏循环。

然后,在我们的游戏循环内,我们想要做的第一件事就是检查是否有任何事件发生。请注意,我们使用while循环,以便在有多个事件等待处理的情况下,处理所有等待的事件。如果有待处理的事件,pollEvent函数将返回true,否则返回false。

每当我们收到一个事件,我们必须检查它的类型(窗口关闭?按键按下?鼠标移动?手柄连接?…),如果我们对它有兴趣就作出相应的反应。在这种情况下,我们只关心Event::Closed事件,当用户想要关闭窗口时会触发该事件。此时,窗口仍然是打开状态,我们必须使用close函数显式地关闭它。这使你能够在窗口关闭之前做一些事情,比如保存应用程序的当前状态,或显示一条消息。

人们经常犯的一个错误是忘记事件循环,只是因为他们暂时不关心处理事件(他们使用实时输入代替)。如果没有事件循环,窗口将变得无响应。需要注意的是,事件循环有两个作用:除了向用户提供事件外,还给窗口处理其内部事件的机会,这是必要的,以便它可以对用户的移动或调整大小的操作做出反应。

窗口关闭后,主循环退出,程序终止。

这时,您可能已经注意到,我们还没有讨论如何向窗口绘制东西。如介绍中所述,这不是 sfml-window 模块的工作,如果您想绘制像精灵、文本或形状等东西,您需要转到 sfml-graphics 教程。

要绘制内容,您也可以直接使用 OpenGL,完全忽略 sfml-graphics 模块。sf::Window 内部创建了一个 OpenGL 上下文,并准备好接受您的 OpenGL 调用。您可以在相应的教程中了解更多信息。

不要期望在这个窗口中看到有趣的东西:您可能会看到统一的颜色(黑色或白色),或者是先前使用 OpenGL 的上一个应用程序的最后内容,或…其他东西。

窗口操作

当然,SFML 允许您对窗口进行一些操作。支持改变大小、位置、标题或图标等基本窗口操作,但与专用的 GUI 库(如 Qt、wxWidgets)不同,SFML 不提供高级功能。SFML 窗口仅用于提供 OpenGL 或 SFML 绘图的环境。

// change the position of the window (relatively to the desktop)
window.setPosition(sf::Vector2i(10, 50));

// change the size of the window
window.setSize(sf::Vector2u(640, 480));

// change the title of the window
window.setTitle("SFML window");

// get the size of the window
sf::Vector2u size = window.getSize();
unsigned int width = size.x;
unsigned int height = size.y;

// check whether the window has the focus
bool focus = window.hasFocus();

...

如果您确实需要窗口的高级功能,可以使用另一个库创建一个窗口(甚至创建一个完整的 GUI),并将 SFML 嵌入其中。为此,可以使用另一个构造函数或创建函数,它们接受现有窗口的操作系统特定的句柄。在这种情况下,SFML 将在给定窗口内创建绘图环境,并捕获所有其事件,而不会干扰父窗口管理。

sf::WindowHandle handle = /* specific to what you're doing and the library you're using */;
sf::Window window(handle);

如果您只想要额外的非常特定的功能,也可以反过来做:创建一个 SFML 窗口并获取其操作系统特定的句柄,以实现 SFML 本身不支持的功能。

sf::Window window(sf::VideoMode(800, 600), "SFML window");
sf::WindowHandle handle = window.getSystemHandle();

// you can now use the handle with OS specific functions

将 SFML 与其他库集成需要一些工作,并且不会在这里进行描述,但是您可以参考专门的教程、示例或论坛帖子。

帧率控制

有时,当您的应用程序运行速度很快时,您可能会注意到视觉伪影,例如撕裂。原因是您的应用程序的刷新速率与显示器的垂直频率未同步,因此前一帧的底部与下一帧的顶部混合在一起。 解决此问题的方法是激活垂直同步。它由显卡自动处理,并且可以使用 setVerticalSyncEnabled 函数轻松地开关:

window.setVerticalSyncEnabled(true); // call it once, after creating the window

在调用此函数后,您的应用程序将以与显示器刷新率相同的频率运行。

有时,调用setVerticalSyncEnabled函数会无效:这很可能是由于您的图形驱动程序设置中强制关闭了垂直同步。应将其设置为“由应用程序控制”。

在其他情况下,您可能希望应用程序以给定的帧速率运行,而不是与显示器的刷新率相同。这可以通过调用setFramerateLimit函数来实现:

window.setFramerateLimit(60); // call it once, after creating the window

与setVerticalSyncEnabled不同,这个功能是由SFML本身实现的,使用sf::Clock和sf::sleep的组合。一个重要的结果是它不是100%可靠的,特别是对于高帧速率:sf::sleep的精度取决于底层操作系统和硬件,并且可能高达10或15毫秒。不要依赖此功能来实现精确的时间控制。

永远不要同时使用setVerticalSyncEnabled和setFramerateLimit!它们会混淆并使情况更糟。

关于窗口需要知道的事情

下面是一个简短的列表,列出了使用SFML windows可以做什么和不能做什么。

你可以创建多个窗口

SFML允许您创建多个窗口,并且可以在主线程中处理所有窗口,或者在自己的线程中处理每个窗口(但是…请参见下文)。在这种情况下,不要忘记每个窗口都有一个事件循环。

尚未正确支持多个监视器

SFML没有显式管理多个监视器。因此,您将无法选择窗口出现在哪个监视器上,也无法创建多个全屏窗口。这应该在未来的版本中得到改进。

必须在窗口所在的线程中轮询事件

这是大多数操作系统的一个重要限制:事件循环(更准确地说是pollEvent或waitEvent函数)必须在创建窗口的同一线程中调用。这意味着,如果您想为事件处理创建一个专用线程,您必须确保窗口也在此线程中创建。如果您真的想在不同的线程之间分配任务,最好保持事件处理在主线程中,并将其余部分(渲染、物理、逻辑等)移动到单独的线程中。此配置也将兼容下面描述的另一个限制。

在macOS上,必须在主线程中管理windows和事件

没错,macOS不会允许您尝试在主线程以外的线程中创建窗口或处理事件

在Windows上,大于桌面的窗口将无法正常运行

由于某些原因,Windows系统不喜欢比桌面更大的窗口。这包括使用VideoMode::getDesktopMode()创建的窗口:在添加窗口装饰(边框和标题栏)后,你将得到一个略大于桌面的窗口。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面我会给你讲解如何使用SFML来实现n-puzzle游戏的可视化。首先,我们需要了解一下SFML是什么。 SFML是一个跨平台的图形库,它提供了简单易用的API,可以帮助我们快速创建2D游戏和图形应用程序。它支持多种编程语言,包括C++、Python、Java等。 接下来,我们可以开始实现n-puzzle游戏的可视化了。首先,我们需要创建一个窗口来显示游戏界面。以下是创建窗口的代码: ```cpp #include <SFML/Graphics.hpp> int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "N-Puzzle Game"); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(sf::Color::White); // 绘制游戏界面 window.display(); } return 0; } ``` 这段代码创建了一个800x600大小的窗口,并且在每一帧中都会清空窗口并绘制游戏界面。接下来,我们需要实现游戏界面的绘制。 n-puzzle游戏的界面由一个N*N的网格组成,每个格子中都会有一个数字。我们可以使用SFML中的矩形来表示每个格子,并且使用文本来显示数字。以下是绘制游戏界面的代码: ```cpp const int N = 3; const int tileSize = 100; sf::RectangleShape tile(sf::Vector2f(tileSize, tileSize)); tile.setFillColor(sf::Color::White); tile.setOutlineColor(sf::Color::Black); tile.setOutlineThickness(2); sf::Font font; font.loadFromFile("arial.ttf"); sf::Text text("", font, tileSize / 2); text.setFillColor(sf::Color::Black); text.setStyle(sf::Text::Bold); text.setOrigin(text.getGlobalBounds().width / 2, text.getGlobalBounds().height / 2); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { int number = i * N + j + 1; if (number == N * N) continue; tile.setPosition(j * tileSize, i * tileSize); text.setString(std::to_string(number)); text.setPosition(j * tileSize + tileSize / 2, i * tileSize + tileSize / 2); window.draw(tile); window.draw(text); } } ``` 这段代码首先定义了每个格子的大小为100x100,并且使用白色填充和黑色边框。然后,它加载了一个字体文件,并且创建了一个文本对象来显示数字。最后,它使用两个嵌套循环来绘制整个游戏界面。 这样,我们就完成了n-puzzle游戏的可视化。完整代码如下: ```cpp #include <SFML/Graphics.hpp> const int N = 3; const int tileSize = 100; int main() { sf::RenderWindow window(sf::VideoMode(N * tileSize, N * tileSize), "N-Puzzle Game"); sf::RectangleShape tile(sf::Vector2f(tileSize, tileSize)); tile.setFillColor(sf::Color::White); tile.setOutlineColor(sf::Color::Black); tile.setOutlineThickness(2); sf::Font font; font.loadFromFile("arial.ttf"); sf::Text text("", font, tileSize / 2); text.setFillColor(sf::Color::Black); text.setStyle(sf::Text::Bold); text.setOrigin(text.getGlobalBounds().width / 2, text.getGlobalBounds().height / 2); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(sf::Color::White); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { int number = i * N + j + 1; if (number == N * N) continue; tile.setPosition(j * tileSize, i * tileSize); text.setString(std::to_string(number)); text.setPosition(j * tileSize + tileSize / 2, i * tileSize + tileSize / 2); window.draw(tile); window.draw(text); } } window.display(); } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值