关于C#:GLFW键回调同步

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);

其中handle_input如下:

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();};

然后按[Space]键将暂停渲染器。


好吧,那么,我会回答我自己的问题(并留下@xirema的正确答案)

我想我刚刚理解了为什么它不能在framerate和callback-on-repeat之间同步,这是因为操作系统处理密钥重复的方式,它不会每秒发送4000+个密钥重复信号(当我得到4000+fps时),并且会将自己限制在60个调用/秒左右!为了纠正这个问题,我实际上只需在回调函数中注册上一次按下的键60次/s,并在每个帧的glfwkeyun(在主循环中)之外执行函数,或者找到一种方法来规范化移动以避免结巴!

不管怎样,再次感谢您花时间回答@xirema我希望我的问题能对某人有用,现在,就开始编写代码吧!(不是真的;-)

提示:知道发送钥匙重复信号的次数,在钥匙重复回调函数中使用一个固定的deltatime值也可以做到这一点!

P.P.S:好吧,考虑到键盘重复可能因操作系统或用户设置或甚至CPU电源的不同而有所不同,最好的解决方案是为glfwkeyun设置一个单独的delta时间,这样您就可以将其发送到回调函数,并始终为键重复获得正确的delta时间,避免所有的结巴!