Search code examples
javaswingappletkey-bindings

Stop pause on key hold with KeyBindings Java


I'm trying to write a Pong applet in Java. When the user holds down either up or down, their paddle should move smoothly, but this isn't the case. It moves, then pauses, then starts moving again. Is there a way to stop this short pause from happening?

Main Class:

public class Main extends JApplet {

    public DrawPanel dp = new DrawPanel(400, 400);

    public void init(){
        add(dp);
        setSize(400, 400);
        requestFocusInWindow();

        Action moveDown = new AbstractAction(){
            public void actionPerformed(ActionEvent e){
                dp.player.y += 10;
                dp.repaint();
            }
        };

        dp.getInputMap().put(KeyStroke.getKeyStroke("pressed DOWN"), "move down");
        dp.getActionMap().put("move down", moveDown);       
    }
}

DrawPanel Class:

public class DrawPanel extends JPanel {

    public Paddle player;
    public Paddle opponent;

    public DrawPanel(int height, int width){
        int y = (height / 2) - (height / 10);
        int w = 15;
        int h = height / 5;

        player = new Paddle(2, y, 15, h, Color.white);
        opponent = new Paddle(width - (w+2), y, 15, h, Color.white);
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        this.setBackground(Color.BLACK);

        g.setColor(player.color);
        g.fillRect(player.x, player.y, player.width, player.height);

        g.setColor(opponent.color);
        g.fillRect(opponent.x, opponent.y, opponent.width, opponent.height);
    }   
}

Paddle Class:

public class Paddle {

    public int x, y, width, height;
    public Color color;

    public Paddle(int _x_, int _y_, int w, int h, Color c){
        x = _x_;
        y = _y_;
        width = w;
        height = h;
        color = c;
    }
}

Solution

  • The underlying issue is an impedance mismatch between the "instance" notion of a keyPressed and the "duration" notion of the movement.

    Instead of trying to smooth that over in the view (which shouldn't have anything to do with the details of the game logic anyway), enhance the game model with api that's a better fit. F.i. add start/stop logic and bind those methods to keyPressed/keyReleased:

    public class Paddle {
    
         public void startMoveDown() {
             // here goes the logic, f.i. starting a timer
             // in the actionPerformed of that timer:
             ...  moveUnitDown(); 
         }
         public void stopMoveDown() {
            //... 
         }
    
         protected void moveUnitDown() {
             y+=unit;
             // ideally, have listeners and notify them the change of y
         } 
    }
    
    // in the view:
    Action startMoveDown = new AbstractAction(){
        public void actionPerformed(ActionEvent e){
            player.startMoveDown();
       }
    };
    
    dp.getInputMap().put(KeyStroke.getKeyStroke("pressed DOWN"), "start move down");
    dp.getActionMap().put("start move down", startMoveDown); 
    
    // in the view:
    Action stopMoveDown = new AbstractAction(){
        public void actionPerformed(ActionEvent e){
            player.stopMoveDown();
       }
    };
    
    dp.getInputMap().put(KeyStroke.getKeyStroke("released DOWN"), "stop move down");
    dp.getActionMap().put("stop move down", stopMoveDown);