Keyboard Input

For a game, you want to use lots of keys, such as the arrow keys for movement and maybe the Control key for firing a weapon. We really aren't going to deal with things like text input—we can leave that for Swing components discussed later in this chapter. To capture key events, you need to do two things: create a KeyListener and register the listener to receive events. To register a listener, just call the addKeyListener() method on the component that you want to receive key events on. For games here, that component is the full-screen window:

Window window = screen.getFullScreenWindow();
window.addKeyListener(keyListener);


To create a KeyListener, you just need to create an object that implements the KeyListener interface. The KeyListener interface has three methods: keyPressed(), keyReleased(), and keyTyped(). The "typed" event occurs when a key is first pressed and then repeatedly based on the key repeat rate. Receiving events when a key is "typed" is pretty useless for a game, so we just focus on the key presses and releases. Each of these three methods takes a KeyEvent as a parameter. The KeyEvent object enables you to inspect what key was actually pressed or released, in the form of a virtual key code. A virtual key code is the Java-defined code to a particular keyboard key, but it is not the same as the character. For example, although Q and q are different characters, they have the same key code. All the virtual key codes are defined in KeyEvent in the form VK_xxx. For example, the Q key has the key code KeyEvent.VK_Q. Most of the time, you can guess the name of a key code (such as VK_ENTER or VK_1), but be sure to look up the KeyEvent class in the Java API documentation for the full list of virtual key codes. Now let's try it out. The KeyTest class in Listing 3.2 is an implementation of the KeyListener interface. It simply displays key press and release events to the screen. Press Escape to exit the program.

Listing 3.2 KeyTest.java
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import com.brackeen.javagamebook.graphics.*;
import com.brackeen.javagamebook.test.GameCore;
/**
 A simple keyboard test. Displays keys pressed and released to
 the screen. Useful for debugging key input, too.
*/
public class KeyTest extends GameCore implements KeyListener {
 public static void main(String[] args) {
 new KeyTest().run();
 }
 private LinkedList messages = new LinkedList();
 public void init() {
 super.init();
 Window window = screen.getFullScreenWindow();
 // allow input of the TAB key and other keys normally
 // used for focus traversal
 window.setFocusTraversalKeysEnabled(false);
 // register this object as a key listener for the window
 window.addKeyListener(this);
 addMessage("KeyInputTest. Press Escape to exit");
 }
 // a method from the KeyListener interface
 public void keyPressed(KeyEvent e) {
 int keyCode = e.getKeyCode();
 // exit the program
 if (keyCode == KeyEvent.VK_ESCAPE) {
 stop();
 }
 else {
 addMessage("Pressed: " +
 KeyEvent.getKeyText(keyCode));
 // make sure the key isn't processed for anything else
 e.consume();
 }
 }
 // a method from the KeyListener interface
 public void keyReleased(KeyEvent e) {
 int keyCode = e.getKeyCode();
 addMessage("Released: " + KeyEvent.getKeyText(keyCode));
 // make sure the key isn't processed for anything else
 e.consume();
 }
 // a method from the KeyListener interface
 public void keyTyped(KeyEvent e) {
 // this is called after the key is released - ignore it
 // make sure the key isn't processed for anything else
 e.consume();
 }
 /**
 Add a message to the list of messages.
 */
 public synchronized void addMessage(String message) {
 messages.add(message);
 if (messages.size() >= screen.getHeight() / FONT_SIZE) {
 messages.remove(0);
 }
 }
 /**
 Draw the list of messages
 */
 public synchronized void draw(Graphics2D g) {
 Window window = screen.getFullScreenWindow();
 g.setRenderingHint(
 RenderingHints.KEY_TEXT_ANTIALIASING,
 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
 // draw background
 g.setColor(window.getBackground());
 g.fillRect(0, 0, screen.getWidth(), screen.getHeight());
 // draw messages
 g.setColor(window.getForeground());
 int y = FONT_SIZE;
 for (int i=0; i<messages.size(); i++) {
 g.drawString((String)messages.get(i), 5, y);
 y+=FONT_SIZE;
 }
 }
}


You should note a couple things about this code. First, the init() method uses this line:

window.setFocusTraversalKeysEnabled(false);


This, of course, disables focus traversal keys. Focus traversal keys are the keys pressed to change the keyboard focus from one component to another. For example, on a web page, you've probably pressed Tab to move from field to field in a form. Normally, the Tab key event is swallowed up by the AWT's focus traversal code, but in this case we want to receive the Tab key event. Calling this method allows us to do so. If you're curious what the various focus traversal keys are, call the getFocusTraversalKeys() method. The Tab key isn't the only key that can cause some strange behavior. The Alt key can also cause a problem. On most systems, the Alt key is used to activate a mnemonic. A mnemonic is a shortcut or accelerator key for a particular user interface element. For example, pressing Alt+F activates the File menu on most apps that have a menu bar. The AWT thinks the next key pressed after Alt is a mnemonic and ignores the key. To disable this, in KeyTest you stop every KeyEvent in each KeyListener from being processed in the default manner by calling this:

e.consume();


This makes sure no other object processes the Alt key (or whatever modifier key activates a mnemonic), so the Alt key is treated like a normal key. A side effect to KeyTest is that it's a useful program for testing how keys behave on different systems. "Gasp! You mean key input isn't the same on all systems?" Unfortunately, no, it's not. Let's take key repetition for an example. When the user holds down a key, the operating system sends multiple events for that key. In a text editor, for example, when you hold down the Q key, several Qs appear. On some systems (such as Linux), both the key press and the key release event are sent for each key repeat. On other systems (such as Windows), only the key press event is sent for each repetition, and the key release event isn't sent until the user actually releases the key. Also, there could be other subtle differences, such as the Windows Start key behaving differently depending on what version of Windows is running. Or, the key behavior could be different depending on what Java VM is running. Luckily, these differences aren't drastic; most of the time, you won't have to deal with them.



   
Comments