
在JavaFX中开发游戏或实时交互应用时,AnimationTimer是实现游戏循环的核心机制。它的handle方法会以显示器的刷新率(通常是每秒60次)被调用,用于更新游戏状态(update)和渲染画面(render)。然而,当按键事件处理逻辑被不恰当地放置在游戏循环内部时,便会引发一系列问题。
一个常见的错误是将按键事件监听器(setOnKeyPressed、setOnKeyReleased)的注册逻辑,以及用于存储当前按下键的列表的初始化,都放在update方法或其调用的方法(例如handleInput)中。考虑以下不当实现:
// 错误示例:Game类中的handleInput方法
private void handleInput() {
ArrayList<String> input = new ArrayList<String>(); // 每次调用都创建新的空列表
this.scene.setOnKeyPressed( // 每次调用都重新注册监听器
new EventHandler<KeyEvent>() {
public void handle(KeyEvent e) {
String code = e.getCode().toString();
if (!input.contains(code)) {
input.add(code);
}
}
});
this.scene.setOnKeyReleased(
new EventHandler<KeyEvent>() {
public void handle(KeyEvent e) {
String code = e.getCode().toString();
input.remove(code);
}
});
System.out.println(input); // 总是打印空的或短暂的列表
}上述代码存在两个主要问题:
这种错误处理方式不仅会导致按键事件无法被正确捕获和跟踪,还可能影响游戏性能和稳定性,例如在退出游戏时出现窗口无法关闭等异常行为。
立即学习“Java免费学习笔记(深入)”;
为了在JavaFX游戏应用中实现稳定、高效的按键事件处理,我们必须遵循以下核心原则:事件监听器只注册一次,且用于跟踪输入状态的列表应作为类的成员变量进行管理。
事件监听器应在应用程序或游戏场景初始化时注册一次。对于Game类,最佳位置是在其构造函数中。这样可以确保监听器在整个游戏生命周期内都处于活动状态,并且避免了重复注册的性能开销。
用于存储当前按下键的列表(例如input)不应是局部变量,而应是Game类的私有成员变量。这样,该列表的生命周期将与Game对象一致,可以在不同的帧周期中持久化按键状态。
在setOnKeyPressed的handle方法中,当一个键被按下时,应将其KeyCode添加到成员变量input列表中(如果尚未存在,以避免重复)。在setOnKeyReleased的handle方法中,当一个键被释放时,应将其KeyCode从input列表中移除。
在update方法(或其调用的方法,如handleInput)中,可以直接访问成员变量input列表来查询当前哪些键被按下,并根据这些输入来更新游戏逻辑。
以下是修正后的Game类代码示例:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.animation.AnimationTimer;
import javafx.event.EventHandler;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.KeyCode; // 推荐使用KeyCode
import java.util.ArrayList;
import java.util.List; // 使用List接口
public class Main extends Application {
private static Game game;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
game = new Game(stage);
final long startNanoTime = System.nanoTime();
new AnimationTimer() {
public void handle(long currentNanoTime) {
double t = (currentNanoTime - startNanoTime) / 1000000000.0;
game.update(t);
game.render();
}
}.start();
}
}
class Game {
private Stage stage;
private Group root;
private Scene scene;
private Canvas canvas;
private GraphicsContext gc;
final static int WINDOW_WIDTH = 800;
final static int WINDOW_HEIGHT = 600;
// 将input列表声明为类的成员变量,用于持久化按键状态
private List<KeyCode> input = new ArrayList<>();
public Game(Stage stage) {
this.stage = stage;
this.root = new Group();
this.scene = new Scene(this.root);
this.canvas = new Canvas(WINDOW_WIDTH, WINDOW_HEIGHT);
this.gc = this.canvas.getGraphicsContext2D();
this.stage.setTitle("Pong");
this.stage.setScene(this.scene);
this.root.getChildren().add(this.canvas);
// 在构造函数中一次性注册事件监听器
this.scene.setOnKeyPressed(
new EventHandler<KeyEvent>() {
public void handle(KeyEvent e) {
KeyCode code = e.getCode(); // 直接使用KeyCode
if (!input.contains(code)) { // 避免重复添加
input.add(code);
}
}
});
this.scene.setOnKeyReleased(
new EventHandler<KeyEvent>() {
public void handle(KeyEvent e) {
KeyCode code = e.getCode();
input.remove(code);
}
});
}
public void update(double time) {
this.handleInput(); // 现在handleInput只负责处理和打印当前的input列表
// 示例:根据按键输入执行游戏逻辑
if (input.contains(KeyCode.W)) {
System.out.println("W is pressed!");
// 执行向上移动逻辑
}
if (input.contains(KeyCode.S)) {
System.out.println("S is pressed!");
// 执行向下移动逻辑
}
this.gc.setFill(Color.RED);
this.gc.fillRect(0,0, WINDOW_WIDTH, WINDOW_HEIGHT);
}
public void render() {
this.stage.show();
}
private void handleInput() {
// 现在这里打印的是持久化的input列表,会显示当前按下的所有键
System.out.println("Current pressed keys: " + input);
}
}通过上述修改,input列表将正确地跟踪用户按下的键,并且System.out.println(input)将打印出当前所有被按下的键码。
遵循这些原则,可以有效地管理JavaFX应用中的用户输入,确保游戏或交互式应用能够准确、响应迅速地对用户操作做出反应。
以上就是JavaFX游戏开发:优化按键事件处理与AnimationTimer的正确实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号