Many input systems use so called Input Events. That is, when input is detected, an event is "fired" and the program responds instantly. This is not normally desirable in games where the programmer often wants more control over when user input is acted upon. For renderers such as Xith3D were you can not modify the scenegraph while rendering, this is a larger problem. While one solution is not to use asynchronous input devices like these, they do have their benefits (one being ubiquity) and it is still quite possible to aggregate the events and poll them once per cycle anyway. This tutorial shows one way to poll the keyboard when using an event driven input system. The API used for the examples is Java's AWT.
Update (2004-12-07): I have created a java input abstraction API which provides a solution to this problem and more.
1.1 Possible Solution
To convert the keyboard events into something you can poll is fairly straight forward. One method is to create a Set that will contain the integer keycodes of all the currently pressed keys (remember: elements of a Set are unique so it is impossible to have the same keycode twice, any attempts to do this are silently ignored). How this works is that every time a key pressed event is received, that key's keycode is added to the Set. Similarly, every time a key released event is received, that key's keycode is removed from the Set. Thus, the Set will always contain a list of currently pressed keys, and this list can be polled (i.e. iterated) and actions taken.
Aside: KeyAdapter's keyTyped events are generally unused in games as they are higher level events which don't directly related to whether the key is up or down (they are fired after the key is released). That said, the principles outlined in this tutorial can be applied to pretty much any event should it be needed for whatever reason.
Example:
/*
* Class Instance Variables
*/
private Set pressedKeys = new HashSet();
//...
/*
* Initialisation method
*/
component.addKeyListener(new KeyAdapter () {
public void keyPressed(KeyEvent e) {
pressedKeys.add(new Integer(e.getKeyCode()));
}
public void keyReleased(KeyEvent e) {
pressedKeys.remove(new Integer(e.getKeyCode()));
}
});
/*
* Key polling method
*/
for (Iterator i = pressedKeys.iterator(); i.hasNext(); ) {
int keyCode = ((Integer) i.next()).intValue();
switch (keyCode) {
case KeyEvent.VK_1 :
// Do action
break;
case KeyEvent.VK_2 :
//...
For keys that are only acted upon once every time they are pressed - simply remove that key from the pressedKeys iterator when it has been processed. If you do this however, be sure to remember that when you are iterating a collection, the only legal way to remove an item from that collection is using the Iterator's remove() method. When the key is released, it will be ``removed'' again from the set but this will just be silently ignored.
1.2 Preventing Access Violations
A problem with the above example code however is that you are not permitted to modify a Collection (the pressedKeys Set) while it is being iterated. To resolve this, a standard mutex can be used to block other threads from accessing it simultaneously.
Example mutex class:
public class Mutex {
private boolean locked = false;
public synchronized void aquireLock() {
while (locked) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
locked = true;
}
public synchronized void releaseLock() {
locked = false;
notifyAll();
}
}
To use the mutex - create a single private instance of it and surround all code that uses the pressedKeys Set (i.e. both methods of KeyAdaptor and the poll iteration loop) with a call to the mutex aquireLock() and releaseLock() methods.
1.3 Preventing ``Lost'' Key Presses
The current solution however still has issues. If a user presses and releases a key very quickly (in between polls) it would quite likely go unnoticed. To solve this, we can use a second Set to store released keys. When the key is released, it's keycode is added to the Set of released keys. Every time the keys are polled, the released keys are removed from the pressedKeys Set, and the releasedKeys Set is cleared. Note that if a key is tapped twice in between polls, it will only be registered once due to the nature of Set. Many applications do not want to process the same key twice in a single frame, but if this is desirable then you will need to device a variation which uses a type of Collection without this restriction (e.g. List).
The code from above becomes this (also including the mutex locking):
/*
* Class Instance Variables
*/
private Set pressedKeys = new HashSet();
private Set releasedKeys = new HashSet();
private Mutex keyLock = new Mutex();
//...
/*
* Initialisation method
*/
component.addKeyListener(new KeyAdapter () {
public void keyPressed(KeyEvent e) {
keyLock.aquireLock();
pressedKeys.add(new Integer(e.getKeyCode()));
keyLock.releaseLock();
}
public void keyReleased(KeyEvent e) {
keyLock.aquireLock();
releasedKeys.add(new Integer(e.getKeyCode()));
keyLock.releaseLock();
}
});
//...
/*
* Key polling method
*/
keyLock.aquireLock();
for (Iterator i = pressedKeys.iterator(); i.hasNext(); ) {
int keyCode = ((Integer) i.next()).intValue();
switch (keyCode) {
case KeyEvent.VK_1 :
// Do action
break;
case KeyEvent.VK_2 :
// Do action only once
i.remove();
//...
}
}
pressedKeys.removeAll(releasedKeys);
keyLock.releaseLock();
In some cases there may be keys which you may wish to ignore if they are released before the keyboard is polled and some keys you don't wish to ignore. The solution to this simple: iterate though the pressedKeys once to process the keys that are not ignored even if they have already been released, then remove the releasedKeys from the pressedKeys Set and iterate though the pressedKeys a second time, taking action on the remaining keys.
The ability to know that a key was pressed even though it may have already been released by the time it is polled is an advantage of this solution and means that the key state can be polled less frequently. The penalty however is that every time a key is pressed, the currently executing thread is interrupted, even if it is only for a millisecond or two while the key is added to the Set.
This sample solution to this common problem is but one way to do it. As the requirements on the input processing system for
different games varies, different approaches may be needed.


Current Projects
Polling the Keyboard State using an Event Driven System