如何在JavaFX中实现按键长按时按固定间隔触发射击逻辑

千静同学_9622

千静同学_9622

2026-01-13

234人浏览

原创

如何在JavaFX中实现按键长按时按固定间隔触发射击逻辑

本文讲解如何通过监听 keyevent 的 key_pressed/key_released 事件,配合定时器控制子弹发射频率,避免 space 键长按时连续无节制发射,实现每2秒发射一发子弹的稳定效果。

在 JavaFX(或 Swing)游戏开发中,直接响应 KeyEvent.KEY_TYPED 或在 KEY_PRESSED 中无条件调用 shoot(),会导致操作系统级的键盘重复输入干扰——即用户长按空格时,系统会以默认速率(如 500ms 间隔)持续触发事件,造成子弹“连发成线”。根本解法是脱离键盘自动重复机制,改由程序自主控制发射节奏。

✅ 正确做法:状态驱动 + 定时调度

你需要维护一个“空格键是否被按下”的布尔状态,并仅在 KEY_PRESSED 时启动周期性发射任务,在 KEY_RELEASED 时取消它:

// 在 Controller 类中添加字段
private Timer shootTimer;
private boolean isSpacePressed = false;

// 修改 key event 处理逻辑(推荐使用 setOnKeyPressed / setOnKeyReleased)
view.getScene().setOnKeyPressed(event -> {
    if (event.getCode() == KeyCode.SPACE && !isSpacePressed) {
        isSpacePressed = true;
        startShooting();
    }
});

view.getScene().setOnKeyReleased(event -> {
    if (event.getCode() == KeyCode.SPACE && isSpacePressed) {
        isSpacePressed = false;
        stopShooting();
    }
});

?️ 封装可控的射击定时器

避免使用无限循环的 TimerTask(如原 ShootBullet 中的 while(true)),这会阻塞线程、浪费 CPU 且难以管理。应使用标准 Timer + TimerTask 执行固定延迟的单次发射:

Java Api Extractor
Java Api Extractor

从 Java 项目中提取 Controller 层接口定义为 JSON 数据。

下载
private void startShooting() {
    // 每 2000ms 触发一次 newBullet(),不重复执行 tick/update 等主循环逻辑
    shootTimer = new Timer("ShootingTimer", true);
    shootTimer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            Platform.runLater(() -> model.newBullet()); // 确保 UI 线程安全
        }
    }, 0, 2000); // 首次立即触发,之后每 2 秒一次
}

private void stopShooting() {
    if (shootTimer != null) {
        shootTimer.cancel();
        shootTimer = null;
    }
}
⚠️ 注意事项:勿在 TimerTask 中调用 model.tick() 或 controller::update —— 这些属于主游戏循环职责,与射击逻辑无关;原 ShootBullet 类混淆了“游戏更新”和“射击触发”,必须剥离。Platform.runLater(...) 是 JavaFX 必需的线程安全包装,确保 newBullet() 在 JavaFX Application Thread 中执行。Timer 使用 true 参数表示为守护线程,防止程序退出时定时器阻止 JVM 关闭。

✅ 补充优化建议

  • 若需更精确的帧同步(如与 60 FPS 渲染对齐),可改用 AnimationTimer,在 handle(long now) 中基于时间差判断是否达到 2s 间隔;
  • 可引入 lastShotTime 时间戳 + System.nanoTime() 实现无定时器的轻量去抖,适合简单场景;
  • 为提升手感,可增加“首次射击延迟”(如按下后 300ms 再开始周期发射),避免误触。

通过将按键状态与发射节奏解耦,你就能彻底摆脱操作系统键盘重复率的限制,实现精准、可配置、易维护的射击逻辑。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.25

354

5

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

4458

6

Java 并发编程高级实践
Java 并发编程高级实践

本专题深入讲解 Java 在高并发开发中的核心技术,涵盖线程模型、Thread 与 Runnable、Lock 与 synchronized、原子类、并发容器、线程池(Executor 框架)、阻塞队列、并发工具类(CountDownLatch、Semaphore)、以及高并发系统设计中的关键策略。通过实战案例帮助学习者全面掌握构建高性能并发应用的工程能力。

2025.12.01

563

16

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

2026.10.28

40

16

Vite 如何搭配 Vue.js
Vite 如何搭配 Vue.js

Vite搭配Vue.js专题汇集创建Vue 3项目、配置开发环境、路径别名、Vue插件、代理、多环境构建与调试的已审核文章,帮助读者完成从初始化到日常开发的配置。

2026.10.28

20

15

Vite 项目目录怎么看
Vite 项目目录怎么看

Vite项目目录专题汇集入口文件、src源码、public静态资源、路径别名、项目根目录和构建输出目录相关的已审核文章,帮助读者理解各文件夹的用途与引用方式。

2026.10.28

0

10

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

2026.10.22

120

11

Gradle Wrapper配置教程
Gradle Wrapper配置教程

本专题介绍Gradle Wrapper的基本作用,涵盖文件组成、生成方法、版本配置和任务调用,帮助你理解项目如何使用指定的Gradle版本,减少不同开发环境带来的构建差异。

2026.10.22

80

10

Golangci-lint检查器使用教程
Golangci-lint检查器使用教程

本专题整理Golangci-lint常用Linters的用途与配置方法,覆盖错误处理、无效代码、安全、复杂度、命名和依赖检查,帮助项目合理选规则。

2026.10.22

60

15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习