You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
}
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:54:57