I'm using KeyListener
s in my code (game or otherwise) as the way for my on-screen objects to react to user key input. Here is my code:
public class MyGame extends JFrame {
static int up = KeyEvent.VK_UP;
static int right = KeyEvent.VK_RIGHT;
static int down = KeyEvent.VK_DOWN;
static int left = KeyEvent.VK_LEFT;
static int fire = KeyEvent.VK_Q;
public MyGame() {
// Do all the layout management and what not...
JLabel obj1 = new JLabel();
JLabel obj2 = new JLabel();
obj1.addKeyListener(new MyKeyListener());
obj2.addKeyListener(new MyKeyListener());
add(obj1);
add(obj2);
// Do other GUI things...
}
static void move(int direction, Object source) {
// do something
}
static void fire(Object source) {
// do something
}
static void rebindKey(int newKey, String oldKey) {
// Depends on your GUI implementation.
// Detecting the new key by a KeyListener is the way to go this time.
if (oldKey.equals("up"))
up = newKey;
if (oldKey.equals("down"))
down = newKey;
// ...
}
public static void main(String[] args) {
new MyGame();
}
private static class MyKeyListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
Object source = e.getSource();
int action = e.getExtendedKeyCode();
/* Will not work if you want to allow rebinding keys since case variables must be constants.
switch (action) {
case up:
move(1, source);
case right:
move(2, source);
case down:
move(3, source);
case left:
move(4, source);
case fire:
fire(source);
...
}
*/
if (action == up)
move(1, source);
else if (action == right)
move(2, source);
else if (action == down)
move(3, source);
else if (action == left)
move(4, source);
else if (action == fire)
fire(source);
}
}
}
I have problems with the responsiveness:
- I need to click on the object for it to work.
- The response I get for pressing one of the keys is not how I wanted it to work - too responsive or too unresponsive.
Why does this happen and how do I fix this?
Note: this is not an answer, just a comment with too much code :-)
Getting keyStrokes via getKeyStroke(String) is the correct way - but needs careful reading of the api doc:
The last line should better be exact name, that is case matters: for the down key the exact key code name is
VK_DOWN
, so the parameter must be "DOWN" (not "Down" or any other variation of upper/lower case letters)Not entirely intuitive (read: had to dig a bit myself) is getting a KeyStroke to a modifier key. Even with proper spelling, the following will not work:
Deeper down in the awt event queue, a keyEvent for a single modifier key is created with itself as modifier. To bind to the control key, you need the stroke:
This answer explains and demonstrates how to use key bindings instead of key listeners for educational purpose. It is not
It is
Answer; Read the Swing tutorial on key bindings.
Well, the Swing tutorial explains that
The tutorial has a good section about it. Key bindings involve 2 objects
InputMap
andActionMap
.InputMap
maps a user input to an action name,ActionMap
maps an action name to anAction
. When the user presses a key, the input map is searched for the key and finds an action name, then the action map is searched for the action name and executes the action.Good question! You will see that this is one of the things that make key bindings more manageable (disable, rebind etc.).
No (the Swing tutorial has working examples).
Here is how to make a single key binding:
Note that there are 3
InputMap
s reacting to different focus states:WHEN_FOCUSED
, which is also the one used when no argument is supplied, is used when the component has focus. This is similar to the key listener case.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
is used when a focused component is inside a component which is registered to receive the action. If you have many crew members inside a spaceship and you want the spaceship to continue receiving input while any of the crew members has focus, use this.WHEN_IN_FOCUSED_WINDOW
is used when a component which is registered to receive the action is inside a focused component. If you have many tanks in a focused window and you want all of them to receive input at the same time, use this.The code presented in the question will look something like this assuming both objects are to be controlled at the same time:
You can see that separating the input map from the action map allow reusable code and better control of bindings. In addition, you can also control an Action directly if you need the functionality. For example:
See the Action tutorial for more information.
Good point. Technically you can do both, but you have to think what makes sense and what allows for easy management and reusable code. Here I assumed moving is similar for all directions and firing is different, so I chose this approach.
Yes, they have a similar function, but are more appropriate for use here. See their API for info and on how to create them.
Questions? Improvements? Suggestions? Leave a comment. Have a better answer? Post it.
Here is an easyway that would not require you to read hundreds of lines of code just learn a few lines long trick.
declare a new JLabel and add it to your JFrame (I didn't test it in other components)
The focus needs to stay on this for the keys to work though.
In constructor :
Use this method:
OLD METHOD:
KeyString must be written properly. It is not typesafe and you must consult the official list to learn what is the keyString(it is not an official term) for each button.
NEW METHOD
In this new method you can simply set it using
KeyEvent.VK_WHATEVER
EXAMPLE CALL:
Send an anonymous class (or use subclass) of AbstractAction. Override its
public void actionPerformed(ActionEvent e)
and make it do whatever you want the key to do.PROBLEM:
I couldn't get it running for VK_ALT_GRAPH.
does not make it work for me for some reason.