LWJGL 3 GLFW键盘和鼠标输入速度较慢,并且在GLFW_PRESS和GLFW_REPEAT之间存在延迟



所以我最近刚从LWJGL 2转到3,在获得键盘和鼠标输入方面有点困难。顺便说一句,我用这个来移动和旋转相机。

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

顺便说一下,我有一个InputHandler类,它有keyDown、keyPressed、keyReleased等方法。我在一个invoke方法中没有所有的键检查。invoke方法将键事件添加到存储键的列表中,以及可以是DOWN、TAPPED、RELEASED、NONE之一的事件状态枚举。该课程主要基于对这个问题的回答

在过去的两个小时里,我一直在努力寻找解决方案,但没有找到太多。不过,我确实发现一些人使用了一种替代回调的方法,比如:

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

if (glfwGetKey(windowm key) == GLFW_RELEASE)

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

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

感谢:)

所以我解决了这个问题,这要归功于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()方法正是告诉场景图中需要输入的节点(即玩家)查询该输入的方法。

虽然Kelan的答案很好,但我想我会分享我的解决方案,因为我在玩游戏时遇到了同样的问题。我实现了自己的Keyboard类,并包含了以下方法

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事件被触发。

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

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

CCD_ 11。

我没有为(我太懒了)做一个类,而是在按下键时抓取:

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。多按键似乎也很容易。

其他答案不是我遇到问题的根本原因。在交换缓冲区时,绘制简单的四边形似乎阻碍了输入处理。我只是简单地glfwSwapInterval(0)来关闭v-sync,这不一定是一个好的练习,但在我的程序的当前阶段,我更喜欢一些屏幕撕裂而不是糟糕的输入滞后,这似乎解决了它。

最新更新