1

People here keep suggesting to me to use Key Bindings in my Java 2D games instead of Key Listener.

So I learned it and I'd like to know wether I understand correctly how to use it.

Let's say I have a game, with two playes, each player can push 5 buttons.

Player 1:

  • UP arrow - move forward
  • LEFT arrow - change angle of movement
  • RIGHT arrow - change angle of movement
  • SPACE key - fire missile
  • L key - fire secondary missile

Player 2:

  • W key - move forward
  • A key - change angle of movement
  • D key - change angle of movement
  • CAPS-LOCK key - fire missile
  • 'Z' key - fire secondary missile

If I want the program to react differently to each one of the different key-presses, than this is what I have to do: (?)

  1. Create 10 new nested classes extending AbstractAction, inside the class that runs most of the game logic.
  2. Create an instance of every one of these 10 new classes, and bind each one to a key.

Is this correct? Is it really logical to create 10 new classes only for pushing buttons? I want to know if I understand correctly how to use Key Bindings, so I can start programming with it.

Thanks

vahid abdi
  • 9,636
  • 4
  • 29
  • 35
user3150201
  • 1,901
  • 5
  • 26
  • 29

3 Answers3

3

"Is this correct? Is it really logical to create 10 new classes only for pushing buttons?"

The answer is YES. You do need to create 10 different instances. It's not difficult. You can either create a helper class or you can just copy and paste something like this

Action leftAction = new AbstractAction(){
    public void actionPerformed(ActionEvent e){
        // do something when left button pressed;
    }
};

InputMap inputMap = panel.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = panel.getActionMap();

inputMap.put(KeyStroke.getKeyStroke("LEFT"), "leftAction");
actionMap.put("leftAction", leftAction);  <----

For each different action, just copy and paste the Action code, change the variable, and the action to perform, and input into the InputMap and the ActionMap accordingly.

I use both ways for different scenarios. For graphics i prefer the above way. For things like menus, I tend to use a separate class

Take a look at this example

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • I understand I need to create 10 different instances. But do I need to create 10 different *classes*? (each one with an instance?) – user3150201 Jan 02 '14 at 18:02
  • What you wrote above: What does it do? Does it create a new class named leftAction, the extends AbstractAction? Or does it create a new instance of AbstractAction, and then overrides actionPerformed() for that specific instance? – user3150201 Jan 02 '14 at 18:05
  • Well technically with the code above, you _are_ creating an annonymous class. you would have to repeat the the top part of my code 10 times for tem different actions. – Paul Samsotha Jan 02 '14 at 18:05
  • It's what you can an anonymous class. Yes the `leftAction` is the varible of the class. `AbstractAction` is a class that _need_ to override the `actionPerformed`, just like an ActionListener class – Paul Samsotha Jan 02 '14 at 18:07
  • So to answer my question in the second comment: What you wrote above equals to 'creating a new class named leftAction, that extends AbstractAction'? Or is it just another instance of AbstractAction? – user3150201 Jan 02 '14 at 18:07
  • It's creating a whole new class just in a shorter way. You cant create an instance of Abstract action. You have to extend it. So the above is kinda like saying `class leftAction extends AbstractAction`. So you really are creating a class, not instantiating one. You can look more into anonymous classes if you're really interested. That's what you would call `leftAction` – Paul Samsotha Jan 02 '14 at 18:10
  • When using what you wrote inside another method, say a constructor. I need a semicolon after the last '}', correct? I get an error if I don't. – user3150201 Jan 02 '14 at 18:14
  • Yes you do need a semi-colon. Sorry, I forgot that – Paul Samsotha Jan 02 '14 at 18:16
  • By the way you got another small error. I suggest you fix it so people in the futue looking for help will get the right code :) This: inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, "leftAction"); Should be this: inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT), "leftAction"); (Or am I wrong?) – user3150201 Jan 02 '14 at 18:29
  • ALso, last question. You said the code above creates an anonymous class. But in the same time, it creates an instance of that class. Am I correct? – user3150201 Jan 02 '14 at 18:33
  • Not exactly, `AbstractAction` is an abstract class which can't be instantiated. Only extended. Try it `Action action = new AbstractAction();`. It won't work – Paul Samsotha Jan 02 '14 at 18:35
  • So how can you refer to the instance leftAction if it hasn't been created? Or are you refering to a class, not an instance? Also, sorry to bother you, but VK_RIGHT doesn't work. However RIGHT does. (Might want to change that for people looking here in the future) – user3150201 Jan 02 '14 at 18:42
  • Take a look at [Anonymous classes](http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html). [The really big Index](http://docs.oracle.com/javase/tutorial/reallybigindex.html) can answer a lot of question you have. – Paul Samsotha Jan 02 '14 at 18:52
2
public TestKeyBindings02() {
    JPanel panel = new JPanel();
    InputMap im = panel.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
    ActionMap am = panel.getActionMap();

    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "RightArrow");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "LeftArrow");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "UpArrow");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "DownArrow");

    am.put("RightArrow", new ArrowAction("RightArrow"));
    am.put("LeftArrow", new ArrowAction("LeftArrow"));
    am.put("UpArrow", new ArrowAction("UpArrow"));
    am.put("DownArrow", new ArrowAction("DownArrow"));
}

public class ArrowAction extends AbstractAction {

    private String cmd;

    public ArrowAction(String cmd) {
        this.cmd = cmd;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (cmd.equalsIgnoreCase("LeftArrow")) {
            System.out.println("The left arrow was pressed!");
        } else if (cmd.equalsIgnoreCase("RightArrow")) {
            System.out.println("The right arrow was pressed!");
        } else if (cmd.equalsIgnoreCase("UpArrow")) {
            System.out.println("The up arrow was pressed!");
        } else if (cmd.equalsIgnoreCase("DownArrow")) {
            System.out.println("The down arrow was pressed!");
        }
    }
}

Taken from here

Community
  • 1
  • 1
thifofdeath
  • 75
  • 2
  • 11
1

Personaly, when I was using keyBindings I would create an inner class extending abstractAction for each action, then create an instance of this inner class and bind it to a key like put("left", ActionLeft);, for example. So basically I did the two steps you've described and that worked.

You could also just create instances of anonymus classes and then bind those instances just the same way.

3yakuya
  • 2,622
  • 4
  • 25
  • 40