Problems with key event handling

Hey guys,

So I’ve been switching from polling to interrupting for my keyboard input handling and I’ve run into a quirk with KeyEvent. These are my keyPressed and keyReleased methods defined in my KeyListener:

public void keyPressed(KeyEvent e) {
		System.out.println(e.getKeyCode());
		KeyPressedEvent event = new KeyPressedEvent(e.getKeyCode(), Event.Type.KEY_PRESSED);
		Engine.onEvent(event);
	}

	public void keyReleased(KeyEvent e) {
		KeyReleasedEvent event = new KeyReleasedEvent(e.getKeyCode(), Event.Type.KEY_RELEASED);
		Engine.onEvent(event);
	}

I’ve coded up a rough event listener to do some preliminary testing with this implementation:

package breakit.mob.player;

import breakit.mob.Mob;
import engine.event.Event;
import engine.event.EventDispatcher;
import engine.event.EventListener;
import engine.event.keyboard.KeyPressedEvent;
import engine.event.keyboard.KeyReleasedEvent;

public class PlayerKeyListener implements EventListener {

	private Player player = null;
	
	public void init(Player player) {
		this.player = player;
	}
	
	@Override
	public void onEvent(Event event) {
		EventDispatcher dispatcher = new EventDispatcher(event);
		dispatcher.dispatch(Event.Type.KEY_PRESSED, (Event e) -> (onKeyPressed((KeyPressedEvent) e)));
		dispatcher.dispatch(Event.Type.KEY_RELEASED, (Event e) -> (onKeyReleased((KeyReleasedEvent) e)));
	}
	
	public boolean onKeyPressed(KeyPressedEvent e) {
		System.out.println(e.getKey());
		if(e.getKey() == 65) {
			player.setxdir(Mob.LEFT);
			player.setxspeed(3);
		}
		
		if(e.getKey() == 68) {
			player.setxdir(Mob.RIGHT);
			player.setxspeed(3);
		}
		return false;
	}
	
	public boolean onKeyReleased(KeyReleasedEvent e) {
		player.setxdir(Mob.NONE);
		player.setxspeed(0);
		return false;
	}
	
}

fyi 68 is ‘a’ and 65 is ‘d’, for left and right. It’s not finished, and I won’t use if statements or literals in the final implementation; I just drew up something quick and dirty for some testing.

Anyway, what PlayerKeyListener does is move the player left or right depending on whether a or d is pressed. The problem I’ve run into is that if both ‘a’ and ‘d’ are pressed simultaneously, meaning for example if I press ‘a’, hold it, then press ‘d’ while still holding down ‘a’, or vice versa, the player stops.

With this implementation, the only way that could possibly happen is if a KeyReleasedEvent is fired, even though I never released a key, but rather pressed an additional key while holding the original key down.

EDIT This was an inaccurate description of the bug. What is actually happening is that when the second key is pressed, while the first one is held down, the player changes direction, but when one of either of the keys is released, a KeyReleasedEvent is fired, causing the player to stop, even though one of the two buttons is still being held.

So I was wondering if anybody had an answer as to why this is happening. I thought somebody might have experienced this at some point and has figured it out.

I can provide more code if needed as there are a few custom classes. FYI, I learned if this from this tutorial:

The tutorial uses the mouse, I merely modified it to use the keyboard instead and ditched the LayerStack in favor of only one Listener at a time, as my game doesn’t require layers. It’s only about 45 minutes long if anybody wants to watch and get a better idea of what I’m implementing.

I probably won’t reply to any posts until tomorrow because right now I have to shift gears to other tasks.

Thank you for any help in debugging this!

Umm could it possibly be because A and D counteract eachother?


 public boolean onKeyPressed(KeyPressedEvent e) {
      System.out.println(e.getKey());
      if(e.getKey() == 65) {
         player.setxdir(Mob.LEFT);
         player.setxspeed(3); //      <---- Move player left 3 units
      }
      
      if(e.getKey() == 68) {
         player.setxdir(Mob.RIGHT);
         player.setxspeed(3); //      <---- Move player right 3 units... If both left and right are pressed, the overall movement is 0.
      }
      return false;
   }

Sorry, it isn’t obvious in the code I provided, but the speed is non-directional. Mob.LEFT and Mob.RIGHT are static final ints, not enums, representing -1 and 1, respectively.

When xa is updated, the formula is xa = xspeed * xdir, then xa is added to x.

Nonetheless, I mis-diagnosed the bug last night. What is really going on is that if I press the second button while the first button is still pressed, a KeyPressedEvent is fired, causing the player to go in the other direction, but then when I release one of them, a KeyReleasedEvent is fired, causing the player to stand still.

Here is a test demo so you can see exactly what I mean: https://www.dropbox.com/s/g25yfjk8v6ilx4r/breakit.jar?dl=0

It’s not finished so you will see some obvious bugs.

It works fine for me, I never experienced the player stopping due to keys being pressed. However RIGHT has a priority because it’s the last key that’s checked. What do you want to happen when both left and right are pressed?

I’ve actually revised PlayerKeyListener to get rid of the if statements and use a HashMap instead:



public class PlayerKeyListener implements EventListener {

	private Player player = null;
	
	private interface Action {
		public void perform();
	}
	
	private Map<Integer, Action> actionMap = new HashMap<>(); 
	
	public PlayerKeyListener init(Player player) {
		this.player = player;		
		actionMap.put(KeyEvent.VK_A, () -> moveLeft());
		actionMap.put(KeyEvent.VK_D, () -> moveRight());
		return this;
	}
	
	@Override
	public void onEvent(Event event) {
		EventDispatcher dispatcher = new EventDispatcher(event);
		dispatcher.dispatch(Event.Type.KEY_RELEASED, (Event e) -> (onKeyReleased((KeyReleasedEvent) e)));
		dispatcher.dispatch(Event.Type.KEY_PRESSED, (Event e) -> (onKeyPressed((KeyPressedEvent) e)));
	}
	
	public boolean onKeyPressed(KeyPressedEvent e) {
		System.out.println("key pressed fired");
		
		if(actionMap.containsKey(e.getKey()))
			actionMap.get(e.getKey()).perform();
		return false;
	}
	
	public boolean onKeyReleased(KeyReleasedEvent e) {
		System.out.println("key released fired");
		
		player.setxdir(Mob.NONE);
		player.setxspeed(0);
		return false;
	}
	
	private void moveLeft() {
		player.setxdir(Mob.LEFT);
		player.setxspeed(3);
	}
	
	private void moveRight() {
		player.setxdir(Mob.RIGHT);
		player.setxspeed(3);
	}	
}

But the problem persists with the exact same behavior, at least on my machine. I just want to make sure it’s understood that I INACCURATELY described the bug in the initial post. The actual behavior, at least on my end, is that if I press the second button while the first button is still pressed, a KeyPressedEvent is fired, causing the player to go in the other direction, but then when I release one of them, a KeyReleasedEvent is fired, causing the player to stand still. I inserted some println statements that confirm this behavior.

Here is a step-by-step breakdown of the sequence I am describing.:

1). A held down : player moves right

2). D held down (A still held down): player moves left. This is okay as far as I’m concerned.

3). A released (D still held down): Player stops, even though D is still held down. This is not okay; player should start moving left again.

The reason I don’t want this is because when you switch directions rapidly, it halts the player, since most people don’t release old key before they press the new one, but rather press the new key then release the old one. I hope that, and the example sequence, make sense.

Also, just wanted to say thanks for taking to time to even bother looking at this. I know it’s a pain in the ass, but it’s also a huge help to me. Can you please check to see if this happens for you?

My approach to detect keys is use an array of booleans where true represent if the key was pressed and falser otherwise.

Basically it allows you tu combine different keys and do almost everything that you want. I saw other people using this approach, e.g. ThinMatrix, TheBennyBox, ChernoProject … all with a lot of cool videos in youtube.

I recommend you separate your input system from your logical/update stuff. Normally it’s more elegant solution.

@vfmachado That’s exactly what I was doing before, and it works great. I’m moving away from polling to interrupting, though, because I eventually want to use it in bigger projects where it needs to be more flexible and be able to handle mechanics like key mapping. Incidentally, the YouTube link I provided in the OP, which has the tutorial for this method I’m using now, is from the ChernoProject. His stuff has been the most useful to me so far out of the YouTube crowd. I could be totally off-base, but I think in the long run interrupting would be the better approach.

[quote]I’m moving away from polling to interrupting, though, because I eventually want to use it in bigger projects where it needs to be more flexible and be able to handle mechanics like key mapping.
[/quote]
I don’t see how key mapping is easier one way or another. It seems to me that separating the key input from the update will probably be more flexible, ultimately. Key input is asynchronous. So it makes sense to keep this in its own thread, and use messaging to the Update thread. This plan makes potential concurrency-related conflicts less likely. I assume the updating goes at a sufficient rate that each loop is “soon enough” for a given key event to be handled.

@philfrei I see. Well, I think putting it in a separate thread is beyond my skill level and a little too complex for this project at this point, so I think what I am going to do, based on advice from some people at /r/gamedev on reddit is to roll back to polling. Their reasoning was that polling is simpler and cleaner for operations that require frame-by-frame updating, whereas interrupting is more useful for things like pressing enter to bring up an options menu. It makes sense to me, so I’m going to keep the event handling interfaces available in the engine, but not use it in the game itself for now. I might find some use for them later. Sometimes I bite off more than I can chew.