《Java 2D游戏编程入门》—— 2.1 处理键盘输入

简介: SimpleKeyboardExample类位于javagames.input包中,它是使用键盘输入类的一个简单测试,它使用输入处理代码来替代渲染代码。注意,使用addKeyListener()方法将SimpleKeyboardInput添加到应用程序中。

本节书摘来异步社区《Java 2D游戏编程入门》一书中的第2章,第2.1节,作者:【美】Timothy Wright(莱特),更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.1 处理键盘输入

在大多数应用程序中,软件都不需要处理键盘事件。当某些事情发生变化的时候,由任意的组件(如文本框)来处理输入并通知软件。但是,大多数计算机游戏使用键盘不是为了录入,而是为了游戏输入。根据游戏的不同,虽然可能会有录入,但键盘按键常用做方向键和发射激光武器。很多计算机游戏具有不同的输入配置,并且有些游戏甚至允许用户根据自己的意愿来设置按键。不管游戏如何使用键盘(并且它可能采用一种全新的方式来使用键盘,监听键盘事件的常用方法都不适用于游戏循环程序设计。

Swing组件允许在实现了KeyListener的接口的对象上添加监听器。

  • void keyTyped(KeyEvent e)—按下并释放键。
  • void keyPressed(KeyEvent e)—按下键。
  • void keyReleased(KeyEvent e)—释放键。

按下一个按键的时候,调用keyPressed()方法。正如所预期的那样,释放按键的时候,调用keyReleased()方法。只有在按键按下并释放之后,才会调用keyTyped()方法。动作按键和修饰按键,例如Shift按键和方向箭头按键,不会产生keyTyped()事件。第11章介绍文本的时候将会讨论这一事件。

问题在于,键盘是由操作系统维护的一种硬件。由操作系统而不是软件来产生键盘事件,并将其分派给相关的应用程序。没什么办法能够阻止用户从游戏窗口切换回Web浏览器并查看Email。因此,所有的键盘事件都通过一个不同的线程到达,并且可供游戏循环使用。

大多数游戏遵从某种循环结构:

while( true ) { 
  processInput();
  updateObjects();
  // other stuff...
  renderScene();
}```
如果在游戏循环之外处理输入,状态可能会随时改变。此外,还可能会同时按下多个按键,因此,处理每个事件自身并不允许用户组合按键。为了简化输入过程,应保存键盘事件并使其可供游戏循环使用。

存储键盘状态时,理解程序如何共享键盘的状态是很重要的。键盘是非常复杂的硬件,不仅那些字符串字符可用,甚至每个并不代表字符的按键(例如Shift键),也可以通过虚拟按键代码变得可用。键盘上的每个按键都映射为KeyEvent类中的一个键代码。如下是一些示例值。

- KeyEvent.VK_E— E键。
- KeyEvent.VK_SPACE—空格键。
- KeyEvent.VK_UP—向上箭头键。

这些常量中的每一个,都映射为传递给KeyEvent对象中的按键监听器的一个数字值。针对产生事件的任何键盘按键,KeyEvent.getKeyCode()方法都返回虚拟的键代码。

SimpleKeyboardInput类位于javagames.util包中,它非常小。这个类实现了KeyListener接口,因此,它可以监控键盘事件。它保存了256个键的一个Boolean数组,其中都是需要取样的虚拟键代码。在键盘状态数组中,存储了键的状态,如果按下的话是true,否则就是false。最后,使用synchronized关键字来防止从多个线程访问键状态数组,然后,通过keyDown(int keyCode)方法允许访问当前按键状态。

package javagames.util;
import java.awt.event.*;
public class SimpleKeyboardInput implements KeyListener {
  private boolean[] keys;
  public SimpleKeyboardInput() {
    keys = new boolean[ 256 ];
  }
  public synchronized boolean keyDown( int keyCode ) {
    return keys[ keyCode ];
  }
  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
  }
}


SimpleKeyboardExample类位于javagames.input包中,它是使用键盘输入类的一个简单测试,它使用输入处理代码来替代渲染代码。注意,使用addKeyListener()方法将SimpleKeyboardInput添加到应用程序中。在游戏循环中,游戏循环会检查空格或箭头按键,并且在这些按键按下时打印一条消息,而不是清除图像并显示帧速率。

当检查到空格时,示例使用一个变量来保存按键的状态,针对每次按键只打印到控制台一次。而对于箭头按键,会持续将其状态输出到控制台,直到按键释放。

package javagames.input;

import java.awt.event.*;
import javax.swing.*;
import javagames.util.*;

public class SimpleKeyboardExample extends JFrame implements Runnable {
  private volatile boolean running;
  private Thread gameThread;
  private SimpleKeyboardInput keys;
  private boolean space;
  public SimpleKeyboardExample() {
    keys = new SimpleKeyboardInput();
  }
  protected void createAndShowGUI() {
    setTitle( "Keyboard Input" );
    setSize( 320, 240 );
    addKeyListener( keys );
    setVisible( true );
    gameThread = new Thread( this );
    gameThread.start();
  }
  public void run() {
    running = true;
    while( running ) {
      gameLoop();
    }
  }
  public void gameLoop() {
    if( keys.keyDown( KeyEvent.VK_SPACE ) ) {
      if( !space ) {
        System.out.println( "VK_SPACE" );
      }
      space = true;
    } else {
      space = false;
    }
    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 ) { }
  }
  protected void onWindowClosing() {
    try {
      running = false;
      gameThread.join();
    } catch( InterruptedException e ) {
      e.printStackTrace();
    }
    System.exit( 0 );
  }
  public static void main( String[] args ) {
    final SimpleKeyboardExample app = new SimpleKeyboardExample ();
    app.addWindowListener( new WindowAdapter() {
      public void windowClosing( WindowEvent e ) {
        app.onWindowClosing();
      }
    });
    SwingUtilities.invokeLater( new Runnable() {
      public void run() {
        app.createAndShowGUI();
      }
    });
  }
}`

相关文章
|
1天前
|
Java 开发工具 Windows
Java入门及环境变量
Java入门及环境变量
|
1天前
|
Java API 调度
[AIGC] 深入理解Java并发编程:从入门到进阶
[AIGC] 深入理解Java并发编程:从入门到进阶
|
1天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
1天前
|
前端开发 Java 测试技术
Java从入门到精通:4.1.1参与实际项目,锻炼编程与问题解决能力
Java从入门到精通:4.1.1参与实际项目,锻炼编程与问题解决能力
|
1天前
|
Java 程序员 数据库连接
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
|
1天前
|
Dubbo Java 应用服务中间件
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
|
1天前
|
SQL Java 数据库连接
Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句
Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句
|
1天前
|
SQL Java 数据库连接
Java从入门到精通:2.3.1数据库编程——学习JDBC技术,掌握Java与数据库的交互
ava从入门到精通:2.3.1数据库编程——学习JDBC技术,掌握Java与数据库的交互
|
1天前
|
设计模式 存储 前端开发
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
|
1天前
|
Java API
Java从入门到精通:2.1.5深入学习Java核心技术之文件操作
Java从入门到精通:2.1.5深入学习Java核心技术之文件操作