用GLFW處理用戶鍵盤輸入有兩種方式,一種是使用回調函數,一種是在每一次游戲循環中處理。這兩種方法的最大差別在於是否能連續獲得鍵盤輸入,第二種方法可以。
一、回調函數
方法介紹
GLFW官方文檔 對這種方法的講解很詳盡,下面只簡單記錄一下要點。
我們只需要自定義回調函數key_callback,並在進入游戲循環前調用函數glfwSetKeyCallback(window, key_callback)
即可。下面簡單介紹回調函數包含的內容。
函數頭為void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
,其中,
key
為按鍵,例如GLFW_KEY_UP,如果敲下的鍵不能被GLFW識別,比如Play和E-mail,key值就是GLFW_KEY_UNKNOWN;
scancode
是每個平台自定義的表示按鍵的代碼,當key值為GLFW_KEY_UNKNOWN時scancode會被用來識別鍵,否則scancode會被忽略;另外它可以和key一起用來獲得鍵的名字const char* key_name = glfwGetKeyName(GLFW_KEY_W, 0);
,第二個參數就是scancode。scancode可以通過const int scancode = glfwGetKeyScancode(GLFW_KEY_X);
獲得;
action
代表動作類型,有
- GLFW_PRESS:按下
- GLFW_RELEASE: 松開
- GLFW_REPEAT:重復按鍵
其中需要特別注意的是GLFW_REPEAT
。引用Stack Overflow上的一個回答來解釋它的意義:
Ask yourself, what is the frequency at which GLFW_REPEAT triggers? The right answer is that this frequency depends on your user's settings. It is exactly the frequency at which characters will appear in any input field in any application if you hold a character key. So the only purpose of GLFW_REPEAT is to implement printing in your application and make it look consistent with all other applications on the machine. You definitely should not use GLFW_REPEAT to implement WASD controls and other things like that.
也就是說如果我們希望在游戲中通過鍵盤控制一個人物的移動的話,是不能依靠GLFW_REPEAT讓人物跑起來的,一個原因是移動速度不由程序員控制,容易引起游戲混亂,另一個原因是,如果像下面這樣定義回調函數:
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
switch (key)
{
// change the position of the camera
case GLFW_KEY_UP:
camera_y += camera_speed;
break;
case GLFW_KEY_DOWN:
camera_y -= camera_speed;
break;
...
}
}
連續按“↑”鍵會發現攝像機位置先上移一個camera_speed的長度,卡頓一會兒,才開始連續向上移動的。當游戲人物逃避Boss追殺時,我們應該不希望他先呆住一兩秒再跑吧?因此要實現連續移動的效果就需要用到第二種方法,而非通過回調函數。
mods
是"modifier bits"的簡寫,有
- GLFW_MOD_SHIFT:按下Shift鍵
- GLFW_MOD_CONTROL:按下Ctrl鍵
- GLFW_MOD_ALT:按下Alt鍵
- GLFW_MOD_SUPER:無
通過key和mods的組合就可以實現諸如Ctrl+N之類的操作。
何時使用
- 按鍵速度不是特別快,不需要連續效果(比如人物不停地跑)
- 要處理文本輸入相關事件
二、循環中處理
方法介紹
這種方法的使用方式如下:
while (!glfwWindowShouldClose(window))
{
processInputs(window);
...
}
processInputs
中包含的要素和上面介紹的回調函數差不多,只是用到了另一個GLFW的函數glfwGetKey
。最終效果上,這個函數返回任意時間某個鍵的狀態,就是這個函數使得連續效果得以實現。processInputs可以這樣實現:
void processInputs(GLFWwindow* window)
{
// move the camera
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS)
camera_x -= camera_speed;
}
何時使用
- 對按鍵速度要求高甚至是一直按着,需要連續效果
glfwGetKey被調用前鍵被松開了怎么辦
除了用戶的操作外,函數glfwSetInputMode也會影響到glfwGetKey的返回值。
void glfwSetInputMode(GLFWwindow* window, int mode, int value)
當mode為GLFW_STICKY_KEYS
且value為GLFW_TRUE
時,只要按下一個鍵,那么glfwGetKey返回的該鍵的狀態一定是按下的,哪怕在glfwGetKey函數被調用前這個鍵又被松開了。也就是說,當我們只關心鍵是否被按下過、而不關心它是何時被按下的時,就可以設置這個狀態。
文本輸入
GLFW可以接受Unicode字符流作為輸入,從而允許程序處理來自用戶的文本輸入。我們可以將接受的Unicode字符轉換成ASCII等其他格式的字符。
在游戲循環前需要調用函數glfwSetCharCallback(window, character_callback);
回調函數的函數頭為void character_callback(GLFWwindow* window, unsigned int codepoint)