处理好来自用户的事件是一个很重要的话题。
首先我们来认识一下什么是事件?
一般来说,事件是在某些情况下被触发的对象。它们依赖于操作系统,但是 SFML 为我们提供了一个很好的对象来处理事件,它是独立于操作系统的方式。sf::Event 类,这个类包含了各种事件。
建议阅读 SFML 官方文档去好好了解 SFML 事件的类型,地址是https://www.sfml-dev.org/tutorials/2.5/window-events.php。 注意 sf::Event 是一个联合体(union),使用的时候要恰当的选择我们需要的事件类型。
逻辑上,我们可以将事件分成四种类型:
- 窗口
- 键盘
- 鼠标
- 手柄
一、窗口事件
类型 | 相关成员 | 描述 |
---|---|---|
Event::Closed | None | 当操作系统检测单用户想要关闭窗口时触发 |
Event::Resized | Event::size 表示新窗口的大小 | 当操作系统检测单用户想要改变窗口大小时触发,或者调用Window::setSize() |
Event::LostFocus Event::GainedFocus | None | 当窗口失去/得到焦点时触发,失去焦点的窗口不会收到键盘事件 |
二、键盘事件
类型 | 相关成员 | 描述 |
---|---|---|
Event::KeyPressed Event::KeyReleased | Event::key 表示的事当前按住/松开的按键 | 在得到焦点的窗口中,当一个按钮被按下或者松开的时候触发 |
Event::TextEntered | Event::text 表示的是一个字符的UTF-32 unicode | 任何时候一个字符输入的时候触发 |
三、鼠标事件
类型 | 相关成员 | 描述 |
---|---|---|
Event::MouseMoved | Event::mouseMove 表示鼠标新位置 | 当鼠标在窗口内移动时触发 |
Event::MouseButtonPressed Event::MouseButtonReleased | Event::mouseButton 表示的是按下/松开的鼠标按钮以及它的位置 | 当鼠标上的按钮按下/松开的时候触发。目前支持5种类型的按钮:左、中、右、额外的按钮1和按钮2 |
Event::MouseWheelMoved | Event::mouseWheel 表示的是滚轮以及鼠标位置 | 当鼠标上的滚轮滚动时触发 |
四、手柄事件
类型 | 相关成员 | 描述 |
---|---|---|
Event::JoystickConnected Event::JoystickDisconnected | Event::joystickConnect 表示的是手柄连接/断开连接时的ID | 当手柄连接或者断开连接时触发 |
Event::JoystickButtonPressed Event::JoystickButtonReleased | Event::joystickButton 表示的是手柄上按钮按下的数量以及手柄ID | 当手柄上的按钮按下时触发,SFML为我们提供了最多8个手柄,每个手柄32个按钮的支持 |
Event::JoystickMoved | Event::joystickMove 表示的是移动操纵杆的轴,操纵杆轴的位置以及手柄ID | 移动操纵杆的轴时会触发此操作 |
对于窗口事件来说,我们通常用 bool Window::pollEvent(sf::Event& event)
函数,如果有事件将要被使用的话,函数会返回 true
, event
参数会对应某个具体事件。需要注意的是,同一时间下可能会有多个事件需要被处理,因此我们需要确保捕获到每一个事件。代码如下:
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
}
// 刷新帧
// 绘制帧
}
运行上面这份代码之后,我们可以做出移动窗口,改变窗口大小或者是最小化窗口这些操作,但是有一个问题,那就是我们无法关闭窗口。当我们点击窗口的关闭按钮时,SFML 不会去关闭窗口,这一操作需要我们去实现。
五、 事件的使用
通过 Window::pollEvent()
捕获事件之后,我们可以通过 Event::type
检查事件类型。事件类型是 Event::EventTyp
,它是 Event
类中的一个枚举类型。使用方法如下
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::EventType::Closed) {
window.close();
}
}
调用 Window::close()
可以关闭窗口。
如果我们想要使用更多的事件,我们的可以使用 switch
语法。如下:
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");
// 当 Esc 键松开的时候关闭窗口
else if (event.key.code == sf::Keyboard::Key::Escape)
window.close();
break;
default:
break;
}
}
上面这份代码的意思很容易理解,通过空格键可以改变窗口的标题,松开 Esc 键时关闭窗口。注意到 Event::key
包含了 code
成员,它是 Keyboard::Key
类型中的一个枚举类型。用上面这种方法可以处理其他事件类型。
然而,Event::EventType::TextEntered
的使用有点意思。按键被按下/松开的时候可以被检测到且可以使用一种相对简单的方式去处理。但是当键入一些特殊字符的时候,事情变得有点复杂起来。例如,输入 !
号的时候在大多数键盘中是使用 shift+1
的。幸好 SFML 为我们提供了一个简单的方式去处理,仅当按下表示字符的组合键时才会发生事件,这意味着按下一个键(例如 shift
)时不会触发事件。
下面这个例子使用了 TextEntered
事件组合了一个字符串,然后按下 Enter
键时这个字符串会显示在窗口标题中。
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;
}
}
}
这里使用 sf::String
类型能够避免不同语言之间的字符编码转换。