JavaFX 实战:从零打造一个功能丰富的英文“刽子手”(Hangman)游戏

大家好!今天我们要挑战一个经典的单词猜谜游戏——“刽子手”(Hangman),并使用 JavaFX 这个强大的 GUI 工具包来赋予它现代化的交互体验。这个项目不仅有趣,而且是学习和实践 JavaFX 核心概念的绝佳途径,涵盖了布局管理、自定义绘制、事件处理、状态管理等多个方面。

我们将构建的这个版本不仅仅是基础功能,还包含了:

  • 单词分类选择: 增加游戏的可玩性和重玩价值。
  • 图形化绞刑架: 使用 Canvas 动态绘制小人被“吊起”的过程,提供直观的视觉反馈。
  • 屏幕虚拟键盘: 提供界面内交互,更适合触屏或纯鼠标操作。
  • 清晰的状态反馈: 实时显示猜测进度、错误次数、猜错的字母等。

无论你是想系统学习 JavaFX,还是寻找一个有一定复杂度的练手项目,这篇文章都将为你提供详细的实现步骤和深入的技术解析。

一、 设计蓝图:游戏规则与界面构思

在动手编码前,清晰的设计是成功的关键。

游戏规则核心:

  1. 选词: 程序从预设的单词库(按分类)中随机选择一个秘密单词。
  2. 显示: 单词以隐藏形式(如下划线 _)展示给玩家,非字母字符(如空格、连字符)直接显示。
  3. 猜测: 玩家通过点击虚拟键盘上的字母进行猜测。
  4. 反馈:
    • 猜对: 单词中所有对应的下划线被替换为正确的字母。
    • 猜错: 错误次数增加,并在画布上绘制“小人”的一部分。猜错的字母会被记录并显示。
  5. 胜负条件:
    • 胜利: 在错误次数达到上限前,猜出所有字母。
    • 失败: 错误次数达到上限(通常是6次,对应小人的6个部分),游戏结束,显示答案。
  6. 重玩: 提供“新游戏”功能。

界面布局规划 (BorderPane):

  • 顶部 (Top): 使用 VBox 放置单词分类选择 ComboBox 和一个主要的 Label (statusLabel) 用于显示游戏提示信息。
  • 左侧 (Left): 使用 VBox 放置 Canvas (hangmanCanvas) 用于绘制绞刑架和小人。
  • 中部 (Center): 使用 VBox 放置核心信息:隐藏的单词 (wordLabel)、错误次数统计 (errorsLabel)、猜错的字母列表 (wrongGuessesLabel) 以及“新游戏”按钮 (newGameButton)。
  • 底部 (Bottom): 使用 TilePane (keyboardPane) 自动排列 A-Z 的字母按钮,形成虚拟键盘。
    在这里插入图片描述

二、 JavaFX 实现深度剖析

现在,让我们深入代码,逐一解析关键技术的实现。

1. 项目基础与状态管理

我们创建 HangmanGameFX_EN 类继承自 Application。核心的游戏状态由以下成员变量维护:

private Map<String, List<String>> wordCategories = new HashMap<>(); // 单词库
private String currentCategory = "Animals"; // 当前分类
private String secretWord;         // 秘密单词 (大写)
private StringBuilder displayedWord; // 显示给玩家的单词 (带下划线)
private int errors;                // 当前错误次数
private Set<Character> guessedLetters; // 已猜字母集合 (高效去重)
private boolean gameOver;          // 游戏结束标志
// ... UI 元素引用 ...
private Map<Character, Button> keyboardButtons = new HashMap<>(); // 键盘按钮引用
  • wordCategories: 使用 Map 存储分类和对应的 List<String>,结构清晰。
  • displayedWord: 使用 StringBuilder 而非 String,因为需要频繁修改其中的字符(替换下划线),StringBuilder 性能更好。
  • guessedLetters: 使用 Set<Character> 存储已猜字母,利用 Set 的特性可以快速判断一个字母是否已被猜过(contains 操作效率高)。
  • keyboardButtons: 使用 Map<Character, Button> 存储虚拟键盘上每个字母按钮的引用,键是字母本身,值是对应的 Button 对象。这使得我们可以通过字母快速找到并禁用对应的按钮。
2. 构建动态用户界面

UI 的构建被拆分到各个 create...Pane() 方法中。

  • 分类选择 (createTopPane): ComboBox<String> 控件绑定 wordCategories 的键集。通过 setOnAction 监听选择变化,一旦改变,就更新 currentCategory 并调用 initializeGame() 开始基于新分类的游戏。
  • 单词显示 (createCenterPane): wordLabel 使用等宽字体 (Monospaced),确保每个下划线 _ 占用的宽度一致,视觉效果更好。
  • 虚拟键盘 (createKeyboardPane):
    • 使用 TilePane 是个巧妙的选择。它会自动将子节点(按钮)排列成网格状,只需设置 setPrefColumns(期望的列数)和间距 (setHgap, setVgap),布局非常方便。
    • 循环创建 A-Z 按钮,为每个按钮设置 setOnAction,调用 handleGuess(letter) 并传入对应的字母。同时,将按钮存入 keyboardButtons Map。
3. Canvas 绘图:动态的绞刑架

这是游戏最具视觉特色的部分。

  • 设置 (createHangmanPane): 创建 Canvas 并获取其 GraphicsContext (gc)。设置线条宽度和颜色。
  • 绘制绞刑架 (drawGallows): 在游戏初始化时调用,使用 gc.strokeLine() 绘制几条直线构成基本的绞刑架结构。坐标计算基于 CANVAS_WIDTHCANVAS_HEIGHT 的比例,使得图形能适应画布大小。
  • 绘制小人 (drawHangmanPart): 这个方法根据传入的 errorCount (1-6) 绘制小人的一个新部分(头、身体、四肢)。使用 switch 语句,每个 case 对应一个错误阶段,调用 gc.strokeOval() 画头,gc.strokeLine() 画身体和四肢。坐标同样是相对计算的。
  • 清空画布 (clearCanvas): 在每次开始新游戏时,需要调用 gc.clearRect() 清除上一次绘制的内容。
// 在 drawHangmanPart(int errorCount) 中
gc.setStroke(HANGMAN_COLOR); // 确保颜色正确
switch (errorCount) {case 1: // Headgc.strokeOval(headX - headRadius, headY - headRadius, headRadius * 2, headRadius * 2);break;case 2: // Bodygc.strokeLine(headX, bodyStartY, headX, bodyEndY);break;// ... case 3, 4, 5, 6 for arms and legs ...
}
4. 核心游戏逻辑:处理猜测 (handleGuess)

这是响应玩家点击键盘按钮的核心方法,逻辑严谨性至关重要:

private void handleGuess(char letter) {// 1. 状态检查: 游戏是否结束?字母是否已猜过?if (gameOver) return;letter = Character.toUpperCase(letter); // 统一转大写if (guessedLetters.contains(letter)) {// ... (提示已猜过) ...return;}// 2. 更新状态: 添加到已猜集合,禁用对应按钮guessedLetters.add(letter);keyboardButtons.get(letter).setDisable(true);// 3. 检查猜测是否正确boolean found = false;for (int i = 0; i < secretWord.length(); i++) {if (secretWord.charAt(i) == letter) {// 关键:更新 displayedWord 中对应位置的下划线int displayIndex = i * 2; // 乘以2是因为每个字符后有空格if (displayIndex < displayedWord.length()) {displayedWord.setCharAt(displayIndex, letter);}found = true;}}// 4. 更新单词显示 UIwordLabel.setText(displayedWord.toString());// 5. 根据猜测结果更新状态和反馈if (found) {// ... (提示猜对,更新状态标签颜色) ...if (checkWin()) endGame(true); // 检查是否胜利} else {errors++;// ... (更新错误标签,绘制小人部分,更新猜错字母列表,提示猜错) ...if (checkLoss()) endGame(false); // 检查是否失败}
}

关键点解析:

  • 状态优先检查: 首先判断游戏是否结束以及字母是否已猜,避免无效操作。
  • 状态更新原子性: 将字母加入 guessedLetters 和禁用按钮紧密关联。
  • displayedWord 更新: 遍历 secretWord,找到匹配字母后,计算其在 displayedWord 中的正确索引(考虑到空格,是 i * 2)并替换下划线。这是保证单词正确显示的核心。
  • 分支处理: 清晰地分为 found (猜对) 和 else (猜错) 两个分支,分别处理 UI 反馈、状态更新(errors++)和胜负检查。
5. 胜负判断与游戏结束
  • checkWin() 实现非常简洁,只要 displayedWord 中不再包含 _,就意味着所有字母都已猜出,玩家获胜。
  • checkLoss() 同样简单,只要 errors 达到 MAX_ERRORS,玩家失败。
  • endGame(boolean won) 负责游戏结束时的收尾工作:设置 gameOver 标志,禁用所有键盘按钮和分类选择,并根据 won 参数显示最终的胜利或失败信息(失败时揭示答案)。
6. 游戏初始化与重置 (initializeGame)

提供良好的“再来一局”体验很重要。initializeGame 方法做了所有必要的重置工作:

  • 重置错误次数、已猜字母集合、gameOver 标志。
  • 调用 chooseNewWord() 获取新单词。
  • 重新生成带下划线的 displayedWord
  • 重置所有 UI 元素到初始状态(标签文本、画布、键盘按钮可用性)。

三、 总结与展望

通过这个 Hangman 项目,我们不仅实现了一个经典游戏,更重要的是,我们深入实践了 JavaFX 的许多核心功能:

  • 布局系统: BorderPane, VBox, HBox, TilePane 的组合使用。
  • 控件交互: Button, Label, ComboBox 的事件处理和状态更新。
  • 自定义绘图: 利用 CanvasGraphicsContext 实现动态图形绘制。
  • 状态管理: 通过成员变量和枚举(虽然此版本未使用枚举,但概念相通)清晰地管理游戏进程。
  • 数据结构应用: Map 用于单词库和按钮引用,Set 用于高效存储已猜字母,StringBuilder 用于高效构建显示单词。

这个项目也为进一步探索留下了空间:

  • 美化与主题: 应用 CSS 打造更个性化的外观。
  • 动画效果: 为小人绘制、字母显示等添加过渡动画。
  • 音效反馈: 增加点击、猜对、猜错、游戏结束的音效。
  • 单词库管理: 从文件加载单词,甚至允许用户添加自定义单词或分类。
  • 更智能的提示: 例如提供一个“提示”按钮,随机显示一个未猜中的字母(并计为一次错误)。

希望这篇详细的开发日志能帮助你理解使用 JavaFX 构建交互式应用的具体过程,并激发你动手尝试和创造的热情。Happy coding!


附注: 上述代码片段是说明性的,完整的可运行代码文前资源文件中的java完整示例。确保你的开发环境已正确配置 JavaFX。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/77930.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【自我介绍前端界面分享】附源码

我用夸克网盘分享了「800套HTML模板」&#xff0c;链接&#xff1a;https://pan.quark.cn/s/a205a794552c <!DOCTYPE HTML> <html> <head> <title>Miniport</title> <meta http-equiv"content-type" content&q…

安宝特分享|AR智能装备赋能企业效率跃升

AR装备开启智能培训新时代 在智能制造与数字化转型浪潮下&#xff0c;传统培训体系正面临深度重构。安宝特基于工业级AR智能终端打造的培训系统&#xff0c;可助力企业构建智慧培训新生态。 AR技术在不同领域的助力 01远程指导方面 相较于传统视频教学的单向输出模式&#x…

今日html笔记

原手写笔记 ------------------------------------------------------------------------------------------------------- 关于超链接的使用 <a href"https://www.luogu.com.cn/" target"_blank">//href属性指定了超链接的目标地址,即当用户点击超…

【人工智能】Ollama 负载均衡革命:多用户大模型服务的高效调度与优化

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在 多用户大模型推理 场景下,负载均衡 是确保高并发、低延迟的关键挑战。本文以 Ollama(一个流行的本地大模型运行框架)为例,深入探讨 …

线上救急-AWS限频

线上救急-AWS限频 问题 在一个天气炎热的下午&#xff0c;我正喝着可口可乐&#xff0c;悠闲地看着Cursor生成代码&#xff0c;忽然各大群聊中出现了加急➕全体的消息&#xff0c;当时就心里一咯噔&#xff0c;点开一看&#xff0c;果然&#xff0c;线上服务出问题&#xff0…

Maven 项目中引入本地 JAR 包

在日常开发过程中&#xff0c;我们有时会遇到一些未上传到 Maven 中央仓库或公司私有仓库的 JAR 包&#xff0c;比如第三方提供的 SDK 或自己编译的库。这时候&#xff0c;我们就需要将这些 JAR 包手动引入到 Maven 项目中。本文将介绍两种常见方式&#xff1a;将 JAR 安装到本…

解锁webpack:对html、css、js及图片资源的抽离打包处理

面试被问到webpack&#xff0c;可别只知道说 HtmlWebpackPlugin 了哇。 前期准备 安装依赖 npm init -y npm install webpack webpack-cli --save-dev配置打包命令 // package.json {"scripts": {// ... 其他配置信息"build": "webpack --mode pr…

SpringBoot整合SSE,基于okhttp

一、引入依赖 <dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.10.0</version> </dependency> <dependency><groupId>com.squareup.okhttp3</groupId><…

【哈希表】1399. 统计最大组的数目

1399. 统计最大组的数目 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数 n 。请你先求出从 1 到 n 的每个整数 10 进制表示下的数位和&#xff08;每一位上的数字相加&#xff09;&#xff0c;然后把数位和相等的数字放到同一个组中。 请你统计每个组中的数字数目&…

手动实现LinkedList

前言 大家好&#xff0c;我是Maybe。最近在学习数据结构中的链表&#xff0c;自己手动实现了一个LinkedList。我想与大家分享一下。 思维导图 代码部分 package Constant;public class constant {public static final String INDEX_IS_WRONG"输入的下标不合法"; }p…

如何检查浏览器是否启用了WebGL2.0硬件加速

一:WebGL Inspector使用 打开 Chrome 或 Edge(推荐使用 Chromium 内核浏览器)。 安装插件: 👉 Spector.js on Chrome Web Store 安装完成后,在浏览器工具栏看到绿色的 S 图标 二:捕获 WebGL 渲染帧 打开你要分析的 Web3D 网站(比如 https://3dviewer.net)。 点击浏…

“时间”,在数据处理中的真身——弼马温一般『无所不能』(DeepSeek)

电子表格时间处理真理&#xff1a;数值存储最瘦身&#xff0c;真身闯关通四海。 笔记模板由python脚本于2025-04-23 22:25:59创建&#xff0c;本篇笔记适合喜欢在电子表格中探求时间格式的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验…

AXOP39062: 25MHz轨到轨输入输出双通道运算放大器

AXOP39062是用于低压应用(1.5V~5.5V)的双通道运算放大器&#xff0c;具有轨到轨的输入输出工作范围&#xff0c;非常适合需要小尺寸、大容性负载驱动能力的低压应用。产品具有25MHz的增益带宽&#xff0c;具有优异的噪声性能和极低的失真度。 主要特性 轨到轨的输入输出范围低…

基于大模型的胃食管反流病全周期预测与诊疗方案研究

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 二、胃食管反流病概述 2.1 疾病定义与分类 2.2 流行病学特征 2.3 发病机制 三、大模型技术原理与应用基础 3.1 大模型简介 3.2 适用于胃食管反流病预测的大模型类型 3.3 数据收集与预处理 四、大模型在胃食…

西门子S7-200SMART 控制Profinet闭环步进MD-4250-PN (1)电机及专栏介绍

一、前言 本系列是我继 《西门子S7-1200PLC 控制步进电机 MD-4240-PN》系列专栏后&#xff0c;新开的一篇专栏。 系列的主题围绕 S7-200SMART Profinet闭环步进(MD-4250-PN) 触摸屏的硬件&#xff0c;预计作四篇文章&#xff0c;分别为&#xff1a;专栏介绍、硬件介绍、PLC…

bedtools coverage 获取每个位置的测序深度

1.bedtools 文档 $ bedtools --version bedtools v2.31.1coverage Compute the coverage over defined intervals. Usage:bedtools coverage [OPTIONS] -a <FILE> \-b <FILE1, FILE2, ..., FILEN>(or):coverageBed [OPTIONS] -a <FILE> \-b <FILE1,…

反向代理和DDNS的区别是什么?

反向代理&#xff08;Reverse Proxy&#xff09;和动态域名解析&#xff08;DDNS&#xff0c;Dynamic Domain Name System&#xff09;是两种不同的网络技术&#xff0c;虽然它们都与外部访问内部服务相关&#xff0c;但解决的问题和应用场景完全不同。具体区别如下&#xff1a…

缩放点积注意力

Scaled Dot-Product Attention 论文地址 https://arxiv.org/pdf/1706.03762 注意力机制介绍 缩放点积注意力是Transformer模型的核心组件&#xff0c;用于计算序列中不同位置之间的关联程度。其核心思想是通过查询向量&#xff08;query&#xff09;和键向量&#xff08;key&am…

可吸收聚合物:医疗科技与绿色未来的交汇点

可吸收聚合物&#xff08;Biodegradable Polymers&#xff09;作为生物医学工程的核心材料&#xff0c;正引领一场从“金属/塑料植入物”到“智能降解材料”的范式转移。根据QYResearch&#xff08;恒州博智&#xff09;预测&#xff0c;2031年全球可吸收聚合物市场销售额将突破…

房地产项目绩效考核管理制度与绩效提升

房地产项目绩效考核管理制度的核心目的是通过合理的绩效考核机制&#xff0c;提升项目的整体运作效率&#xff0c;并鼓励项目团队成员的积极性。该制度适用于所有房地产项目部工作人员&#xff0c;涵盖了项目经理和项目成员的考核。考核的主要内容包括项目经理和项目部成员的工…