Java Swing贪吃蛇游戏完整实现(MVC架构+MySQL排行榜+音效系统)
## 一、项目简介
这是一个基于 **Java Swing** 开发的经典贪吃蛇游戏,采用 **MVC架构** 设计,支持三种难度选择、MySQL排行榜存储、音效系统和多线程优化。该项目适合Java初学者学习GUI开发、设计模式和数据库操作。
## 二、功能特性
- 🎮 **完整游戏逻辑**:蛇的移动、吃食物、碰撞检测、游戏结束判定
- ⚡ **三种难度选择**:简单(250ms/帧)、中等(150ms/帧)、困难(80ms/帧)
- 🏆 **分数系统**:实时分数显示、本地文件存储最高分
- 📊 **MySQL排行榜**:支持全服排行榜和各难度排行榜
- 🔊 **音效系统**:开始游戏、吃食物、碰撞、游戏结束等音效
- 🎨 **现代化界面**:圆润的蛇设计、苹果食物、渐变背景、半透明遮罩
- ⏸️ **暂停/继续**:支持游戏暂停和继续功能
## 三、技术栈
- **语言**: Java SE 17
- **GUI框架**: Java Swing
- **设计模式**: MVC(Model-View-Controller)
- **数据库**: MySQL 8.0+
- **多线程**: SwingWorker
- **数据结构**: LinkedList
## 四、架构设计
### MVC模式
本项目采用经典的MVC架构模式,将游戏逻辑、界面渲染和用户交互分离:
```
┌─────────────────────────────────────────────────────────┐
│ View │
│ GameWindow + GamePanel │
│ (界面渲染、用户输入、音效播放) │
└───────────────────┬─────────────────────────────────────┘
│ 更新界面
│ 获取输入
▼
┌─────────────────────────────────────────────────────────┐
│ Controller │
│ GameController │
│ (协调模型和视图、处理游戏逻辑) │
└───────────────────┬─────────────────────────────────────┘
│ 更新状态
│ 获取数据
▼
┌─────────────────────────────────────────────────────────┐
│ Model │
│ Snake + food + GameState │
│ (数据模型、业务逻辑、状态管理) │
└─────────────────────────────────────────────────────────┘
```
### 核心类职责
| 类名 | 职责 | 所属层 |
|------|------|--------|
| GameController | 游戏核心逻辑,处理键盘事件 | Controller |
| GameWindow | 主窗口管理,界面切换 | View |
| GamePanel | 游戏画布,渲染蛇和食物 | View |
| Snake | 蛇的数据模型和移动逻辑 | Model |
| food | 食物生成和位置管理 | Model |
| GameState | 游戏状态封装 | Model |
| GameWorker | 后台线程,游戏循环 | Controller |
## 五、核心代码解析
### 1. 游戏主循环(SwingWorker优化)
使用 `SwingWorker` 进行多线程优化,游戏逻辑在后台线程执行,UI更新在事件调度线程执行:
```java
public class GameWorker extends SwingWorker<GameState, GameState> {
private final GameController controller;
private volatile boolean running = true;
@Override
protected GameState doInBackground() throws Exception {
while (running && !isCancelled()) {
if (controller.isGameRunning()) {
controller.update();
publish(new GameState(
controller.getSnake().getBody(),
controller.getFood().getPosition(),
controller.getScore(),
controller.isGameOver()
));
}
Thread.sleep(controller.getDifficulty().getDelay());
}
return null;
}
@Override
protected void process(List<GameState> chunks) {
for (GameState state : chunks) {
controller.getGamePanel().updateState(state);
}
}
}
```
**设计思路**:`doInBackground()` 在后台线程执行游戏逻辑,`process()` 在EDT线程更新UI,避免界面卡顿。
### 2. 蛇的移动逻辑
使用 `LinkedList` 实现蛇身的高效增删操作:
```java
public void move(Direction direction) {
Node head = snakeBody.getFirst();
Node newHead = new Node(
head.getX() + direction.getX(),
head.getY() + direction.getY()
);
snakeBody.addFirst(newHead);
if (isFoodEaten(newHead, food)) {
score += difficulty.getScorePerFood();
food.generateNewPosition(snakeBody);
} else {
snakeBody.removeLast();
}
}
```
**技术亮点**:
- `LinkedList.addFirst()` 和 `removeLast()` 时间复杂度均为 O(1)
- 蛇身增长时只添加头部,不删除尾部
- 正常移动时添加头部并删除尾部
### 3. 碰撞检测
实现蛇与墙壁、蛇与自身的碰撞检测:
```java
public boolean checkCollision() {
Node head = snakeBody.getFirst();
// 边界碰撞
if (head.getX() < 0 || head.getX() >= WIDTH ||
head.getY() < 0 || head.getY() >= HEIGHT) {
return true;
}
// 自身碰撞
for (int i = 1; i < snakeBody.size(); i++) {
Node bodyPart = snakeBody.get(i);
if (head.getX() == bodyPart.getX() &&
head.getY() == bodyPart.getY()) {
return true;
}
}
return false;
}
```
### 4. MySQL数据库操作
使用 `PreparedStatement` 防止SQL注入:
```java
public void saveScore(String playerName, int score, Difficulty difficulty) {
String insertSQL = "INSERT INTO high_scores " +
"(player_name, score, difficulty, date_time) VALUES (?, ?, ?, NOW())";
try (PreparedStatement pstmt = connection.prepareStatement(insertSQL)) {
pstmt.setString(1, playerName);
pstmt.setInt(2, score);
pstmt.setString(3, difficulty.name());
pstmt.executeUpdate();
} catch (SQLException e) {
System.err.println("保存分数失败: " + e.getMessage());
}
}
```
**安全措施**:
- 使用 `PreparedStatement` 预编译语句
- 配置文件读取数据库密码,不硬编码
- `.gitignore` 排除敏感配置文件
### 5. 音效系统
使用 Java Sound API 生成音效:
```java
public void playEatSound() {
try {
AudioFormat format = new AudioFormat(44100, 16, 1, true, false);
SourceDataLine line = AudioSystem.getSourceDataLine(format);
line.open(format);
line.start();
byte[] sound = new byte[44100 / 50];
for (int i = 0; i < sound.length; i++) {
sound[i] = (byte) (Math.sin(i * 440 * 2 * Math.PI / 44100) * 127);
}
line.write(sound, 0, sound.length);
line.stop();
line.close();
} catch (Exception e) {
e.printStackTrace();
}
}
```
## 六、数据库设计
### 表结构
```sql
CREATE TABLE high_scores (
id INT AUTO_INCREMENT PRIMARY KEY,
player_name VARCHAR(50) NOT NULL,
score INT NOT NULL,
difficulty VARCHAR(20) NOT NULL,
date_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
```
### 配置方式
创建 `db.properties` 文件:
```properties
db.password=your_mysql_password
```
> **注意**:`db.properties` 文件已加入 `.gitignore`,不会被提交到版本控制。
## 七、运行说明
### 环境要求
- JDK 17+
- MySQL 8.0+(可选,用于排行榜功能)
### 运行方式
**方式一:使用批处理脚本**
```bash
双击 run_game.bat
```
**方式二:手动编译运行**
```bash
javac -encoding UTF-8 -d out/production/snakeGrme src/com/zut/*.java
java -Dfile.encoding=UTF-8 -cp "out/production/snakeGrme;lib/mysql-connector-java-8.3.0.jar" com.zut.main
```
### 操作说明
| 按键 | 功能 |
|------|------|
| ↑ ↓ ← → | 控制蛇的移动方向 |
| P | 暂停 / 继续游戏 |
| R | 游戏结束后重新开始 |
| ← 返回按钮 | 返回主菜单 |
