2.2 键盘改进
尽管键盘输入类允许在游戏循环中访问键盘状态,但是实现起来还是有一些问题。首先,游戏循环代码执行的时候,如果键盘按键没有按下,将会错过keypress事件。尽管对于这些简单的示例来说,不太可能发生这种情况,但当应用程序变得更强大并且游戏循环需要更多的时间来处理代码时,游戏循环就可能变得太慢而导致错过了输入。现在,只需要知道这可能是一个问题就行了。我们将会在第11章中讨论确保事件不会被错过的一种解决方案。
第二个问题是,当按键第一次被按下的时候,很难进行测试。如果需要跟踪20个按键,并且其中一些按键会根据游戏的状态而改变行为,那么,试图使用类似下面的代码来跟踪所有这些状态,可能会变得非常糟糕:
// SimpleKeyboardExample.java
if( keys.keyDown( KeyEvent.VK_SPACE ) ) {
if( !space ) {
System.out.println( "VK_SPACE" );
}
space = true;
} else {
space = false;
}```
为了更新KeyboardInput类来跟踪初始的按键事件以及键盘状态,添加了一个整数型数值的数组。这些值将会记录按键被按下了多少帧。实现KeyListener接口的代码并不会改变,但是keyDown()方法不再会从布尔的按键数组提取值。
poll()方法同步地保护共享的按键数组,将键盘状态从布尔型数组转换为整数型数组。如果按键按下,这个值将会增加1;否则的话,它将会设置为0。现在,keyDown()方法将检测这个值是否为0,并且当这个值确实为1的时候,新的方法keyDownOnce()将会返回真。
package javagames.util;
import java.awt.event.*;
public class KeyboardInput implements KeyListener {
private boolean[] keys;
private int[] polled;
public KeyboardInput() {
keys = new boolean[ 256 ];
polled = new int[ 256 ];
}
public boolean keyDown( int keyCode ) {
return polled[ keyCode ] > 0;
}
public boolean keyDownOnce( int keyCode ) {
return polled[ keyCode ] == 1;
}
public synchronized void poll() {
for( int i = 0; i < keys.length; ++i ) {
if( keys[i] ) {
polled[i]++;
} else {
polled[i] = 0;
}
}
}
public synchronized void keyPressed( KeyEvent e ) {
int keyCode = e.getKeyCode();
if( keyCode >= 0 && keyCode < keys.length ) {
keys[ keyCode ] = true;
}
}
public synchronized void keyReleased( KeyEvent e ) {
int keyCode = e.getKeyCode();
if( keyCode >= 0 && keyCode < keys.length ) {
keys[ keyCode ] = false;
}
}
public void keyTyped( KeyEvent e ) {
// Not needed
}
}`
别忘了,针对每一帧要调用KeyboardInput.poll() 方法。
通过使用如下代码替换前面例子中的游戏循环代码,可以使用这个新的类。检查空格键按下一次所需的代码大大简化了。
// replacing the game loop
public void gameLoop() {
keys.poll();
if( keys.keyDownOnce( KeyEvent.VK_SPACE ) ) {
System.out.println( "VK_SPACE" );
}
if( keys.keyDown( KeyEvent.VK_UP ) ) {
System.out.println( "VK_UP" );
}
if( keys.keyDown( KeyEvent.VK_DOWN ) ) {
System.out.println( "VK_DOWN" );
}
if( keys.keyDown( KeyEvent.VK_LEFT ) ) {
System.out.println( "VK_LEFT" );
}
if( keys.keyDown( KeyEvent.VK_RIGHT ) ) {
System.out.println( "VK_RIGHT" );
}
try {
Thread.sleep( 10 );
} catch( InterruptedException ex ) { }