创建窗口
创建窗口的只需要一行代码:
#include <SFML/Graphics.hpp>
int main()
{
sf::Window window(sf::VideoMode(200, 200), "HUANGCHENGTAO!"); //创建窗口代码
sf::sleep(sf::seconds(3)); //添加此代码 这样我们就可以创建窗口之后,运行代码可以看到窗口,否则不可以
return 0;
}
● main函数唯一做的是通过调用sf :: Window构造函数初始化窗口变量,之后程序退出。 还有一种方法可以使用默认构造函数打开一个窗口,然后调用Window :: create()。 这个函数的参数与构造函数完全相同,我们刚刚讲过。 如果在已经打开的窗口上调 Window :: create(),它将关闭窗口并使用新的参数集重新初始化它 。
● 请注意 Window 和VideoMode 都在sf命名空间中。 SFML中的每个类都在该命名空间下,该命名空间将SFML中的所有类与其他库中的类分开。
● 我们可以在创建窗口时,窗口的大小、标题、样式和图形设置时指定各种配置。您可能已经注意到我们将两个参数传递 给 Window构造函数 - 一个VideoMode实例和一个字符串(标题)。 构造函数实际上最多可以使用四个参数,最后两个是可选的 - Style和ContextSettings。
VideoMode
● VideoMode类包含显示一个窗口的信息,例如宽度、高度和每个像素的比特数。最后一个参数是表示单个像素颜色的位数。它的默认值为32,如果我们想要创建一个全屏窗口,所提供的值必须由机器的显示器和显卡支持。如果我们为全屏窗口选择无效的参数,窗口创建将会失败的。可以使用 VideoMode::isValid() 方法检 查VideoMode 类的有效性,结果返回一个布尔值。
● 如果我们需要根据PC端桌面版的大小(或像素深度)创建窗口,VideoMode::getDesktopMode()是一个静态方法。另一方面,如果我们要在全屏模式下创建一个窗口,我们可能需要使用 VideoMode::getFullScreenModes() 检查可用的分辨率。这将返回显示模式的std::vector,我们可以自己选择其中一种模式,或者让用户决定哪种模式最适合他们。
然而,仅仅指定全屏视频模式还不足以创建全屏窗口。我们也需要设定一个窗口的样式。
Style
● Style参数是一个位掩码。掩码是标志的组合,其中每个标志代表掩码的一个特定位。在本例中,flags存储在 sf::Style 名称空间中的enum中。我们可以使用flags 的组合来创建所需的标志掩码。以下是SFML提供的风格:
Enum value | 描述 |
sf::Style ::None | 这个窗户没有任何装饰,也不能与任何其他风格相结合 |
sf::Style ::Titlebar | 添加一个标题栏 |
sf::Style ::Resize | 这增加了一个最大化按钮。还允许手动调整窗口的大小 |
sf::Style ::Close | 添加一个关闭按钮 |
sf::Style ::Fullscreen | 这将以全屏模式打开窗口。注意,这不能与任何其他样式组合,需要有效的显示模式。 |
sf::Style ::Default | 这将标题栏、大小调整和关闭组合在一起。这是默认样式。 |
可以通过使用位运算符来组合不同的样式。如果我们想要一个带有标题栏和关闭按钮的窗口,我们可以这样写:
sf::Uint32 style = sf::Style::Titlebar | sf::Style::Close;
这里要做的唯一一件事就是将该样式作为Window构造函数的第三个参数传递。
ContextSettings
● 最后一个参数是ContextSettings的一个实例。 此结构是一组设置的集合,描述了所需的渲染上下文。 SFML使用OpenGL进行渲染,因此这些设置与它直接相关。 可用的上下文设置如下:
depthBits ——这指的是深度缓冲区位数。
stencilBits —— 这指的是模板缓冲区的位数。
antialiasingLevel: —— This refers to the requested number of multisampling levels
majorVersion and minorVersion: —— 这些是指所要求的OpenGL版本
这些设置中的每一个都将在第5章(操作2D摄像机)中得到更详细的解释,您将学习如何使用OpenGL直接渲染物体。
Disabling the mouse cursor (禁用鼠标光标)
● Window类有一个方法,可以在窗口上设置光标的可见性 —— Window :: setMouseCursorVisible()。 对于不使用光标的游戏,或者当我们想要将光标的图像更改为与默认情况不同的内容时,这是非常有用的。
The game loop
下面是一个典型的游戏循环:
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(200, 200), "HUANGCHENGTAO!"); //创建窗口代码
sf::sleep(sf::seconds(3)); //这样我们就可以创建窗口之后,运行代码可以看到窗口,否则不可以
while (window.isOpen()) //游戏循环
{
/*
处理输入——处理来自输入设备和窗口的事件。
更新框架——更新场景中的对象
渲染框架 —— 将场景中的对象渲染到窗口上
*/
}
return 0;
}
典型的游戏循环有三个主要阶段:
处理输入——处理来自输入设备和窗口的事件。
更新框架——更新场景中的对象
渲染框架 —— 将场景中的对象渲染到窗口上
SFML中的输入处理可以通过捕获事件(由窗口分配)或直接查询输入设备的当前状态来完成。这两种方法有不同的用途。例如,我们可能想关闭按钮按下事件上的窗口,或者只要按下某个键,我们的主角就会向右移动。
● 在捕获和使用事件后,我们到达更新框架阶段。在这个阶段,我们想要推进我们的游戏逻辑,更新我们的世界状态
●在我们完成对象更新后,循环的最后阶段就会出现。 在这里,我们清除从上次绘制的所有内容,并再次渲染屏幕上的每个对象。
Event handling (处理事件)
可以通过 bool Window :: pollEvent(sf :: Event&event) 从窗口查询事件。 如果有一个事件等待处理,该函数将返回true,并且事件变量将被事件数据填充。 如果不是,则该函数返回false。 同样重要的是要注意,一次可能有多个事件;因此我们必须确保捕获每个可能的事件。 以下是典型的事件循环:
while (window.isOpen()) //游戏循环
{
sf::Event event;
while (window.pollEvent(event))
{
}
}
请务必注意C ++中的Event类包含一个union。 这意味着其中只有一个有效成员。 访问任何其他成员将导致未定义的行为。 我们可以通过查看事件类型来获取有效成员。
事件类型在逻辑上可以分为四个部分,具体取决于它们与什么有关:
- Window
- Keyboard
- Mouse
- Joystick
Window related events
Enum value | Member associated | Description |
Event::Closed | None | 当操作系统检测到用户想要关闭窗口时触发此事件——关闭按钮、组合键等等。 |
Event::Resized | Event::size holds the new size of the window |
当操作系统检测到窗口已手动调整大小或已使用Window :: setSize()时,将触发此事件。 |
Event::LostFocus
|
None | This event is triggered when the window loses or gains focus. Windows which are out of focus don't receive keyboard events. |
Keyboard related events
Enum value | Member associated | Description |
Event::KeyPressed
|
Event :: key保存 按下/松开 的键 | 当按下或松开焦点窗口上的单个按钮时,将触发此事件。 |
Event::TextEntered | Event :: text保存 UTF-32 unicode 的字符值 | 每次输入字符时都会触发此事件。这将从用户输入中生成可打印的字符,对于文本字段非常有用。 |
Mouse related events
Enum value | Member associated | Description |
Event::MouseMoved |
Event::mouseMove holds the new mouse position | 当鼠标在窗口内更改其位置时会触发此事件。 |
Event::MouseButtonPressed
|
Event::mouseButton holds the pressed / released button and the mouse position | 在窗口内按下鼠标按钮时会触发此事件。 目前支持五个按钮 - 左,右,中,其它按钮1和其它按钮2。 |
Event::MouseWheelMoved | Event::mouseWheel 保存了鼠标的滚轮移动了多少时间以及鼠标位置 | 当滚动轮在窗口内移动时触发此事件 |
joystick related events
Enum value | Member associated | Description |
Event::JoystickConnected
|
Event :: joystickConnect 保存刚才连接 / 断开的操纵杆的ID | 当操纵杆连接或断开时触发此事件。 |
Event::JoystickButtonPressed
|
Event :: joystickButton 保存按下的按钮次数和操纵杆ID | 按下操纵杆上的按钮时会触发此操作。 SFML最多支持8个操纵杆,每个操纵杆最多32个按钮。 |
Event::JoystickMoved | Event :: joystickMove保存移动的坐轴,新的坐标轴位置和操纵杆ID | 当操纵杆的轴线移动时触发。可以通过Window::setJoystick threshold()设置移动阈值。SFML最多支持8个轴 |
Using events
通过调用Window :: pollEvent()获取事件后,我们可以通过查看Event :: type来检查它的类型。 该事件的类型为Event :: EventType,它是Event类中的枚举。 以下是典型的关闭事件的实现方式:
while (window.isOpen()) //游戏循环
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
}
这里,Window :: close()函数将负责关闭窗口。 如果窗口变量超出范围,则调用析构函数,并且关闭窗口。
如果我们想处理多个事件,那么使用Switch语句是有意义的,因为它提高了可读性。让我们看看键盘键是如何按下和释放的:
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(200, 200), "HUANGCHENGTAO!"); //创建窗口代码
sf::sleep(sf::seconds(3)); //这样我们就可以创建窗口之后,运行代码可以看到窗口,否则不可以
sf::CircleShape shape(100.f); //创建图形对象
shape.setFillColor(sf::Color::Red); //改shape的填充颜色
while (window.isOpen()) //游戏循环
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::EventType::Closed:
window.close();
break;
case sf::Event::EventType::KeyPressed:
//如果按下空格键,请更改标题
if (event.key.code == sf::Keyboard::Key::Space) //键盘按下事件
{
window.setTitle("Space pressed");
}
break;
case sf::Event::EventType::KeyReleased: //键盘松开事件
//如果空格键又被松开,则改变标题
if (event.key.code == sf::Keyboard::Key::Space)
{
window.setTitle("Space released");
}//如果松开了Escape键,则关闭窗口
else if (event.key.code == sf::Keyboard::Key::Escape)
{
window.close();
}
break;
default:
break;
}
}
window.clear();
window.draw(shape);
window.display();
}
return 0;
}
上面的代码演示了如何捕获事件,以便在每次按下和松开空格键时更改窗口的标题。除此之外,当Escape键被松开时,窗口就会关闭。
Event :: key包含一个名为code的成员,code是Keyboard :: Key类型的枚举。
但是,Event :: EventType :: TextEntered 更好。 可以以相对简单的方式检测和处理单键被按下 和 松开。
但是,当涉及到特定的字符时,事情开始变得有点复杂。例如,如果我们想检测时!符号已经输入,我们必须查看是否同时按下了两个单独的键。
在这种情况下,SFML通过提供简单易用的TextEntered事件为我们节省了大量工作。 仅当按下表示字符的键组合时才会触发该事件; 意味着单个键(例如,只有Shift)可能不会触发事件。 当然,如果单独按下K,事件将被正常触发,并将包含该字符。
查看这个示例,其中使用 TextEntered 事件将使用的字符串是由字符组装而成的,当按下Enter(或Reach)按钮时,文本设置为标题:
sf::String buffer;
while (window.isOpen()) //游戏循环
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::EventType::Closed:
window.close();
break;
case sf::Event::EventType::TextEntered:
//将字符直接添加到字符串中
buffer += event.text.unicode;
break;
case sf::Event::EventType::KeyReleased:
//将标题更改为当前缓冲区并清除缓冲区
if (event.key.code == sf::Keyboard::Key::Return)
{
window.setTitle(buffer);
buffer.clear();
}
break;
default:
break;
}
}
}
return 0;
注意,我们使用的字符串缓冲区的类型是sf:string,而不是std:string。创建sf:string类是为了自动处理字符串类型和编码之间的转换。