一.设计目标.
一个简单的猜数游戏系统,通过GUI界面设计来和用于进行一个简单的游戏交互。游戏会随机生成一个0-100的数字,在用户猜数字的过程中会相应的提示所输入的数字太大或者太小,在猜中时会提示恭喜语句并给出分数并记录在txt文件中。其中,分数为百分制,分数设置为时间50分,每过一秒扣除1分,最低为0分;次数50分,猜错一次扣除一分,最低为0分。文件中记录时间,次数以及成绩。
二.功能简介图![](https://img-blog.csdnimg.cn/direct/77556ec302aa471f839733252f16441e.png)
三.运行结果截图
四.代码
/**
* 游戏界面的 Java Swing 应用程序。
* 允许用户猜测随机生成的数字,记录猜测次数和时间,计算并展示得分,保存分数记录到文件,并展示历史分数记录。
* 游戏还包括弹出对话框,显示猜中时的得分和当前分数排名。
*/
// 导入 Java Swing 和其他必要的包
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.Scanner;
/**
* 猜数游戏的图形用户界面类
*/
//继承自JFrame
public class GuessingGameGUI extends JFrame {
// 存储随机生成的数字
private int randomNumber;
// 记录玩家猜测次数
private int attempts;
// 记录游戏开始时间
private long startTime;
// 输入猜测的文本框
private JTextField guessField;
// 猜测按钮
private JButton guessButton;
// 显示猜测结果的标签
private JLabel resultLabel;
// 显示历史分数记录的文本区域
private JTextArea scoreArea;
// 分数记录文件路径
private static final String SCORE_FILE = "scores.txt";
/**
* 构造函数,初始化游戏界面
*/
public GuessingGameGUI() {
super("猜数游戏"); // 设置窗口标题为“猜数游戏”
// 设置窗口关闭行为
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 当点击窗口关闭按钮时,退出程序
// 设置窗口大小
setSize(400, 300); // 设置窗口宽度为400像素,高度为300像素
// 居中显示
setLocationRelativeTo(null); // 将窗口居中显示
// 设置布局为网格布局,5行1列
setLayout(new GridLayout(5, 1)); // 使用网格布局管理器,将组件分为5行1列的网格
// 初始化各组件
guessField = new JTextField(); // 创建一个文本框用于输入猜测的数字
guessButton = new JButton("猜!"); // 创建一个按钮,点击后执行猜数字操作
resultLabel = new JLabel("欢迎来到猜数游戏!"); // 创建一个标签,用于显示游戏结果
scoreArea = new JTextArea(); // 创建一个文本区域,用于显示游戏历史记录
scoreArea.setRows(20); // 设置文本区域的行数为20
scoreArea.setEditable(false); // 设置文本区域不可编辑
JScrollPane scrollPane = new JScrollPane(scoreArea); // 创建一个滚动面板,将文本区域添加到滚动面板中
guessField.setPreferredSize(new Dimension(200, 30)); // 设置文本框的首选尺寸为200x30像素
guessButton.setPreferredSize(new Dimension(80, 30)); // 设置按钮的首选尺寸为80x30像素
resultLabel.setPreferredSize(new Dimension(400, 30)); // 设置标签的首选尺寸为400x30像素
scrollPane.setMinimumSize(new Dimension(400, 160)); // 设置滚动面板的最小尺寸为400x160像素
// 添加各组件到界面中
add(guessField); // 将文本框添加到窗口中
add(guessButton); // 将按钮添加到窗口中
add(resultLabel); // 将标签添加到窗口中
add(scrollPane); // 将滚动面板添加到窗口中
// 为按钮添加监听器
guessButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
checkGuess(); // 当按钮被点击时,调用checkGuess方法进行猜数字操作
}
});
// 为文本框添加监听器
guessField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
checkGuess(); // 当文本框内容发生变化时,调用checkGuess方法进行猜数字操作
}
});
// 初始化游戏状态和历史记录
initializeGame(); // 调用initializeGame方法初始化游戏状态和历史记录
updateScoreArea(); // 调用updateScoreArea方法更新游戏历史记录显示区域的内容
}
/**
* 初始化游戏状态
*/
private void initializeGame() {
generateRandomNumber();
updateResultLabel("欢迎来到猜数游戏!");
}
/**
* 生成随机数
*/
private void generateRandomNumber() {
Random random = new Random();
randomNumber = random.nextInt(100) + 1;
attempts = 0;
startTime = System.currentTimeMillis();
}
/**
* 检查猜测
*/
private void checkGuess() {
try {
int userGuess = Integer.parseInt(guessField.getText());
attempts++;
if (userGuess == randomNumber) {
handleWin();
} else {
updateResultLabel(userGuess > randomNumber ? "太大了,再试试吧!" : "太小了,再试试吧!");
}
guessField.setText("");
} catch (NumberFormatException ex) {
updateResultLabel("请输入有效的整数!");
}
}
/**
* 处理猜测正确
*/
private void handleWin() {
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
double gameTimeInSeconds = totalTime / 1000.0;
int score = calculateScore(attempts, gameTimeInSeconds);
updateResultLabel(String.format("恭喜你猜对了!得分:%d", score));
saveScore(attempts, gameTimeInSeconds, score);
// 弹出对话框,包含当前分数排名
String currentRanking = getCurrentRanking();
JOptionPane.showMessageDialog(this, "恭喜你猜中!得分:" + score + "\n当前分数排名:\n" + currentRanking, "恭喜", JOptionPane.INFORMATION_MESSAGE);
// 重新开始游戏并更新历史记录
initializeGame();
updateScoreArea();
}
/**
* 计算得分
*/
private int calculateScore(int attempts, double gameTimeInSeconds) {
int maxTimeScore = 50;
int maxAttemptsScore = 50;
int timeScore = maxTimeScore - (int) Math.min(gameTimeInSeconds, maxTimeScore);
int attemptsScore = maxAttemptsScore - Math.min(attempts, maxAttemptsScore);
return Math.max(timeScore + attemptsScore, 0);
}
/**
* 保存得分记录到文件
*/
private void saveScore(int attempts, double gameTimeInSeconds, int score) {
try {
FileWriter fileWriter = new FileWriter(SCORE_FILE, true);
PrintWriter printWriter = new PrintWriter(fileWriter);
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
printWriter.printf("[%s] 次数: %d, 时间: %.2f 秒, 得分: %d%n", timestamp, attempts, gameTimeInSeconds, score);
printWriter.close();
} catch (IOException e) {
updateResultLabel("无法保存成绩到文件:" + e.getMessage());
}
}
/**
* 更新结果标签
*/
private void updateResultLabel(String message) {
resultLabel.setText(message);
}
/**
* 更新历史分数记录区域
*/
// 更新分数显示区域的方法
private void updateScoreArea() {
try {
// 创建一个扫描器对象,用于读取分数文件
Scanner scanner = new Scanner(new File(SCORE_FILE));
// 创建一个字符串构建器对象,用于存储历史分数记录
StringBuilder scores = new StringBuilder("历史分数记录:\n");
// 当文件中还有内容时,循环执行以下操作
while (scanner.hasNextLine()) {
// 将文件中的每一行内容添加到字符串构建器中,并在每行后添加换行符
scores.append(scanner.nextLine()).append("\n");
}
// 将字符串构建器中的分数信息转换为字符串,并设置为分数显示区域的文本
scoreArea.setText(scores.toString());
} catch (IOException e) {
// 如果发生输入输出异常,调用更新结果标签方法,显示错误信息
updateResultLabel("无法读取分数文件:" + e.getMessage());
}
}
/**
* 获取当前排名
*/
private String getCurrentRanking() {
// 创建一个字符串构建器对象,用于存储当前排名信息
StringBuilder currentRanking = new StringBuilder();
try {
// 创建一个扫描器对象,用于读取分数文件
Scanner scanner = new Scanner(new File(SCORE_FILE));
// 当文件中还有内容时,循环执行以下操作
while (scanner.hasNextLine()) {
// 将文件中的每一行内容添加到字符串构建器中,并在每行后添加换行符
currentRanking.append(scanner.nextLine()).append("\n");
}
} catch (IOException e) {
// 如果发生输入输出异常,调用更新结果标签方法,显示错误信息
updateResultLabel("无法读取分数文件:" + e.getMessage());
}
// 将字符串构建器中的当前排名信息转换为字符串,并返回
return currentRanking.toString();
}
/**
* 主方法,启动游戏
*/
public static void main(String[] args) {
// 在事件调度线程上创建并显示游戏界面
SwingUtilities.invokeLater(() -> new GuessingGameGUI().setVisible(true));
}
}
五.不足之处
可以继续丰富提示语的内容,第一次猜错和第三次猜错采用不同的鼓励语,在越来越接近生成的随机数时使用不同的评语。第一次直接命中和第十次命中采用不同的恭喜语句。
系统可以进一步设计一个排行榜功能,按照分数从高到低的排行榜功能,并且可以实时更新排行榜。
设计一个用户名输入栏,来记录来自不同用户的使用数据。