Java:Tutorials:Key States
From GPWiki
The wiki is now hosted by GameDev.NET at wiki.gamedev.net. All gpwiki.org content has been moved to the new server. However, the GPWiki forums are still active! Come say hello.
[edit] IntroductionEvent-based key input can greatly simplify application programming. Instead of always having to ask if a key has been pressed, you simply wait until the application tells you it's been pressed, or released. In game programming though, this can be undesirable. The problem is that Java can only tell you if a key's been pressed or released because those are events have happen because something else has happened, like the user pressing or releasing a key. In games, you will often want to know if a key is being held down. You don't care when it happened; it's only important if it's down now. Java doesn't tell you this, not by itself anyway. [edit] Basic SetupSince the only interface we have with the keyboard is event-based, we'll have to make do. I'm not going to explain how to set up your program to receive KeyEvents in great detail, as that is covered elsewhere. Understanding the typical, event-based way of getting keyboard input is helpful for getting away from it. Code Example 1 shows a small program that extends JFrame so that it is a window and receives keyboard input. [edit] Code Example 1Setup for receiving keyboard input. import java.awt.*; import java.awt.event.*; import javax.swing.*; public class KeyTest extends JFrame implements KeyListener { public static void main(String[] args) { new KeyTest(); } public KeyTest() { this.addKeyListener(this); this.setSize(800,600); this.setVisible(true); } public void keyPressed(KeyEvent e) { // Prints the key code to the console. System.out.println(e.getKeyCode()); } public void keyReleased(KeyEvent e) { // Do stuff. } public void keyTyped(KeyEvent e) { // Do stuff. } } Not very exciting, but it has everything we need to get keyboard input. [edit] Key StatesUsually, programming in a logical exercise. In this case, things become a little bit unintuitive. If a key is pressed, it must be down until it is released, right? Java doesn't see the states of the keys, only if they were pressed or released. Since it clear to us, the programmers, we'll have to take care of it ourselves. [edit] Low-Level TriviaThere's something you should know about the keys: They are represented as numbers. The numbers seem to be mostly arbitrarily chosen, so trying to remember that the Escape key is number 27 is difficult. Fortunately, there are constants for all of the keys that have nice, easy-to-remember names, like VK_ESCAPE. Normally, if you wanted to check if the Escape key was pressed, you'd have code like this: public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { // Do stuff. } } The fact that all of the keys are represented as numbers can be exploited. [edit] Storing the Key StatesWhat's the best way to keep track of a lot of information? That's right, an array. How big should this array be? If you look at a standard keyboard, there are around 100 keys on it. We can't count on the user having a standard keyboard though, or even one in the same layout as our own. Java supports some pretty weird keys, like VK_HALF_WIDTH, VK_FINAL, VK_DEAD_DOUBLEACUTE and even function keys up to VK_F24! Remember how I said the numbers to represent the keys were rather arbitrarily chosen? The letter keys are nice, they are the ASCII values for the corresponding keys. Other keys can be like VK_CAPS_LOCK, which is 20, VK_F1, which is 112, VK_WINDOWS, which is 524(!) or even VK_STOP, which is 65480(!!!). These numbers aren't all sequential, so there aren't 60000 different keys supported. The keys that you will probably care about end after the Windows key, 524. Yes, that's right. 100 keys will fit into an array with 525 elements. Seems like a waste, doesn't it? You can optimize this if you like, but that may get complicated. We only care about whether the key is up or down, so this will be an array of booleans. The only thing left is naming it. I usually use "keys". boolean[] keys = new boolean[525]; The great thing about using an array like this is that we can use the key code constants to directly access the state of each key. Code Example 2 shows how to set the states of the keys based on the key events that Java raises. [edit] Code Example 2Setting the states of the keys. import java.awt.event.*; ... boolean[] keys = new boolean[525]; ... public void keyPressed(KeyEvent e) { keys[e.getKeyCode()] = true; } public void keyReleased(KeyEvent e) { keys[e.getKeyCode()] = false; } ... There will be nothing simpler in your game engine than setting the key states. Code Example 3 shows how to use the key states in your game logic. [edit] Code Example 3Using the key states in your game logic. import java.awt.event.KeyEvent; ... // This is assumed to be defined elswhere, and is just used as an example. // Sprite is assumed to have a move() function that takes new X and Y coordinates. Sprite mySprite; public void gameLogic(boolean[] keys) { int x = mySprite.getX(); int y = mySprite.getY(); if (keys[KeyEvent.VK_UP]) { y = -1; } if (keys[KeyEvent.VK_DOWN]) { y = 1; } if (keys[KeyEvent.VK_LEFT]) { x = -1; } if (keys[KeyEvent.VK_RIGHT]) { x = 1; } mySprite.move(x,y); } ... [edit] Techniques[edit] Limiting the InputDepending on your implementation, you might be checking the states of the keys hundreds of times per second. This can cause your character to move much faster than you would like. In order to limit how quickly the character moves, you can set a wait between checking the states. Code Example 4 demonstrates one way of doign this. [edit] Code Example 4Limiting how often the game checks the key states. // This example extends the Code Example 3. final int keyCheckPause = 100; // Waits 100ms between checks. long lastKeyCheck = 0; public void gameLogic(boolean[] keys) { long currentTime = System.currentTimeMillis(); if ((currentTime - lastKeyCheck) >= keyCheckPause) { int x = mySprite.getX(); int y = mySprite.getY(); if (keys[KeyEvent.VK_UP]) { y = -1; } if (keys[KeyEvent.VK_DOWN]) { y = 1; } if (keys[KeyEvent.VK_LEFT]) { x = -1; } if (keys[KeyEvent.VK_RIGHT]) { x = 1; } mySprite.move(x,y); lastKeyCheck = currentTime; } } [edit] Static ImportsAs of Java 5, it is possible to import static members and have them act as if they belonged to the current class. This really only serves to simplify the code by taking out the class name of the owner of the constants. Some people prefer this, as it makes the code look nicer. Code Example 5 shows how Code Example 3 would look with static imports. [edit] Code Example 5Demonstration of static imports. import static java.awt.event.KeyEvent.*; import java.awt.event.KeyEvent; ... // This is assumed to be defined elswhere, and is just used as an example. // Sprite is assumed to have a move() function that takes new X and Y coordinates. Sprite mySprite; public void gameLogic(boolean[] keys) { int x = mySprite.getX(); int y = mySprite.getY(); if (keys[VK_UP]) { y = -1; } if (keys[VK_DOWN]) { y = 1; } if (keys[VK_LEFT]) { x = -1; } if (keys[VK_RIGHT]) { x = 1; } mySprite.move(x,y); } ... [edit] More Information |


