0

I have created a program that creates a square and draws it on a JPanel. There are also KeyBindings that control the movement of the square (w is up) (s is down) (a is left) (d is right). My problem is that when I press the keys, it moves one instance, hesitates, then continues moving. Is there a way to prevent to the hesitation of the movement.

import javax.swing.JFrame;


public class MovingBox extends JFrame{

Panel panel;

public static void main(String args[]){

    MovingBox mb = new MovingBox();
}

public MovingBox(){

    super("Moving Box");
    setSize(800, 600);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    panel = new Panel();
    add(panel);

    setVisible(true);
}
}

This is my Panel class.

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;


public class Panel extends JPanel{

Rectangle box;

public Panel(){

    box = new Rectangle(100, 100, 50, 50);

    getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
            KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "move left");
    getActionMap().put("move left", new MoveAction(3, 3));

    getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
            KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "move right");
    getActionMap().put("move right", new MoveAction(1, 3));

    getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
            KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "move up");
    getActionMap().put("move up", new MoveAction(0, 3));

    getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
            KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "move down");
    getActionMap().put("move down", new MoveAction(2, 3));
}

public void paintComponent(Graphics window){

    super.paintComponent(window);

    window.fillRect((int)box.getX(), (int)box.getY(), (int)box.getWidth(), (int)box.getHeight());
}

private class MoveAction extends AbstractAction{

    int direction;
    int speed;

    public MoveAction(int direction, int speed){

        this.direction = direction;
        this.speed = speed; 
    }

    public void actionPerformed(ActionEvent e){

        if(direction == 0){

            box.setLocation((int)box.getX(), (int)box.getY() - speed);

        }else if(direction == 1){

            box.setLocation((int)box.getX() - speed, (int)box.getY());

        }else if(direction == 2){

            box.setLocation((int)box.getX(), (int)box.getY() + speed);

        }else if(direction == 3){

            box.setLocation((int)box.getX() + speed, (int)box.getY());
        }

        repaint();
    }
}
}

Any Help would be much appreciated. Thank You.

Raywl
  • 17
  • 5
  • http://stackoverflow.com/questions/20584539/detect-a-key-being-held-down-in-java-7 Essentially you should have a second listener to detect when the key is released. – Phil Jan 17 '15 at 19:34

2 Answers2

0

To get a smooth movement, you need to create a thread that handles the movement.

Make a boolean variable for each key (w, a, s, d) and set it true when the key is pressed, and false when the key is released. Just a simple key listener. Dont forget to add the listener to the jframe and check if the jframe has the focus.

private class InputListener implements KeyListener{
    @Override
    public void keyPressed(KeyEvent pressed) {

        //arrow keys
        if(pressed.getKeyCode() == 37){
            arrowLeft = true;
        }else if(pressed.getKeyCode() == 38){
            arrowUp = true;
        }else if(pressed.getKeyCode() == 39){
            arrowRight = true;
        }else if(pressed.getKeyCode() == 40){
            arrowDown = true;
        }

        //System.out.println(pressed.getKeyCode());
    }
    @Override
    public void keyReleased(KeyEvent released) {
        if(released.getKeyCode() == 37){
            arrowLeft = false;
        }
        if(released.getKeyCode() == 38){
            arrowUp = false;
        }
        if(released.getKeyCode() == 39){
            arrowRight = false;
        }
        if(released.getKeyCode() == 40){
            arrowDown = false;
        }
    }
    @Override
    public void keyTyped(KeyEvent arg0) {}
}

I used this for movement with the arrow keys, so for wasd movement you need to change the key codes. Second thing you need is the background thread that moves the square if a key is pressed. It calculates the movement based on the speed and time passed so its totally smooth.

long currTime = 0;
long lastTime = 0;

new Thread() {
    @Override
      public void run() {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

        while (true) {
           currTime = System.currentTimeMillis();
           long time = currTime - lastTime;

           int movePixel = (int) (speed * time);

           if (movePixel < 1 ) {
                            currPlayerTime -= time;
           }else{
               if(arrowLeft){
                     //move left by movePixel pixel
               }else if(arrowRight){
                     //move right by movePixel pixel
               }
               if(arrowUp){

               }else if(arrowDown){

               }     
             }

            lastTime = currTime;
        }
    }
}.start();

This might not be the easiest and fastest solution but it definetly is the best :)

  • `but it definetly is the best` - don't use "magic numbers". Also don't use KeyListeners. Follow the link I provided for more information. – camickr Jan 17 '15 at 22:25
0

Is there a way to prevent to the hesitation of the movement.

You can use a Swing Timer to schedule the animation. Check out Motion Using the Keyboard for a working example.

camickr
  • 321,443
  • 19
  • 166
  • 288