【问题标题】:LWJGL 3 GLFW keyboard and mouse input is slow and has a delay between GLFW_PRESS and GLFW_REPEATLWJGL 3 GLFW 键盘和鼠标输入很慢,在 GLFW_PRESS 和 GLFW_REPEAT 之间有延迟
【发布时间】:2016-04-11 13:57:33
【问题描述】:

所以我最近刚从 LWJGL 2 转到 3,我在获得键盘和鼠标输入时遇到了一些困难。顺便说一句,我正在使用它来移动和旋转相机。

我的问题是,如果我使用 GLFW 回调,移动似乎非常不稳定和缓慢。这不是一个一致的速度,只是感觉不对。此外,当我按下一个键时,例如 w 向前移动,从 GLFW_PRESSGLFW_REPEAT 的转换之间大约有半秒的延迟被按下。

顺便说一句,我有一个 InputHandler 类,它具有 keyDown、keyPressed、keyReleased 等方法。我没有在一个调用方法中进行所有键检查。调用方法将键事件添加到列表中存储密钥和一个事件状态枚举,可以是 DOWN、TAPPED、RELEASED、NONE 之一。该课程主要基于对this question的回复

在过去的 2 个小时里,我一直在尝试解决这个问题,但没有找到太多的解决方案。我确实发现有些人使用回调的替代方法,就像这样:

if (glfwGetKey(window, key) == GLFW_PRESS)

if (glfwGetKey(windowm key) == GLFW_RELEASE)

但我无法找到一种方法来使用它来检测单个按键点击,即GLFW_REPEAT 状态,因为glfwGetKey 方法只能检测GLFW_PRESSGLFW_RELEASE

如果有人能告诉我一种使用 glfwGetKey 方法检测单个按键敲击的方法,或者使 GLFW 回调更不滞后,更流畅,我将不胜感激。

谢谢:)

【问题讨论】:

  • 大概,如果您正在制作动画(在循环中使用glfwPollEvents),您可以假设键被按住,直到您的键处理程序回调接收到该键的RELEASE。否则,循环中的glfwWaitEvents 不会做任何事情,直到窗口系统决定REPEAT 的时间间隔足够长。
  • 有没有办法缩短注册GLFW_REPEAT的时间?
  • 不使用 glfw。可能有依赖于操作系统的方法来做到这一点,但 glfw 只响应操作系统提供的事件。而且我怀疑它会产生全局影响,而不是针对每个应用程序。
  • 好的。好吧,我设法得到您的建议,即假设密钥已关闭,直到触发 GLFW_RELEASE 事件。它有效,我将发布我的工作 InputHandler 类,谢谢 :)

标签: java opengl user-input lwjgl glfw


【解决方案1】:

所以我解决了这个问题,这要感谢 Brett Hale 的建议,即假设密钥已关闭,直到触发 GLFW_RELEASE 事件。这是我的工作实现:

public final class InputHandler
{
    private static long window;
    private static final int KEYBOARD_SIZE = 512;
    private static final int MOUSE_SIZE = 16;

    private static int[] keyStates = new int[KEYBOARD_SIZE];
    private static boolean[] activeKeys = new boolean[KEYBOARD_SIZE];

    private static int[] mouseButtonStates = new int[MOUSE_SIZE];
    private static boolean[] activeMouseButtons = new boolean[MOUSE_SIZE];
    private static long lastMouseNS = 0;
    private static long mouseDoubleClickPeriodNS = 1000000000 / 5; //5th of a second for double click.

    private static int NO_STATE = -1;


    protected static GLFWKeyCallback keyboard = new GLFWKeyCallback()
    {
        @Override
        public void invoke(long window, int key, int scancode, int action, int mods)
        {
            activeKeys[key] = action != GLFW_RELEASE;
            keyStates[key] = action;
        }
    };

    protected static GLFWMouseButtonCallback mouse = new GLFWMouseButtonCallback()
    {
        @Override
        public void invoke(long window, int button, int action, int mods)
        {
            activeMouseButtons[button] = action != GLFW_RELEASE;
            mouseButtonStates[button] = action;
        }
    };

    protected static void init(long window)
    {
        InputHandler.window = window;

        resetKeyboard();
        resetMouse();
    }

    protected static void update()
    {
        resetKeyboard();
        resetMouse();

        glfwPollEvents();
        Engine.getInput();
    }

    private static void resetKeyboard()
    {
        for (int i = 0; i < keyStates.length; i++)
        {
            keyStates[i] = NO_STATE;
        }
    }

    private static void resetMouse()
    {
        for (int i = 0; i < mouseButtonStates.length; i++)
        {
            mouseButtonStates[i] = NO_STATE;
        }

        long now = System.nanoTime();

        if (now - lastMouseNS > mouseDoubleClickPeriodNS)
            lastMouseNS = 0;
    }

    public static boolean keyDown(int key)
    {
        return activeKeys[key];
    }

    public static boolean keyPressed(int key)
    {
        return keyStates[key] == GLFW_PRESS;
    }

    public static boolean keyReleased(int key)
    {
        return keyStates[key] == GLFW_RELEASE;
    }

    public static boolean mouseButtonDown(int button)
    {
        return activeMouseButtons[button];
    }

    public static boolean mouseButtonPressed(int button)
    {
        return mouseButtonStates[button] == GLFW_RELEASE;
    }

    public static boolean mouseButtonReleased(int button)
    {
        boolean flag = mouseButtonStates[button] == GLFW_RELEASE;

        if (flag)
            lastMouseNS = System.nanoTime();

        return flag;
    }

    public static boolean mouseButtonDoubleClicked(int button)
    {
        long last = lastMouseNS;
        boolean flag = mouseButtonReleased(button);

        long now = System.nanoTime();

        if (flag && now - last < mouseDoubleClickPeriodNS)
        {
            lastMouseNS = 0;
            return true;
        }

        return false;
    }
}

如果您愿意,请随意使用此代码。不过只是一些注意事项:应该在每一帧调用更新方法。此外,如果您在其他地方有glfwPollEvents(),我认为这很可能,那么您需要保持重置键盘/鼠标的顺序,然后是轮询,然后是 getinput()。

编辑: 我的Engine.getInput() 方法正是告诉场景图中需要输入的节点(即玩家)查询该输入。

【讨论】:

  • 我不太明白你的意思。我对 GLFW 和 OpenGL 3 还是很陌生,所以我之前没见过glfwWaitEvents()。我跟着this guide 设置了我的窗口。 glfwWaitEvents() 应该在哪里??!
【解决方案2】:

虽然 Kelan 的答案非常有效,但我想我会分享我的解决方案,因为我在开发游戏时遇到了同样的问题。我实现了自己的键盘类并包含以下方法

public static boolean isKeyPressed(int key)
{
    return (GLFW.glfwGetKey(Game.window.handle, key) == KEY_PRESS);
}

public static boolean isKeyReleased(int key)
{
    return (GLFW.glfwGetKey(Game.window.handle, key) == KEY_RELEASE);
}

由于 GLFW 保存了每个键的最后一个事件,您只需要查看最后一个事件是 PRESS 还是 RELEASE。 GLFW 不会使用此方法报告 REPEAT 事件,但您可以假设它一直在重复,直到触发 RELEASE 事件。

【讨论】:

    【解决方案3】:

    我参加聚会有点晚了,但我想我可以分享对我有用的东西。我是用 C++ 编写的,但我会尽可能简单。

    我创建了一个任意大小的布尔数组来存储所有按下的键。

    bool *keysArePressed = new bool[512];.

    如果按键被按下,我所做的不是为(我太懒惰)创建一个类:

    void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) 
    { 
       keysArePressed[key] = (glfwGetKey(window, key) == GLFW_PRESS);
    }
    
    void keyInput()
    {
       if(keyArePressed['W'])// 'W' has to be uppercase otherwise it won't work
       {
       /*Do whatever here*/
       }
    }
    

    然后在glfwPollEvents();之前在主循环中调用keyInput。 多次按键似乎也很容易。

    【讨论】:

      【解决方案4】:

      其他答案不是我遇到的问题的根本原因。交换缓冲区时,绘制简单的四边形似乎会阻塞输入处理。我只是 glfwSwapInterval(0) 关闭垂直同步,这不一定是一个好习惯,但在我的程序的当前阶段,我宁愿屏幕撕裂而不是糟糕的输入延迟,这似乎可以解决它。

      【讨论】:

      • 当我在没有垂直同步的情况下强制我的游戏以 4k FPS 运行时,我的屏幕永远不会撕裂
      • @Fox 是的,写完这个答案后,我想我发现它是别的东西。我将编辑我的答案。
      • @Fox 即使没有垂直同步,当它向屏幕帧缓冲区发送不完整的帧时也会发生撕裂,当您的 FPS 低于显示器刷新率时会发生这种情况。
      • 是的,我曾经看到 youtube 的屏幕撕裂(我真的看 youtube 将每个像素设置为 400 毫秒)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-27
      • 2014-09-18
      • 1970-01-01
      • 2011-02-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多