JavaFX:如何为非初始场景编写移动功能的KeyEvent处理器?
问题:JavaFX游戏场景中添加键盘事件控制Paddle移动
我正在通过制作Pong游戏学习JavaFX,想给第三个游戏场景添加控制paddle移动的KeyEvent,但找到的示例都是在初始场景的start函数里实现的,不知道怎么在初始场景外使用这个功能。目前我设置了三个带独立控制器的场景:第一个(Main)是启动界面,触发Action事件后切换到第二个菜单场景;菜单界面有Start、Load、Quit三个选项,点击Start进入第三个游戏场景(Gameplay)。
现有代码
Main.java
@Override public void start(Stage stage) throws Exception { //Stage stage1 = new Stage(); Parent root = FXMLLoader.load(getClass().getResource("Main.fxml")); Scene scene = new Scene(root); Image icon = new Image("pongicon.png"); stage.getIcons().add(icon); stage.setTitle("Pong"); stage.setResizable(false); stage.setScene(scene); stage.show(); }
MainController.java
public void switchToScene2(ActionEvent e) throws IOException{ root = FXMLLoader.load(getClass().getResource("Menu.fxml")); stage = (Stage)((Node)e.getSource()).getScene().getWindow(); scene = new Scene(root); stage.setScene(scene); stage.show(); }
MenuController.java(原始版本)
public class MenuController { @FXML private Circle myCircle; private double radius; private Stage stage; private Scene scene; private Parent root; public void switchToGameplay(MouseEvent e) throws IOException{ root = FXMLLoader.load(getClass().getResource("Gameplay.fxml")); stage = (Stage)((Node)e.getSource()).getScene().getWindow(); scene = new Scene(root); stage.setScene(scene); stage.show(); } public void start(MouseEvent e) throws IOException { myCircle.setRadius(radius+=1000); switchToGameplay(e); } public void load(MouseEvent e) { System.out.println("Load"); } public void quit(MouseEvent e) { System.exit(0); } }
尝试过程与问题
我尝试过像设置鼠标或Action事件那样用switch配置KeyEvent,但没效果。后来查到需要给场景设置键盘事件监听器,于是把这段代码放到MenuController的switchToGameplay函数里:
public void switchToGameplay(MouseEvent e) throws IOException{ FXMLLoader loader = new FXMLLoader(getClass().getResource("Gameplay.fxml")); Parent root = loader.load(); Controller controller = loader.getController(); stage = (Stage)((Node)e.getSource()).getScene().getWindow(); scene = new Scene(root); scene.setOnKeyPressed(new EventHandler<KeyEvent>() { @Override public void handle(KeyEvent e) { switch(e.getCode()) { case W: controller.moveUp(); break; case S: controller.moveDown(); break; default: break; } } }); stage.setScene(scene); stage.show(); }
但这样行不通,在controller.moveUp()/moveDown()处遇到了ModuleLayer错误。之后尝试直接把代码加到GameplayController里,结果完全出错,现在更困惑了。
解决方案
1. 修正控制器类型引用
你这里的核心问题是控制器类型引用错误:代码里的Controller controller = loader.getController();应该替换成实际的游戏场景控制器类名GameplayController,因为Gameplay.fxml对应的控制器是GameplayController类。
2. 实现GameplayController的移动方法
确保GameplayController中存在moveUp和moveDown方法,示例如下:
public class GameplayController { @FXML private Rectangle paddle; // 假设你的paddle是FXML中定义的Rectangle组件 private final double MOVE_SPEED = 10; public void moveUp() { double newY = paddle.getY() - MOVE_SPEED; // 限制paddle不超出窗口顶部 if (newY >= 0) { paddle.setY(newY); } } public void moveDown() { double newY = paddle.getY() + MOVE_SPEED; // 限制paddle不超出窗口底部(这里假设窗口高度为600,可根据实际调整) if (newY <= 600 - paddle.getHeight()) { paddle.setY(newY); } } }
3. 修正MenuController的switchToGameplay方法
修改后的方法如下,同时要确保场景获取焦点,否则键盘事件不会触发:
public void switchToGameplay(MouseEvent e) throws IOException{ FXMLLoader loader = new FXMLLoader(getClass().getResource("Gameplay.fxml")); Parent root = loader.load(); // 引用正确的控制器类 GameplayController controller = loader.getController(); stage = (Stage)((Node)e.getSource()).getScene().getWindow(); Scene scene = new Scene(root); scene.setOnKeyPressed(keyEvent -> { switch(keyEvent.getCode()) { case W: controller.moveUp(); break; case S: controller.moveDown(); break; default: break; } }); stage.setScene(scene); stage.show(); // 关键步骤:让场景根节点获取焦点,确保键盘事件能被捕获 scene.getRoot().requestFocus(); }
4. 额外注意事项
- 确保Gameplay.fxml的根节点(如AnchorPane)没有设置
focusTraversable="false",否则无法获取焦点,键盘事件不会生效。 - 如果游戏场景中有其他可获取焦点的组件(如按钮),切换场景后必须让根节点请求焦点,否则键盘事件会被其他组件拦截。
内容的提问来源于stack exchange,提问作者River Lockhart
相关产品推荐
相关产品推荐

