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);
}
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 am making a game in java and I have added mouse input. Here is my code.
public class MouseInput implements MouseListener, MouseMotionListener {
public static boolean leftPressed;
public static boolean rightPressed;
public MouseInput(){
}
public void tick(){
if(leftPressed){
System.out.println("left pressed");
}
}
#Override
public void mousePressed(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1){
leftPressed = true;
}else if(e.getButton() == MouseEvent.BUTTON3){
rightPressed = true;
}
}
#Override
public void mouseReleased(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1)
leftPressed = false;
else if(e.getButton() == MouseEvent.BUTTON3)
rightPressed = false;
}
I removed all of the excess code that isn't involved in this question such as getters, setters and abstract methods.
When I run this and I click what I see is
left pressed
left pressed
left pressed
left pressed
left pressed
left pressed
several times.
This is because it is inside of the tick method, which updates 60 times per second. What can I change to the mousePressed and mouseReleased methods to only make it one
left pressed
Thanks a lot
What can I change to the mousePressed and mouseReleased methods to only make it one
fore the time being you can obviously move the sysout statement from the tick() method to mousePressed()
public void tick(){
if(leftPressed){
}
}
#Override
public void mousePressed(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON1){
leftPressed = true;
System.out.println("left pressed");
}else if(e.getButton() == MouseEvent.BUTTON3){
rightPressed = true;
}
}
Forethemore you should not repead the code in mousePressed() and mouseReleased() choose either one that fits your need better.
To avoid empty method implementations you may inherit from MouseAdapter which has empty methods implementations for several Mouse releted listeners
I have a game where you can attack. When the SPACE-key is pressed, the weapon moves down, and when it's released it should move up.
The problem is that on (certain) UNIX systems, the KeyReleased event is also triggered while holding the key down. I was working with a BitSet to keep track of which keys are pressed, but because the KeyReleased keeps firing, the weapon just moves up and down really fast on my Ubuntu-machine.
I can move with my two characters at the same time, but I cannot keep the weapon down while the spacebar is pressed.
(What really happens when holding the key for a few seconds is:
keyPressed - keyReleased - keyPressed - keyReleased - keyPressed - keyReleased - ....)
Here is a System.out from what happens, with the System.currentTimeMillis() before each action:
1368696607559 - Pressed
1368696608052 - Released
1368696608053 - Pressed
1368696608082 - Released
1368696608083 - Pressed
1368696608112 - Released
1368696608113 - Pressed
1368696608143 - Released
1368696608144 - Pressed
1368696608173 - Released
The code I'm using is:
//Inner Class
private class KeyDispatcher extends KeyAdapter implements ActionListener{
private BitSet keyBits = new BitSet(256);
private Timer timer = new Timer(20, this);
boolean player1Attack = false, player2Attack = false;
public KeyDispatcher(){
timer.start();
}
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
keyBits.set(keyCode);
}
#Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
keyBits.clear(keyCode);
if(player1Attack && !isKeyPressed(KeyEvent.VK_SPACE)){
controller.stopAttack(0);
player1Attack = false;
}
//player 2
...
}
public boolean isKeyPressed(final int keyCode) {
return keyBits.get(keyCode);
}
#Override
public void actionPerformed(ActionEvent e) {
if(isGameOver()){
timer.stop();
}else{
//player1
if(!player1Attack && isKeyPressed(KeyEvent.VK_RIGHT)){
controller.moveRight(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_UP)){
controller.moveUp(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_DOWN)){
controller.moveDown(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_LEFT)){
controller.moveLeft(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_NUMPAD0)){
controller.attack(0); player1Attack = true; update();
}
//player2
...
}
}
}
I also tried replacing it with KeyBinding, as these seem more recommended, but here the same 'problem' remains (the released action keeps firing while it's pressed)...
Action attack = new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e) {
controller.attack(0);
}
};
Action stopAttack = new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e) {
controller.stopAttack(0);
}
};
background.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "attack");
background.getActionMap().put("attack", attack);
background.getInputMap().put(KeyStroke.getKeyStroke("released SPACE"), "stopAttack");
background.getActionMap().put("stopAttack", stopAttack);
I have read multiple topics on this situation, but haven't been able to get it working.
Thanks in advance
So let's put an example, in one of those UBUNTU machines that you have a problem;
Let's say they press and hold a keyboard button for 1000 milliseconds.
According to your comments, this is what would happen on the time line:
0 ms - 10ms - 1000ms
keyPressed keyReleased keyReleased
So in these problematic computers, you have 1x keyPressed event, and 2x keyReleased events.
The second keyReleased event, being the bad guy.
I would do the following to "Patch" the problem (Until you properly identify the difference in those UBUNTU computers):
(Note I'm using System time, and not a fixed MS per second, since the KeyEvents happen on the UI thread and not a game or OpenGL thread)
//This isn't the 10ms of the example, but if this doesn't work, try increasing it a bit further or
//try meassuring the exact time of the "faulty/not wanted" first onKeyReleased event
final private static long KEY_RELEASE_TIME_LOCK_IN_MS = 100L;
private static long lastKeyRelease_TimeMillis;
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
keyBits.set(keyCode);
}
#Override
public void keyReleased(KeyEvent e) {
final long currentTimeMillis = System.currentTimeMillis();
if( (currentTimeMillis - lastKeyRelease_TimeMillis) <= KEY_RELEASE_TIME_LOCK_IN_MS){
//We just ignored the keyRelease!, we are not allowing keyReleases that fast!
return;
}
int keyCode = e.getKeyCode();
keyBits.clear(keyCode);
if(player1Attack && !isKeyPressed(KeyEvent.VK_SPACE)){
controller.stopAttack(0);
player1Attack = false;
}
lastKeyRelease_TimeMillis = System.currentTimeMillis();
//player 2
...
}
//Rockster., let me know if it worked out
I got it kind of working with keeping track of the times like Rockster suggested. Because the keyPressed is fired almost immediately after the keyReleased, you can check if:
keyPressedTime
When the application is busy (adding enemies to the field in my case), this sometimes failes because the keyPressed isn't fired immediately. But for this application this is not a big deal.
The code for those interested:
//Inner Class
private class KeyDispatcher extends KeyAdapter implements ActionListener{
private long p1AttackLastKeyRelease = 0, p1AttackLastKeyPressed = 0;
private BitSet keyBits = new BitSet(256);
private Timer timer = new Timer(20, this);
boolean player1Attack = false;
public KeyDispatcher(){
timer.start();
}
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_NUMPAD0){
p1AttackLastKeyPressed = System.currentTimeMillis();
}
keyBits.set(keyCode);
}
#Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_NUMPAD0){
p1AttackLastKeyRelease = System.currentTimeMillis();
}
keyBits.clear(keyCode);
}
public boolean isKeyPressed(final int keyCode) {
return keyBits.get(keyCode);
}
#Override
public void actionPerformed(ActionEvent e) {
if(isGameOver()){
timer.stop();
}else{
//release the key
if(p1AttackLastKeyPressed != 0 && p1AttackLastKeyPressed<p1AttackLastKeyRelease && player1Attack && !isKeyPressed(KeyEvent.VK_NUMPAD0)){
p1AttackLastKeyPressed = 0;
controller.stopAttack(0);
player1Attack = false;
}
//do the actions of the keys that are down
if(!player1Attack && isKeyPressed(KeyEvent.VK_RIGHT)){
controller.moveRight(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_UP)){
controller.moveUp(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_DOWN)){
controller.moveDown(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_LEFT)){
controller.moveLeft(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_NUMPAD0) && !isKeyPressed(KeyEvent.VK_UP) && !isKeyPressed(KeyEvent.VK_DOWN) && !isKeyPressed(KeyEvent.VK_LEFT) && !isKeyPressed(KeyEvent.VK_RIGHT)){
controller.attack(0); player1Attack = true; update();
}
}
}
}
This still isn't fully working like I said before, but good enough for this application (the players cannot get advantage out of it).
You should try using Key Bindings.
More information from docs here
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!
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.