I've tried searching how to handle multiple key presses in java but all the solutions I've found are assuming that all keys are pressed.
I'm creating a very simple game where an image that I loaded will be moving along a canvas. i want it to move left, right, and jump by using the left, right, and up arrow keys. My problem is that, when I press the left and right keys, they work perfectly fine. But, when I press the up arrow key, it just jumps and stops moving all together.
I have an array list of all keys pressed and a run() method that checks all the keys pressed and does the specific job. But java only remembers the latest key that I pressed and it doesn't call the run() method once I press the up arrow key.
What do you suggest I do? I've tried threads but they mix up my code (or maybe I did something wrong?).
Here's my code:
public class Moving implements KeyListener {
ProgramClass bg; //this is another class
Set<Integer> pressed = new HashSet<Integer>();
public Moving (ProgramClass aa) {
bg = aa;
}
public void keyPressed (KeyEvent ae) {
pressed.add(ae.getKeyCode());
run();
}
public void keyTyped (KeyEvent ae) {
}
public void keyReleased (KeyEvent ae) {
pressed.remove (ae.getKeyCode());
if (bg.left==false) {
if (ae.getKeyCode()==KeyEvent.VK_UP) {
bg.yy -=40;
}
}
run();
}
public void run () {
if (bg.left==true) {
if (pressed.contains(KeyEvent.VK_A)) {}
if (pressed.contains(KeyEvent.VK_D)) {
if (bg.pic==true)
bg.pic = false;
else
bg.pic = true;
if (bg.x==-550)
bg.x = -2;
else
bg.x -= 2;
}
}
if (bg.left==false) {
if (pressed.contains(KeyEvent.VK_LEFT)) {
if (bg.pic==true)
bg.pic = false;
else
bg.pic = true;
if (bg.x==550)
bg.x = 2;
else
bg.x += 2;
}
if (pressed.contains(KeyEvent.VK_RIGHT)) {}
if (pressed.contains(KeyEvent.VK_UP)) {
bg.yy -=40;
}
}
}
}
Thanks!
Related
So I am basically creating a platformer game in java. Here is a simple question. Can I respond to two different KeyEvents simultaneously? For example, I am pressing the right arrow key and my player is moving right. Now, I want my player to jump but keep moving right. For this I would need to respond to two different KeyEvents simultaneously. How do I achieve this? I have not tried any code yet because I don't know where to start with. Thank you for the answer in advance.
You can use seperate "KeyDown" and "KeyUp"-events and set state values for your player.
You need a State-Machine for your player.
public class KeyboardListener extends JFrame implements KeyListener {
public KeyboardListener() {
addKeyListener(this);
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
System.out.println("Right key pressed");
}
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
System.out.println("Left key pressed");
}
}
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
System.out.println("Right key Released");
}
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
System.out.println("Left key Released");
}
}
public static void main(String[] args) {
new KeyboardListener();
}
}
Use the keyPressed and keyReleased-EventHandlers from the KeyListener Interface and save your current player states like walking, jumping, walk_left, walk_right etc as booleans for example.
In your mainloop you can check what state your player is in and change its coordinates. Like:
if(player.left) { player.x += speed } //etc.
You might want to use Threads and then add pressed keys to a list the player physics class.
public class KeysListener implements KeyListener
...
//FIRST THREAD
new Thread(new Runnable() {
#Override
public void run() {
public void keyEvent(KeyEvent e) {
int key = e.getKeyCode();
//TODO ADD KEY TO A LIST IN THE CLASS THAT HANDLES YOUR PHYSYICS
}
}
}).start();
//SECOND THREAD
new Thread(new Runnable() {
#Override
public void run() {
public void keyEvent(KeyEvent e) {
int key = e.getKeyCode();
//TODO ADD KEY TO A LIST IN THE CLASS THAT HANDLES YOUR PHYSICS
}
}
}).start();
...
}
Add as many threads as the number of simultaneous keys you require.
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.
I hope I can describe my problem in the right way. I am programming a simple Twin-Stick Shooter at the moment so I implemented a KeyInputHandler calss that implements KeyListener. But when the player pressed 2 buttons at the same time I ran into a problem. It took always a second before the player actually moves. As far as I know that is because of the key repeat. I might be wrong. I googled a bit and found a possible solution here:
https://gamedev.stackexchange.com/questions/51623/smoother-controls?rq=1
The people suggested to not change the x- and y-velocity of the player on key press but instead flag the direction when the player presses a certain button and handle the movement in a different method. So I created the boolean variables and activated them when the button is pressed:
public class KeyInputHandler implements KeyListener{
private Game game;
// checks if A key is down
public boolean left_key_down = false;
// checks if D key is down
public boolean right_key_down = false;
// checks if W key is down
public boolean up_key_down = false;
// checks if S key is down
public boolean down_key_down = false;
public KeyInputHandler(Game game) {
this.game = game;
}
public void keyPressed(KeyEvent e) {
int keys = e.getKeyCode();
if (keys == KeyEvent.VK_D) {
right_key_down = true;
} else if (keys == KeyEvent.VK_A) {
left_key_down = true;
} else if (keys == KeyEvent.VK_W) {
up_key_down = true;
} else if (keys == KeyEvent.VK_S) {
down_key_down = true;
}
}
public void keyReleased(KeyEvent e) {
int keys = e.getKeyCode();
if (keys == KeyEvent.VK_D) {
right_key_down = false;
} else if (keys == KeyEvent.VK_A) {
left_key_down = false;
} else if (keys == KeyEvent.VK_W) {
up_key_down = false;
} else if (keys == KeyEvent.VK_S) {
down_key_down = false;
}
}
public void keyTyped(KeyEvent e) {
}
public void tick() {
if (left_key_down) {
game.p.setVelX(-1);
} else if (right_key_down) {
game.p.setVelX(1);
} else if (up_key_down) {
game.p.setVelY(-1);
} else if (down_key_down) {
game.p.setVelY(1);
} else {
game.p.setVelY(0);
game.p.setVelX(0);
}
}
}
(The tick() method is called in the main game loop)
The problem is now that there is no delay anymore (yay), but the things get strange when I press 2 buttons together. Example:
I press the left Button
I press the down Button
I let the down Button go -> Objects still goes down-left
I press up -> Object goes up-left
But when I hold Right and then als hold Down it is just moving down.
I also checked the boolean variables and they are getting refreshed correctly.
This is kinda weird and I am not really sure how to handle this and what exactly is causing this behaviour. I have the assumption that it has something to do with the order in which the cooleans are checked. Maybe someon can help me on this matter. I would greatly appreciate it. :)
The problem why things are getting strange in multiple key presses together is that you are using if else's in checking the booleans. Separete all the boolean checks in tick() to own individual ifs. If any one of the buttons are being pressed constantly, the code never goes to the last else-block where VelX and VelY are reset to zero.
Change the code in tick method to something like this:
//Set 'velocity' to zero first
game.p.setVelY(0);
game.p.setVelX(0);
if (left_key_down) {
game.p.setVelX(-1);
}
if (right_key_down) {
game.p.setVelX(1);
}
if (up_key_down) {
game.p.setVelY(-1);
}
if (down_key_down) {
game.p.setVelY(1);
}
Ok, how to i move from keyboard a ball using an Applet?
I have so far this code, that don't do anything.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class KeyboardGame extends Applet implements KeyListener
{
private static final long serialVersionUID = 1L;
private static boolean keyboadrRightPressed = false;
public void init()
{
addKeyListener(this);
}
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_RIGHT)
{
keyboadrRightPressed = true;
}
else
{
keyboadrRightPressed = false;
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public void paint(Graphics g)
{
g.fillOval(20,20,20,20);
g.drawString("String :"+keyboadrRightPressed,20,30);
}
}
And also i have to understand how it works. I don't get why my action listener won't work, do i need an
while(true)
or an Thread?
Your action listener might actually be working fine, but you need to repaint the applet when the key is pressed so that your string actually appears. Try changing the keyPressed to this:
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_RIGHT)
{
keyboadrRightPressed = true;
}
else
{
keyboadrRightPressed = false;
}
repaint();
}
Actually moving the ball will differ depending on how you want the ball to actually move. I'm guessing you want it to continue moving right while the key is held down, so what I would do is implement a timer or some other form of thread that every .25 seconds (or however long you want) checks the keyboardRightPressed and will move the ball right if it is true. Then in the keyReleased portion of your code you should also add logic to set keyboardRightPressed back to false when you let up on the key.
I've added a keylistener to my JTextArea field, but it doesn't behave as I expected.
inputTextArea.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent k) {
//If the return button is hit, only set to a new line if shift is also down.
if(k.getKeyChar() == KeyEvent.VK_ENTER) {
if(k.isShiftDown()) {
inputTextArea.append(" \n");
} else {
//Send The Message...
boolean cleanTextField = false;
try {
sendMessage(inputTextArea.getText());
cleanTextField = true;
msgScrollPane.setAutoscrolls(true);
JScrollBar vbar = msgScrollPane.getVerticalScrollBar();
if ((vbar.getValue() + vbar.getVisibleAmount()) == vbar.getMaximum()) {
msgPane.setCaretPosition(msgDoc.getLength());
}
} catch (Exception ex) {
ex.printStackTrace();
cleanTextField = false;
} finally {
if(cleanTextField) {
inputTextArea.setText("");
}
}
}
}
}
});
I want this:
- If the return button is hit and shift is down: add a new line.
- If the return button is hit and the shift button isn't down: no new line, but submit.
Now it behaves like this:
- If I hit the return button and shift is down: no line added. Nothing happens.
- If I hit the return button and shift isn't down: submitted, but if I start typing again it begins on new line.
Does someone know how to do what I want?
EDIT:
I tried some other code to detect if the shift button is down:
if((k.getModifiersEx() == KeyEvent.SHIFT_DOWN_MASK) ||
(k.getModifiers() == KeyEvent.SHIFT_DOWN_MASK)) {
This doesn't work as well
You may use the InputMap and ActionMap of the JTextArea to map the key strokes to actions:
private static final String TEXT_SUBMIT = "text-submit";
private static final String INSERT_BREAK = "insert-break";
...
private void initialize() {
InputMap input = inputTextArea.getInputMap();
KeyStroke enter = KeyStroke.getKeyStroke("ENTER");
KeyStroke shiftEnter = KeyStroke.getKeyStroke("shift ENTER");
input.put(shiftEnter, INSERT_BREAK); // input.get(enter)) = "insert-break"
input.put(enter, TEXT_SUBMIT);
ActionMap actions = inputTextArea.getActionMap();
actions.put(TEXT_SUBMIT, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
submitText();
}
});
}
...
private void submitText() {
// TODO
}
The original action for ENTER - "insert-break" - is used for shift ENTER.
Try using keyTyped and not keyPressed. I beleive keyPressed gives you an event for the shift and for the enter, whereas keyTyped gives you one combined event with a modifier.
Instead of doing the actions immediately on receiving the event, sequence them for later by posting them using SwingUtilities.invokeLater(). The code should look like:
if(k.isShiftDown()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
inputTextArea.append(" \n");
}
});
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//rest of the else body here
}
});
}
In my opinion, the problems seen here are because application-defined actions and internal actions are not being properly sequenced, leading to repaints happening before the text has been modified.