## 一、项目简介

这是一个基于 **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 | 游戏结束后重新开始 |
| ← 返回按钮 | 返回主菜单 |

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐