当前位置: 首页 > news >正文

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。

相关文章:

  • NLP高频面试题(五十一)——LSTM详解
  • 玩转Docker | 使用Docker部署DashMachine个人书签工具
  • 深度学习3.6 softmax回归的从零开始实现
  • 模拟实现strncat、qsort、atoi
  • 低光环境下双目云台摄像头监控性能解析
  • Element UI、Element Plus 里的表单验证的required必填的属性不能动态响应?
  • 题解:[ABC385F] Visible Buildings
  • GNOME桌面隐藏回收站和分区
  • 赛灵思 XC7K325T-2FFG900I FPGA Xilinx Kintex‑7
  • 基于SpringBoot的中华诗词文化分享平台-项目分享
  • 【FPGA开发】Vivado开发中的LUTRAM占用LUT资源吗
  • FPGA设计 时空变换
  • 前端学习笔记
  • 系统架构师2025年论文《论软件三层结构的设计》
  • Ubuntu24.04安装ROS2问题
  • 服务器上安装maven
  • 题解:P11185 奖牌排序
  • linux下内存地址数学运算
  • HTTP状态码有哪些常见的类型?
  • 搭建 Spark - Local 模式:开启数据处理之旅
  • 普京:俄方积极对待任何和平倡议
  • 18条举措!上海国际金融中心进一步提升跨境金融服务便利化
  • 全球在役最大火电厂被通报
  • 服务业扩大开放,金融、医疗等多领域明确155项试点任务
  • 人民日报读者点题·共同关注:花粉过敏增多,如何看待城市绿化“成长的烦恼”
  • 调查显示特朗普在经济问题上的支持率跌至其总统生涯最低