如何通过KeyEvent实现长按空格键每2秒发射一次飞船子弹?
问题修复方案
问题根源
- 长按空格键时,系统会重复触发
KEY_PRESSED事件,原代码每次触发都会调用shoot(),而shoot()每次都会新建一个定时任务并启动,导致多个任务同时发射子弹,形成连续串射的问题。 - 原
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
相关产品推荐
相关产品推荐

