取消按下第二个键 LWJGL 时的延迟 Java

Remove delay while pressing the second key LWJGL Java

我运行遇到一个问题,即使在互联网上搜索也暂时找不到任何解决方案。

我目前正在开发一款 2D 游戏,我希望我的玩家可以沿对角线向各个方向移动。出于这个原因,我需要从键盘获得两个输入,这是我可以编码的东西,但现在我的问题是,当我按下第二个键时,我的播放器不动时会有轻微的延迟。这很烦人,因为当我改变方向时,运动一点也不流畅。 这里是我的 class,我在这里管理所有输入。 预先感谢您的帮助。

public class Input
{
    private static final Input INSTANCE = new Input();
    
    public final static float PI = (float) 3.141592;
    public final static float P2 = (float) (PI/2);
    
    private double x = Jeu.Isaac.getDeplacement().getHit().getEntity().getX();
    private double y = Jeu.Isaac.getDeplacement().getHit().getEntity().getY();
    private double a = Jeu.Isaac.getDeplacement().getA();
    
    private final int[] listeInput = {GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_W, GLFW.GLFW_KEY_S,
            GLFW.GLFW_KEY_UP, GLFW.GLFW_KEY_DOWN, GLFW.GLFW_KEY_RIGHT, GLFW.GLFW_KEY_LEFT};
    
    private double speed = 5.85;

    public void drawBalle() 
    {
        Jeu.Isaac.getMunitions().drawBalle();
    }
    
    public DeplacerPersonnage getPlayerMove() {
        return Jeu.Isaac.getDeplacement();
    }
    
    private GLFWKeyCallback keyboard;
    
    private HashMap<Integer, Boolean> mappageTouches;
    
    private Input()
    {
        mappageTouches = new HashMap<Integer, Boolean>();
        
        for(int key:listeInput)
        {
            mappageTouches.put(key, false);
        }

        keyboard = new GLFWKeyCallback()
        {

            @Override
            public void invoke(long window, int key, int scancode, int action, int mods) 
            {
                if(action == GLFW.GLFW_RELEASE)
                {
                    mappageTouches.replace(key, false);
                }
                else if(action == GLFW.GLFW_PRESS)
                {
                    System.out.println("Pressed");
                    mappageTouches.replace(key, true);
                    getAWSDkeys();
                    getShotsKeys();
                }
                else
                {
                    System.out.println("hold");
                    getAWSDkeys();
                    getShotsKeys();
                }
            }
            
        };
    }
    
    
    public void getAWSDkeys()
    {
        for(Integer key:listeInput)
        {
            if(mappageTouches.get(key))
            {
                switch(key)
                {
                    case GLFW.GLFW_KEY_A:
                        a = PI;
                        if(!Jeu.Isaac.getDeplacement().getHit().isQCollision()) x -= speed;
                        Jeu.Isaac.getDeplacement().update(x, y, a);
                        Jeu.Isaac.getDeplacement().drawPlayer();
                        break;
                    case GLFW.GLFW_KEY_D:
                        a = 0;
                        if(!Jeu.Isaac.getDeplacement().getHit().isDCollision()) x += speed;
                        Jeu.Isaac.getDeplacement().update(x, y, a);
                        Jeu.Isaac.getDeplacement().drawPlayer();
                        break;
                    case GLFW.GLFW_KEY_W:
                        a = PI/2;
                        if(!Jeu.Isaac.getDeplacement().getHit().isZCollision()) y += speed;
                        Jeu.Isaac.getDeplacement().update(x, y, a);
                        Jeu.Isaac.getDeplacement().drawPlayer();
                        break;
                    case GLFW.GLFW_KEY_S:
                        a = 3*(PI/2);
                        if(!Jeu.Isaac.getDeplacement().getHit().isSCollision()) y -= speed;
                        Jeu.Isaac.getDeplacement().update(x, y, a);
                        Jeu.Isaac.getDeplacement().drawPlayer();
                        break;
                }
            }
        }

    }
    
    public void getShotsKeys()
    {
        for(Integer key:listeInput)
        {
            if(mappageTouches.get(key))
            {
                switch(key)
                {
                    case GLFW.GLFW_KEY_UP:
                        Jeu.Isaac.getMunitions().addBalle(new Balle(1, 1, Jeu.Isaac.getDeplacement().getHit().getEntity().getX(), Jeu.Isaac.getDeplacement().getHit().getEntity().getY(), 3));
                        break;
                    case GLFW.GLFW_KEY_DOWN:
                        Jeu.Isaac.getMunitions().addBalle(new Balle(1, 1, Jeu.Isaac.getDeplacement().getHit().getEntity().getX(), Jeu.Isaac.getDeplacement().getHit().getEntity().getY(), 4));
                        break;
                    case GLFW.GLFW_KEY_RIGHT:
                        Jeu.Isaac.getMunitions().addBalle(new Balle(1, 1, Jeu.Isaac.getDeplacement().getHit().getEntity().getX(), Jeu.Isaac.getDeplacement().getHit().getEntity().getY(), 2));
                        break;
                    case GLFW.GLFW_KEY_LEFT:
                        Jeu.Isaac.getMunitions().addBalle(new Balle(1, 1, Jeu.Isaac.getDeplacement().getHit().getEntity().getX(), Jeu.Isaac.getDeplacement().getHit().getEntity().getY(), 1));
                        break;
                }
            }
        }
        
    }
    
    public static Input getInstance()
    {
        return INSTANCE;
    }

    public void init(long window)
    {
        glfwSetKeyCallback(window, keyboard);
    }
    
}

问题在于您的输入处理逻辑,例如通过 x += speed;(在 getAWSDkeys() 中)递增 x 变量,仅在 OS 发送 window 键盘的事件消息。这只发生在 KEY_DOWN、KEY_UP 和 KEY_REPEAT 事件中。而且,是的,当您按住同一个键时,KEY_DOWN 和 KEY_REPEAT 事件之间存在延迟。

为了解决这个问题,您只需将游戏逻辑(如前面提到的 xy 变量的 increment/decrement 移动到 外部 键事件处理程序。

通常,您有一个游戏循环,它也会调用 glfwPollEvents()(如果您将 input/window 事件处理与渲染分离,则调用 glfwWaitEvents())。

因此,您的键盘事件处理函数应该只为每个可能的键设置一些布尔标志,指示该键现在是 down/pressed 或 up/released。 在你的 game/input 循环中,你只需查询你的布尔标志的状态,并通过执行 x/y increment/decrement 来相应地采取行动。

您也可以使用 glfwGetKey() 函数,而不是为所有键存储您自己的布尔标志,无论它们是否 down/up。但是请注意,此函数 不会 查询当前键状态,而是 returns 缓存键状态(缓存在 GLFW 内部),当您在 GLFW sets/remembers 内部调用 glfwPollEvents()/glfwWaitEvents()。 参见:https://www.glfw.org/docs/3.3/input_guide.html#input_key

This function only returns cached key event state. It does not poll the system for the current physical state of the key.

简而言之:问题在于您的游戏逻辑在 window 事件消息回调的 内部 (由 glfwPollEvents/glfwWaitEvents 当 OS 向您的 process/game 发送键盘事件时。您必须将输入事件的处理与实际游戏逻辑分离。

对不起,我最近很忙。

我最终解决了我的回调逻辑问题。 我删除了所有内容,然后直接使用 glfwGetKey() 并且它起作用了。

这里是我编写的代码,如果它可以帮助一些人。

public class Input
{
    private static final Input INSTANCE = new Input();
    
    public final static float PI = (float) 3.141592;
    public final static float P2 = (float) (PI/2);
    
    private double x = Jeu.Isaac.getPosition().getY();
    private double y = Jeu.Isaac.getPosition().getX();
    private double a = Jeu.Isaac.getA();
    
    private final int[] listeInput = {GLFW.GLFW_KEY_A, GLFW.GLFW_KEY_D, GLFW.GLFW_KEY_W, GLFW.GLFW_KEY_S, GLFW.GLFW_KEY_I, GLFW.GLFW_KEY_L, GLFW.GLFW_KEY_K, GLFW.GLFW_KEY_P,GLFW.GLFW_KEY_O,
            GLFW.GLFW_KEY_SPACE, GLFW.GLFW_KEY_UP, GLFW.GLFW_KEY_DOWN, GLFW.GLFW_KEY_RIGHT, GLFW.GLFW_KEY_LEFT};
    
    private double speed = 5.85;

    public void drawBalle() 
    {
        Jeu.Isaac.getMunitions().drawBalle();
    }

    public Personnage getPlayerMove() {
        return Jeu.Isaac;
    }
    
    private GLFWKeyCallback keyboard;
    
    //private HashMap<Integer, Boolean> mappageTouches;
    
    private Input()
    {
        /*mappageTouches = new HashMap<Integer, Boolean>();
        
        for(int key:listeInput)
        {
            mappageTouches.put(key, false);
        }*/

        /*keyboard = new GLFWKeyCallback()
        {
            @Override
            public void invoke(long window, int key, int scancode, int action, int mods) 
            {
                if(glfwGetKey(window, key) == GLFW_PRESS)
                {
                    System.out.println("Pressed");
                    getAWSDkeys(window);
                    getShotsKeys(window);
                }
                if(glfwGetKey(window, key) == GLFW_RELEASE)
                {
                    glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_FALSE);
                    System.out.println("Released");
                }
            }
            
        };*/
    }
    
    public void deplacement()
    {
        for(Integer key:listeInput)
        {
            checkDeplacement(Fenetre.getInstance().getWindow(), key);
        }
        Jeu.Isaac.updateGameObject();
    }
    
    public void tire()
    {
        for(Integer key:listeInput)
        {
            checkTire(Fenetre.getInstance().getWindow(), key);
        }
    }
    
    /**
     * Méthode qui vérifie si une touche a été pressée.
     * Si oui renvoie vers la méthode getAWSDkeys(window).
     * Si non, définie l'entrée rémanente à faux.
     * @param window
     * La fenêtre active.
     * @param key
     * La touche détectée à vérifier.
     */
    public void checkDeplacement(long window, int key)
    {
        if(glfwGetKey(window, key) == GLFW_PRESS)
        {
    //      System.out.println("Pressed");
            getAWSDkeys(window);
        }
        if(glfwGetKey(window, key) == GLFW_RELEASE)
        {
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_FALSE);
    //      System.out.println("Released");
        }
    }
    
    public void checkTire(long window, int key)
    {
        if(glfwGetKey(window, key) == GLFW_PRESS)
        {
    //      System.out.println("Pressed");
            getShotsKeys(window);
        }
        if(glfwGetKey(window, key) == GLFW_RELEASE)
        {
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_FALSE);
    //      System.out.println("Released");
        }
    }
    
    public void moveUp()
    {
        if(!Jeu.Isaac.getHitbox().isZCollision()) {
            Jeu.Isaac.goUpNext();
        }
        Jeu.Isaac.setA(PI/2);
        Jeu.Isaac.drawPlayer();
    }
    
    public void moveDown()
    {
        if(!Jeu.Isaac.getHitbox().isSCollision()) {
            Jeu.Isaac.goDownNext();
        }
        Jeu.Isaac.setA(3*PI/2);
        Jeu.Isaac.drawPlayer();
    }
    
    public void moveRight()
    {
        a = 0;
        if(!Jeu.Isaac.getHitbox().isDCollision()) {
            Jeu.Isaac.goRightNext();
        }
        Jeu.Isaac.setA(0);
        Jeu.Isaac.drawPlayer();
    }
    
    public void moveLeft()
    {
        a = PI;
        if(!Jeu.Isaac.getHitbox().isQCollision()) {
            Jeu.Isaac.goLeftNext();
        }
        Jeu.Isaac.setA(PI);
        Jeu.Isaac.drawPlayer();
    }
    
    public void poseBomb() {
        ObjetsInventaire bomb = new ObjetsInventaire(-1, 100, 100, Jeu.gameWorld.getPlayer().getPosition(), "");
    }
    
    public void shootUp()
    {
        Jeu.Isaac.getMunitions().addBalle(new Balle(25, 25, Jeu.Isaac.getPosition().getX(), Jeu.Isaac.getPosition().getY(), new Vector2(0, 1), "", 10));
        Jeu.gameWorld.getPlayer().setFace(1);
    }
    
    public void shootDown()
    {
        Jeu.Isaac.getMunitions().addBalle(new Balle(25, 25, Jeu.Isaac.getPosition().getX(), Jeu.Isaac.getPosition().getY(), new Vector2(0, -1), "", 10));
        Jeu.gameWorld.getPlayer().setFace(2);
    }
    
    public void shootRight()
    {
        Jeu.Isaac.getMunitions().addBalle(new Balle(25, 25, Jeu.Isaac.getPosition().getX(), Jeu.Isaac.getPosition().getY(), new Vector2(1, 0), "", 10));
        Jeu.gameWorld.getPlayer().setFace(3);
    }
    
    public void shootLeft()
    {
        Jeu.Isaac.getMunitions().addBalle(new Balle(25, 25, Jeu.Isaac.getPosition().getX(), Jeu.Isaac.getPosition().getY(), new Vector2(-1, 0), "", 10));
        Jeu.gameWorld.getPlayer().setFace(4);
    }
    
    public void getAWSDkeys(long window)
    {
        if(glfwGetKey(window, GLFW_KEY_W) == GLFW.GLFW_PRESS)
        {
            moveUp();
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_S) == GLFW.GLFW_PRESS)
        {
            moveDown();
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_D) == GLFW.GLFW_PRESS)
        {
            moveRight();
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_A) == GLFW.GLFW_PRESS)
        {
            moveLeft();
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_L) == GLFW.GLFW_PRESS)
        {
            Jeu.gameWorld.getPlayer().setSpeed(20);
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_I) == GLFW.GLFW_PRESS)
        {
            Jeu.gameWorld.getPlayer().setInvincible(true);
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_K) == GLFW.GLFW_PRESS)
        {
            Jeu.gameWorld.getMapEnCours().setListeEnnemi(new listeEnnemi());
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_P) == GLFW.GLFW_PRESS)
        {
            Jeu.gameWorld.getPlayer().setDegat(Integer.MAX_VALUE);
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_O) == GLFW.GLFW_PRESS)
        {
            Jeu.gameWorld.getPlayer().setCoin(Jeu.gameWorld.getPlayer().getCoin() + 10); //TODO pas fini
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_SPACE) == GLFW.GLFW_PRESS)
        {
            poseBomb();
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
    }
    
    public void getShotsKeys(long window)
    {
        if(glfwGetKey(window, GLFW_KEY_UP) == GLFW.GLFW_PRESS)
        {
            shootUp();
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_DOWN) == GLFW.GLFW_PRESS)
        {
            shootDown();
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW.GLFW_PRESS)
        {
            shootRight();
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
        if(glfwGetKey(window, GLFW_KEY_LEFT) == GLFW.GLFW_PRESS)
        {
            shootLeft();
            glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
        }
    }
    
    public void init(long window)
    {
        
    }

    public static Input getInstance() {
        return INSTANCE;
    }
}```