GLFW key callback synchronization
我对GLFW和OpenGL非常陌生,我正在开发一个小型的模型渲染器。我目前正在研究输入,并且我面临着GLFW如何处理输入的问题,让我解释一下:每个教程都告诉您使用glfwgetkey和"if-forest"来查看是否已经按下了这样的键和这样的键。我遇到的问题是,如果我映射大量的键,它可能会变慢,而且很难看。所以我使用函数指针表和glfwsetkeycallback来加快速度并获得更清晰的代码。我遇到的问题是,我正面临着一个看起来像比赛条件的问题,摄像头似乎有点结巴。为了保持恒定的速度,我用每帧计算的增量时间。据我所见,键回调函数似乎每隔一段时间调用一次,而不是在每一帧上重复一个键时调用一次…我从他们的Github使用最新版本的glfw3,在每个循环的开始交换缓冲区,在结束时使用glfwPollEvents()。
我的问题是:是否有一种方法可以同步glfwpollEvents调用和呈现,以避免呈现循环和回调函数之间的结巴和增量差异?提前感谢您的帮助!
一般来说,处理输入的方法是保存一个键列表,并记录它们的最后一个输入状态。
1 2 3 4 5 6 7 8 9 10 | struct key_event { int key, code, action, modifiers; std::chrono::steady_clock::time_point time_of_event; } std::map<int, bool> keys; std::queue<key_event> unhandled_keys; void handle_key(GLFWwindow* window, int key, int code, int action, int modifiers) { unhandled_keys.emplace_back(key, code, action, modifiers, std::chrono::steady_clock::now()); } |
然后,在呈现循环中(或者,如果您对多线程+同步功能有信心,可以将其分成不同的循环),您可以编写如下代码:
1 2 3 4 5 | float now = glfwGetTime(); static float last_update = now; float delta_time = now - last_update; last_update = now; handle_input(delta_time); |
其中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | float external_position[2]; std::map<int, std::function<void(/*args*/)>> key_functions; void handle_input(float delta_time) { //Anything that should happen"when the users presses the key" should happen here while(!unhandled_keys.is_empty()) { key_event event = unhandled_keys.front(); unhandled_keys.pop(); key_functions[event.key](/*args*/); bool pressed = event.action == GLFW_PRESS || event.action == GLFW_REPEAT; keys[event.key] = pressed; } //Anything that should happen"while the key is held down" should happen here. float movement[2] = {0,0}; if(keys[GLFW_KEY_W]) movement[0] += delta_time; if(keys[GLFW_KEY_S]) movement[0] -= delta_time; if(keys[GLFW_KEY_A]) movement[1] -= delta_time; if(keys[GLFW_KEY_D]) movement[1] += delta_time; external_position[0] += movement[0]; external_position[1] += movement[1]; } |
编辑:我添加了逻辑来处理"on press"/"on release"类型的功能。例如,如果这个代码在渲染器中:
1 | key_functions[GLFW_KEY_SPACE] = [&renderer] {renderer.pause();}; |
然后按
好吧,那么,我会回答我自己的问题(并留下@xirema的正确答案)
我想我刚刚理解了为什么它不能在framerate和callback-on-repeat之间同步,这是因为操作系统处理密钥重复的方式,它不会每秒发送4000+个密钥重复信号(当我得到4000+fps时),并且会将自己限制在60个调用/秒左右!为了纠正这个问题,我实际上只需在回调函数中注册上一次按下的键60次/s,并在每个帧的glfwkeyun(在主循环中)之外执行函数,或者找到一种方法来规范化移动以避免结巴!
不管怎样,再次感谢您花时间回答@xirema我希望我的问题能对某人有用,现在,就开始编写代码吧!(不是真的;-)
提示:知道发送钥匙重复信号的次数,在钥匙重复回调函数中使用一个固定的deltatime值也可以做到这一点!
P.P.S:好吧,考虑到键盘重复可能因操作系统或用户设置或甚至CPU电源的不同而有所不同,最好的解决方案是为glfwkeyun设置一个单独的delta时间,这样您就可以将其发送到回调函数,并始终为键重复获得正确的delta时间,避免所有的结巴!