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

如何通过KeyEvent实现长按空格键每2秒发射一次飞船子弹?

问题修复方案

问题根源

  1. 长按空格键时,系统会重复触发KEY_PRESSED事件,原代码每次触发都会调用shoot(),而shoot()每次都会新建一个定时任务并启动,导致多个任务同时发射子弹,形成连续串射的问题。
  2. 原ShootBullet类的逻辑完全错误,它内部包含了游戏循环死循环,这不是定时发射子弹的正确实现,会导致线程资源浪费和逻辑混乱。

修复步骤

1. 修正ShootBullet定时任务类

将其改为仅负责定时发射子弹的任务,移除错误的游戏循环:

public class ShootBullet extends TimerTask {
    private Game model;
    
    public ShootBullet(Game model) {
        this.model = model;
    }

    @Override
    public void run() {
        // 每次任务执行时发射一枚子弹,使用Platform.runLater确保UI线程安全
        Platform.runLater(() -> model.newBullet());
    }
}

2. 重构控制器的按键处理逻辑

添加状态变量跟踪发射任务,区分按键按下与释放事件,避免重复创建任务:

// 控制器类中添加成员变量,维护当前发射任务和定时器
private Timer shootTimer;
private ShootBullet currentShootTask;

// 重写按键处理方法,区分按下和释放事件
private void handleKeyEvent(KeyEvent k) {
    switch(k.getCode()){
        case LEFT:
            if (k.getEventType() == KeyEvent.KEY_PRESSED) {
                this.model.spaceshipLeft();
            }
            break;
        case RIGHT:
            if (k.getEventType() == KeyEvent.KEY_PRESSED) {
                this.model.spaceshipRight();
            }
            break;
        case UP:
            if (k.getEventType() == KeyEvent.KEY_PRESSED) {
                this.model.spaceshipUp();
            }
            break;
        case DOWN:                     
            if (k.getEventType() == KeyEvent.KEY_PRESSED) {
                this.model.spaceshipDown();
            }
            break;
        case SPACE:
            if (k.getEventType() == KeyEvent.KEY_PRESSED) {
                startShooting();
            } else if (k.getEventType() == KeyEvent.KEY_RELEASED) {
                stopShooting();
            }
            break;
    }
    view.update();      
}

// 启动发射逻辑:仅当无活跃任务时才创建并启动定时任务
private void startShooting() {
    if (shootTimer == null || currentShootTask == null || currentShootTask.isCancelled()) {
        shootTimer = new Timer();
        currentShootTask = new ShootBullet(this.model);
        // 0延迟立即发射第一枚,之后每2000毫秒(2秒)发射一枚
        shootTimer.scheduleAtFixedRate(currentShootTask, 0, 2000);
        update();
    }
}

// 停止发射逻辑:取消并清理定时任务
private void stopShooting() {
    if (currentShootTask != null) {
        currentShootTask.cancel();
        currentShootTask = null;
    }
    if (shootTimer != null) {
        shootTimer.cancel();
        shootTimer = null;
    }
}

3. 调整事件绑定

确保视图绑定的是KEY_PRESSED和KEY_RELEASED事件,而不是仅绑定单次触发的事件(比如KEY_TYPED),这样才能正确响应长按和松开操作。

内容的提问来源于stack exchange,提问作者Uri Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 19:31:14