1

I am creating a simple game with Java Swing. I have a problem - the KeyListener seems to react a bit choppy/slow. The character moves using this code:

// player control
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        switch(key){
        case(KeyEvent.VK_LEFT): dx = -speed; break;
        case(KeyEvent.VK_RIGHT): dx = speed; break;
        case(KeyEvent.VK_UP): dy = -speed; break;
        case(KeyEvent.VK_DOWN): dy = speed; break;
        }
    }

public void keyReleased(KeyEvent e) {
    int key = e.getKeyCode();
    switch(key){
    case(KeyEvent.VK_LEFT): dx = 0; break;
    case(KeyEvent.VK_RIGHT): dx = 0; break;
    case(KeyEvent.VK_UP): dy = 0; break;
    case(KeyEvent.VK_DOWN): dy = 0; break;
    }
}

// this method is performed each time before redrawing the screen
public void step() {
    x += dx;
    y += dy;
}

Most of the time everything is fine, but sometimes the character stops for a while before reacting to the keyboard. How to make the keyboard input more smooth?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
YemSalat
  • 19,986
  • 13
  • 44
  • 51
  • 1
    Could be the 'key repeat speed'. Have you considered using key bindings? – Andrew Thompson Jun 05 '12 at 18:34
  • 1
    that isn't true at all, slow reactions ???, sure there is one issue == depends of your code that .... (same will be by using `KeyBinding`), for better help sooner edit your question with an [SSCCE](http://sscce.org/) – mKorbel Jun 05 '12 at 18:39
  • actually after reading about key bindings it seems that they work in the same way as the `KeyListener` I was also wondering if it is possible to 'exit' the KeyPressed event after it has been fired, so it does not repeat – YemSalat Jun 05 '12 at 18:44
  • I think that the problem is in the 'key repeat speed' Basically what happens is: for example the right key is pressed, the character moves right. Then the left key is pressed - character starts moving to the left. If you release the right key now the `keyReleased` will fire and it will reset the xSpeed to 0. Now the listener waits for the next `keyPressed` to fire, as we are still holding the left key. – YemSalat Jun 05 '12 at 18:51
  • 1
    right output from keyboard to `KeyListener` is the same as `KeyBindings`, but here the resemblance ends already, everything next is about `Focus` and `FocusSubsystem`, with `KeyBindings` doesn't matter if is or isn't, you can't solving this issue ever, with `KeyListener` is most importans, major issue and depends of ..., up to, you including edit your post with SSCCE, for better help to check post by mouseclick to the [tag:keylistener] – mKorbel Jun 05 '12 at 19:06
  • you are right, it turns out there is no way to solve this with `KeyListener` itself, however I found another solution - just keeping track of which keys are pressed at the moment and checking them on each `keyReleased()` So we don't have to reset the character's speed to zero when for example right key is released but the user is still holding the left key. I will update my post to show the solution. – YemSalat Jun 05 '12 at 19:15
  • 1
    there no issue with delaying events from any of Listener by using Swing Timer, – mKorbel Jun 05 '12 at 19:17
  • I am not really trying to delay the event - I was looking for a solution to 'speed up' the `KeyListener` but as I said, checking which keys are pressed solves the issue – YemSalat Jun 05 '12 at 19:50

2 Answers2

1

Thanks everybody for your answers! They helped me get on the right track.

Whats happening here, as I said in my comment, is the following: if for example we are holding the right key - the character is moving to the right, then we press the left key and it starts moving to the left. Now if we release the right key the xSpeed will be reset to zero and the KeyListener has to wait until the keyPressed() is fired again, as we are still holding the left key. The 'key repeat speed' is set by default and as far as I understand can not be adjusted.

So the solution is to keep track of which keys are currently pressed, so we don't have to drop character's speed to zero when the right key is released if we are still holding the left key.

Sorry for a bit cryptic example, just trying to keep the code small:

// Define keys: up, right, down, left
// 0 - not pressed, 1 - pressed
private int[] keys = {0, 0, 0, 0};

// player control
public void keyPressed(KeyEvent e) {
    int key = e.getKeyCode();
    // we set the corresponding 'key indicator' to 1 when it is pressed
    switch(key){
        case(KeyEvent.VK_LEFT): dx = -speed; keys[3] = 1; break; 
        case(KeyEvent.VK_RIGHT): dx = speed; keys[1] = 1; break;
        case(KeyEvent.VK_UP): dy = -speed; keys[0] = 1; break;
        case(KeyEvent.VK_DOWN): dy = speed; keys[2] = 1; break;
        case(KeyEvent.VK_SPACE): fireOn = true; break;
    }
}

public void keyReleased(KeyEvent e) {
    int key = e.getKeyCode();
    // now before resetting the speed we check if the opposite key
    // is still pressed
    switch(key){
        case(KeyEvent.VK_LEFT): if(keys[1]==0){dx = 0;} keys[3] = 0; break;
        case(KeyEvent.VK_RIGHT): if(keys[3]==0){dx = 0;} keys[1] = 0; break;
        case(KeyEvent.VK_UP): if(keys[2]==0){dy = 0;} keys[0] = 0; break;
        case(KeyEvent.VK_DOWN): if(keys[0]==0){dy = 0;} keys[2] = 0; break;
        case(KeyEvent.VK_SPACE): fireOn = false; break;
    }
}
YemSalat
  • 19,986
  • 13
  • 44
  • 51
  • [then to check an alternative](http://stackoverflow.com/a/10879366/714968), can you see differencies now ??? – mKorbel Jun 05 '12 at 20:08
1

Another way may helps:

object.addKeyListener(new KeyAdapter() {
    @Override
    public void keyTyped(KeyEvent e) {
        updateOpreation();
    }

    @Override
    public void keyReleased(KeyEvent e) {
        updateOpreation();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        updateOpreation();
    }

}