0

I'm trying to make a program that moves a rectangle around the screen when the arrow keys are pressed. At the moment I'm working on keybindings, after KeyListeners proved to be unreliable and fairly useless. Before trying to make the rectangle move, I'm just trying to make the press of the Up arrow key trigger System.out.println("Up key pressed!"), simply to make sure my KeyBindings are actually working. The problem is, they aren't. I'm following this tutorial, which is a little different from what I'm trying to achieve but should still teach me how to use key bindings. Why does the KeyBinding not work?

Code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;


public class GamePanel extends JPanel implements Runnable{

    boolean isRunning = true;
    int count = 0;
    Thread t;
    static int rectX = 250;
    static int rectY = 250;
    Action upAction;
    Action downAction;
    Action leftAction;
    Action rightAction;

    public GamePanel()
    {
        upAction = new UpAction();
        t = new Thread(this);
        t.run();
        /*downAction = new DownAction();
        leftAction = new LeftAction();
        rightAction = new RightAction();*/
        this.getInputMap().put(KeyStroke.getKeyStroke("UP"), "upMotion");
        this.getActionMap().put("upMotion",upAction);

    }
    public void run()
    {
        loop();
    }
    public void loop()
    {
        if(isRunning)
        {
            Thread t = Thread.currentThread();
            try
            {
            t.sleep(5);
            }
            catch(InterruptedException e)
            {
                e.printStackTrace();
            }
            repaint();
        }

    }
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        count += 1;
        g.drawString(Integer.toString(count), 10, 10);
        g.drawRect(rectX, rectY, 50, 50);
        loop();
    }
    static class UpAction extends AbstractAction
    {
        public void actionPerformed(ActionEvent e)
        {
            System.out.println("Up key pressed!");
            rectY++;
        }
    }
}

Main JFrame code:

import javax.swing.*;

public class MainFrame{

    JFrame frame = new JFrame("Space Invaders");
    GamePanel gamepanel = new GamePanel();

    public MainFrame()
    {
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setSize(500,500);
        frame.setLocationRelativeTo(null);
        frame.add(gamepanel);
    }
    public static void main(String[] args)
    {
        new MainFrame();
    }


}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
imulsion
  • 8,820
  • 20
  • 54
  • 84
  • where is your main? cant run this code. – kai Jul 28 '14 at 10:58
  • @kai My main code simply makes a JFrame and adds this class to it as a JPanel – imulsion Jul 28 '14 at 10:59
  • That loop is *bad*. It will halt the current thread which called the constructor, so you can't have a running Panel anywhere (except if you export `this` from constructor). Try running the `loop` method in a new Thread. – kajacx Jul 28 '14 at 11:00
  • add your main code, so i can run it. – kai Jul 28 '14 at 11:00
  • Is the frame even visible? you create a `new GamePanel();` in your frame (and this initialization will be called just before frame constructor) but your GamePanel constructor halts the current thread, so you should't see anything. Am i right? – kajacx Jul 28 '14 at 11:06
  • 1
    @kajacx I've edited my code. And no, even before I edited I still saw a rapidly increasing number and a rectangle be drawn – imulsion Jul 28 '14 at 11:07
  • 1. Thread.sleep(5); is under latency for most of todays Native OS's, 2. Thread.sleep(5); block EDT until if(isRunning) ended – mKorbel Jul 28 '14 at 11:09
  • @mKorbel I don't understand. Can you explain further? – imulsion Jul 28 '14 at 11:10
  • loop(); is invoked from 1. all mouse and key events, 2. methods implemented in API, 3. most important from endless cloning invoked by repaint() inside if(isRunning) – mKorbel Jul 28 '14 at 11:10
  • @mKorbel Yes I want it to be endlessly invoked; that works as a game loop – imulsion Jul 28 '14 at 11:11
  • short answer 1. use Swing Timer, forgot for idea to call recrusive loop(); == creating endless, un_manageble loop, 3. delay (minimal value) should be 25/33/50 miliseconds, depends of Native OS and GPU – mKorbel Jul 28 '14 at 11:14
  • Yes I want it to be endlessly invoked; == for this reason is there loop, but calling loop(); from public void paintComponent(Graphics g) multiply this crazy idea :-) – mKorbel Jul 28 '14 at 11:15
  • @mKorbel Changing the delay to 25 worked! Thank you! – imulsion Jul 28 '14 at 11:22
  • @mKorbel And...after I ran it and then closed it, I ran it again and it stopped working. – imulsion Jul 28 '14 at 11:24
  • similair question are asked 3-5 times per day, you can increasing your chances by click to paintComponent or KeyBindings tag – mKorbel Jul 28 '14 at 11:26

1 Answers1

1

The whole problem was that your panel didn't have focus, and thusly couldn't recieve any input events. After following this answer, adding 2 lines solved the problem:

public GamePanel() {
    ...
    setFocusable(true); //add this anywhere in this constructor 
}

public MainFrame() {
    ...
    frame.add(gamepanel);
    gamepanel.requestFocusInWindow();
    //add this after adding the panel to your frame and making it visible
}

EDIT:

Also, you code has several bugs:

  • Moving up should be y--, since top-left corner has coordinates [0,0] and the x axis is going to right and y is going down (unlike in math class)
  • You should never call Thread.sleep() inside paint() method, since that method has to be as fast as possible.
  • You are "starting" a new thread using thread.run(), use thread.start() instead, that will acctually start a new thread instead of just calling the run method in current thread.
  • Change the if to while in your loop method, also remove the loop call from the paint method to solve previous two mistakes.

These are not to criticise you (everyone was starting at some point) but to help you.

Community
  • 1
  • 1
kajacx
  • 12,361
  • 5
  • 43
  • 70
  • After mKorbel's suggestion of changing the Thread.sleep() delay to 25, the Keybindings worked without this. They are however inconsistent – imulsion Jul 28 '14 at 11:27
  • Do I have to use `public void start(){...}` or will the call to `run()` start the thread? – imulsion Jul 28 '14 at 11:37
  • Google "java thread run vs start" and pick any link you want... :3 – kajacx Jul 28 '14 at 11:44
  • KeyBindings has setting for Focus implemented in API by default, then everything about Focus aren't answer to OPs question – mKorbel Jul 28 '14 at 11:46
  • 1
    Thanks for your help. I can move a rectangle around a screen now!! *Achievement intensifies* – imulsion Jul 28 '14 at 11:46