《第一章》SFML 入門
建立視窗
建立視窗的只需要一行程式碼:
#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,
● 如果我們需要根據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類是為了自動處理字串型別和編碼之間的轉換。