I am making a game, and when a user presses a key it works when they press another key the previous key stops working, and the newly pressed key takes over. Using the following blocks as examples, is this a Java thing, or does it have to do with my code?
keyboard.setEvent("LEFT", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent evt){
// Moves an object left
}
});
keyboard.setEvent("SPACE", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent evt){
// Creates and object and moves it upwards
}
});
This method (called above) creates the events.
public void setEvent(String key, AbstractAction act){
// Key Pressed
comp.
getInputMap(Room.WHEN_IN_FOCUSED_WINDOW).
put(KeyStroke.getKeyStroke(key), "do" + key + "Action");
comp.
getActionMap().
put("do" + key + "Action", act);
}
Related
I need to print something in the method on every key press event. I have tried the below code and the problem with that is, first key press in always returning null. Whereas, after typing the second letter, it prints the first key event. Key Press event is not capturing the letter on first event. Can you please help in resolving this ?
final StringComboBox searchGridTextBox = new StringComboBox();
searchGridTextBox.setEmptyText("Search Grid");
searchGridTextBox.addFocusHandler(new FocusHandler(){
#Override
public void onFocus(FocusEvent event){
if(searchGridTextBox.getStore().size() > 0)
searchGridTextBox.expand();
}
});
searchGridTextBox.addKeyPressHandler(new KeyPressHandler() {
#Override
public void onKeyPress(KeyPressEvent event) {
System.out.println("On key press event ") ;
}
});
For this scenario, you need to use KeyUpEvent. Please find the updated code below.
final StringComboBox searchGridTextBox = new StringComboBox();
searchGridTextBox.setEmptyText("Search Grid");
searchGridTextBox.addFocusHandler(new FocusHandler(){
#Override
public void onFocus(FocusEvent event){
if(searchGridTextBox.getStore().size() > 0)
searchGridTextBox.expand();
}
});
searchGridTextBox.addKeyUpHandler(new KeyUpHandler() {
#Override
public void onKeyUp(KeyUpEvent event) {
System.out.println("On key up event ") ;
}
});
There are 2 more handlers available keyUp and keyDown handler. Try using keyUp/keyDown handler and see if it fulfills your requirements.
There is a difference how keyPress behaves in a case of empty combo box which is explained in this post:
https://stackoverflow.com/a/42036960/3612019
I'm currently working on a little game. I'm using getKeyCode to move my character but the thing is that I don't want you to be able to keep moving if you hold in the button. Is there anyway I can use getKeyCode to only register on the first click and then won't register until I release the button and press again?
else if (event.getKeyCode()== KeyEvent.VK_UP)
{
spelare1.setLocation(spelare1.getX(),spelare1.getY()-50);
}
This is how it currently looks like.
I think you are confusing KeyEvents. VK_UP is the up arrow key. Use KeyEvent.KEY_RELEASED to react on a released key.
You can keep a boolean indicating whether the key is currently pressed. Then you can react once after each press, like in this example:
public static void main(String[] args) {
JFrame f = new JFrame();
f.addKeyListener(new KeyAdapter() {
private boolean pressed;
#Override
public void keyReleased(KeyEvent e) {
pressed = false;
}
#Override
public void keyPressed(KeyEvent e) {
if (!pressed) {
System.out.println("Key pressed: " + e.getKeyCode());
pressed = true;
}
}
});
SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.setVisible(true);
}
});
}
Depending on your needs, you might want to have a separate state for each key, or just one shared state.
In this code I have a graphic (paintcomponent graphic) that has two tanks. The paintcomponent is controlled by variables and then a repaint(). Right now, it works perfectly when only one button is being pressed or held down. However, if I hold the left for tank1 and the right for tank2, for example, the last-most key is the one that "wins": It is the only direction that occurs.
In my code you will find about 4 of my latest attempts kind of ‘iteratingly’ added to the code.
The basic setup is: Keybinding→AbstractAction→...
The setup above without ellipses(…) represents the original attempt. Then, in another attempt, this is chained to a thread. Same result. In another attempt the abstract action is set to change an array value, which is connected to a timer that checks every 20 seconds. The two attempts branching from there is the connection of the timer to the thread, and the directly repaint put in the timer.
My only other option at this point is to make separate key bindings for each combination (i.e., holding left and D [D moves tank1] would be a keybinding with the keycode "LEFT D" and thus would be a separate event that moved both). However, this would be a hassle because I also have turrets that change angles, and bullets that fire… Also I'd like to think there's a more decent way to do this.
Any help will be appreciated. The code is below.
class OneMoveRightThread implements Runnable { //This is a thread that contains code to 'move' the tank1.
public void run(){
tankGraphic.TankOneMoveRight();
gameArea.repaint();
}
}
class OneMoveLeftThread implements Runnable { //thread to move tank2
public void run(){
tankGraphic.TankOneMoveLeft();
gameArea.repaint();
}
}
//Meow is a [0,0,0,0,0,0,0,0] (8 zeroes) array. When a button is clicked the value is set to 1 for the spot in the array.
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (meow[0] == 1){ //This attempt uses a timer to constantly check the array for potential movement. Still works for 1 movement at a time.
(new Thread(new OneMoveLeftThread())).start();
meow[0]=0;
}
if(meow[1]==1){
(new Thread(new OneMoveRightThread())).start(); //using code in threads by calling the thread. My hope was that this would fix it.
meow[1]=0;
}
if (meow[2]==1){ //TANK TWO MOVE LEFT using code in threads
tankGraphic.TankTwoMoveLeft();
gameArea.repaint();
meow[2]=0;
}
}
};
new Timer(delay, taskPerformer).start();
//This abstract action is a different part of the code.
Action doNothing = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
lifeBar1.setText("\n Player 1: " + player1Name + " \n Health: " + player1Health + " \n Bullets: A LOT!");
lifeBar2.setText("\n Player 2: " + player2Name + " \n Health: " + player2Health + " \n Bullets: A LOT!");
}
};
//TANK TWO BELOW
Action leftMove = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) { //This is code for "When LEFT arrow is pressed, set meow[2]=1"
//tankGraphic.TankTwoMoveLeft();
//gameArea.repaint();
meow[2]=1;
}
};
Action rightMove = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) { //This is code for when RIGHT arrow is pressed, move tank directly. This and the above have same erroneous result.
tankGraphic.TankTwoMoveRight();
gameArea.repaint();
}
};
Action angleLEFT = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
tankGraphic.TankTwoAngleLeft();
gameArea.repaint();
}
};
Action angleRIGHT = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
tankGraphic.TankTwoAngleRight();
gameArea.repaint();
}
};
//TANK ONE BELOW
Action leftMove2 = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
meow[0]=1;
}
};
Action rightMove2 = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
//(new Thread(new OneMoveRightThread())).start();
meow[1]=1;
}
};
Action angleLEFT2 = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
tankGraphic.TankOneAngleLeft();
gameArea.repaint();
}
};
Action angleRIGHT2 = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
tankGraphic.TankOneAngleRight();
gameArea.repaint();
}
};
//these two lines are irrelevant. They match the irrelevant abstractaction.
gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("B"), "doNothing");
gameArea.getActionMap().put("doNothing",doNothing);
//below is the code for the keybindings.
//the names are reversed, below. (fire1 corresponds to tank two, but the right side of the keyboard)
gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("LEFT"), "leftMove");
gameArea.getActionMap().put("leftMove",leftMove);
gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("RIGHT"), "rightMove");
gameArea.getActionMap().put("rightMove",rightMove);
gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("UP"), "angleLEFT");
gameArea.getActionMap().put("angleLEFT",angleLEFT);
gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("DOWN"), "angleRIGHT");
gameArea.getActionMap().put("angleRIGHT",angleRIGHT);
gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("A"), "leftMove2");
gameArea.getActionMap().put("leftMove2",leftMove2);
gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"), "rightMove2");
gameArea.getActionMap().put("rightMove2",rightMove2);
gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("W"), "angleLEFT2");
gameArea.getActionMap().put("angleLEFT2",angleLEFT2);
gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("S"), "angleRIGHT2");
gameArea.getActionMap().put("angleRIGHT2",angleRIGHT2);
//gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("E"), "fire2");
//gameArea.getActionMap().put("fire2",fire2);
//gameArea.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("SLASH"), "fire1");
//gameArea.getActionMap().put("fire1",fire1);
frame.requestFocus();
Take a look at Motion Using the Keyboard.
The KeyboardAnimation.java example does this. The class shows one way to add animation using Key Bindings and a Swing Timer. Each key binding can control a different image and a different Timer.
One image is controlled by the 4 arrow keys and the other by the "a, d, w, s" keys.
I'm using this code to bind keyboard keys to custom actions without using the KeyListener:
Action left = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("pressed left key");
}
};
Action right = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("pressed right key");
}
};
Action space = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("pressed space key");
}
};
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("LEFT"), "pressedLeft");
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("A"), "pressedLeft");
myJPanel.getActionMap().put("pressedLeft", left);
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("RIGHT"), "pressedRight");
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("D"), "pressedRight");
myJPanel.getActionMap().put("pressedRight", right);
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "pressedSpace");
myJPanel.getActionMap().put("pressedSpace", space);
Everything works perfectly, but i noticed that when i press i.e. SPACE while holding A, the left action isn't fired anymore, it would be great if events for both pressed keys are fired.
Is there any way to use key bindings with key combinations?
See Motion Using the KeyBoard for a potential solution.
An event is only generated for the last key pressed so you need to manually keep track of any other keys that have been pressed (and keep manually simulate firing the event). This is true whether you use key bindings or a KeyListener.
I'm making a program in Java, using Swing, with a GUI that contains arrow keys. The arrow keys correspond to the arrow keys on the keyboard.
When I press the up arrow key on the keyboard, I'd like the up arrow key on the GUI to show up as being pressed. Until I release the arrow key, it should show it is still being pressed, and when released it should also release.
A snippet of my code so far (only for the Up button), which I think is totally wrong in the show being pressed category:
...
if (e.getKeyCode() == KeyEvent.VK_UP) {
actionArrowUp();
JButton buttonUp = (JButton) mainTab.getComponent(4);
buttonUp.setSelected(true);
}
...
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP)
actionArrowUpRelease();
buttonUp.setSelected(true);
Using keyBindings (as #trashgod already mentioned) is the way to go. To get the exact same visual behaviour as if activating the button by space/enter (when it were focused)
implement actions that delegate to the button's default actions registered for pressed/released
needs binding to both pressed and released of the key to simulate
install the binding to the buttons's parent in its inputMap of type WHEN_ANCESTOR
In code:
// the delegating action
public static class SimulateButtonAction extends AbstractAction {
AbstractButton button;
public SimulateButtonAction(AbstractButton model, String fire) {
super(fire);
this.button = model;
}
#Override
public void actionPerformed(ActionEvent e) {
Action delegate = button.getActionMap().get(getName());
delegate.actionPerformed(new ActionEvent(button,
ActionEvent.ACTION_PERFORMED, getName()));
}
public String getName() {
return (String) getValue(Action.NAME);
}
}
// example usage
JComponent content = new JPanel(new GridLayout(0, 5));
Action log = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("triggered: " + ((AbstractButton) e.getSource()).getText());
}
};
String pressed = "pressed";
String released = "released";
ActionMap actionMap = content.getActionMap();
InputMap inputMap = content.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
String[] arrows = {"UP", "DOWN", "LEFT", "RIGHT"};
for (int i = 0; i < arrows.length; i++) {
JButton button = new JButton(log);
button.setAction(log);
button.setText(arrows[i]);
content.add(button);
// simulate pressed
String pressedKey = pressed + arrows[i];
inputMap.put(KeyStroke.getKeyStroke(arrows[i]), pressedKey);
actionMap.put(pressedKey, new SimulateButtonAction(button, pressed));
String releasedKey = released + arrows[i];
inputMap.put(KeyStroke.getKeyStroke(released + " " +arrows[i]), releasedKey);
actionMap.put(releasedKey, new SimulateButtonAction(button, released));
}
This LinePanel uses Key Bindings and invokes doClick() in actionPerformed() to achieve an effect similar to the one you describe.
Addendum: As you want the button to appear pressed while the key is pressed, you may be able to use the optional onKeyReleased parameter of KeyStroke.getKeyStroke(). As described in ButtonModel, you'll need to make the model both armed and pressed to simulate a mouse down in the button.