用java写一个贪吃蛇游戏

贪吃蛇小游戏

**

1. 项目概述

本贪吃蛇游戏是基于Java Swing框架开发的经典游戏实现,具有完整的游戏流程和用户交互功能。游戏采用面向对象的设计思想,实现了蛇的移动、食物生成、碰撞检测等核心机制,并提供了美观的图形界面和流畅的游戏体验。

2. 系统架构

2.1 类结构设计

mermaid
classDiagram
    class SnakeGame {
        -TILE_SIZE: int
        -WIDTH: int
        -HEIGHT: int
        -ALL_TILES: int
        -DELAY_SLOW: int
        -DELAY_MEDIUM: int
        -DELAY_FAST: int
        -x: int[]
        -y: int[]
        -bodyParts: int
        -applesEaten: int
        -appleX: int
        -appleY: int
        -currentDelay: int
        -direction: char
        -running: boolean
        -inStartMenu: boolean
        -timer: Timer
        -random: Random
        +SnakeGame()
        +startGame()
        +newApple()
        +moveSnake()
        +checkApple()
        +checkCollisions()
        -gameOver()
    }

    class GamePanel {
        -selectedSpeed: int
        +GamePanel()
        +paintComponent(Graphics)
        -drawStartMenu(Graphics)
        -drawGame(Graphics)
        -drawGameOver(Graphics)
    }

    class GameLoop {
        +actionPerformed(ActionEvent)
    }

    SnakeGame --> GamePanel
    SnakeGame --> GameLoop
    GamePanel --|> JPanel
    GameLoop ..|> ActionListener

2.2 关键数据结构

坐标数组

x[]y[]:存储蛇身体各部分的坐标
长度固定为ALL_TILES(WIDTH*HEIGHT)
实际使用长度由bodyParts控制

游戏状态变量

running:标识游戏是否进行中
inStartMenu:标识是否在开始菜单
direction:当前移动方向(‘U’,‘D’,‘L’,‘R’)

3. 核心功能实现

3.1 游戏初始化

public SnakeGame() {
            
    random = new Random();
    this.setTitle("贪吃蛇游戏");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setResizable(false);

    // 初始化蛇的位置(居中)
    for (int i = 0; i < bodyParts; i++) {
            
        x[i] = (WIDTH/2 - i) * TILE_SIZE;
        y[i] = (HEIGHT/2) * TILE_SIZE;
    }

    GamePanel gamePanel = new GamePanel();
    this.add(gamePanel);
    this.pack();
    this.setSize(WIDTH * TILE_SIZE, HEIGHT * TILE_SIZE + 30);
    this.setLocationRelativeTo(null);
    this.setVisible(true);
}

创建随机数生成器
设置窗口基本属性
初始化蛇的位置(居中水平排列)
创建并添加游戏面板
设置窗口大小和位置

3.2 游戏主循环

private class GameLoop implements ActionListener {
            
    @Override
    public void actionPerformed(ActionEvent e) {
            
        if (running) {
            
            moveSnake();
            checkApple();
            checkCollisions();
            repaint();
        }
    }
}

移动蛇:更新蛇身坐标
检查苹果:判断是否吃到食物
碰撞检测:检查是否撞墙或自身
重绘界面:更新游戏画面

3.3 蛇移动算法

public void moveSnake() {
            
    // 从尾部开始更新位置
    for (int i = bodyParts; i > 0; i--) {
            
        x[i] = x[i - 1];
        y[i] = y[i - 1];
    }

    // 根据方向移动头部
    switch (direction) {
            
        case 'U': y[0] -= TILE_SIZE; break;
        case 'D': y[0] += TILE_SIZE; break;
        case 'L': x[0] -= TILE_SIZE; break;
        case 'R': x[0] += TILE_SIZE; break;
    }
}

采用数组移位方式实现蛇身移动
从尾部开始,每个部分移动到前一个部分的位置
头部根据当前方向移动一个格子

3.4 食物生成

public void newApple() {
            
    boolean onSnake;
    do {
            
        onSnake = false;
        appleX = random.nextInt(WIDTH) * TILE_SIZE;
        appleY = random.nextInt(HEIGHT) * TILE_SIZE;

        for (int i = 0; i < bodyParts; i++) {
            
            if (appleX == x[i] && appleY == y[i]) {
            
                onSnake = true;
                break;
            }
        }
    } while (onSnake);
}

使用do-while循环确保食物不会生成在蛇身上
随机生成坐标并检查是否与蛇身重叠
直到找到有效位置才结束循环

3.5 碰撞检测

public void checkCollisions() {
            
    // 检查撞到自己
    for (int i = bodyParts; i > 0; i--) {
            
        if (x[0] == x[i] && y[0] == y[i]) {
            
            gameOver();
            return;
        }
    }

    // 检查撞墙
    if (x[0] < 0 || x[0] >= WIDTH * TILE_SIZE ||
            y[0] < 0 || y[0] >= HEIGHT * TILE_SIZE) {
            
        gameOver();
    }
}

自身碰撞:检查头部是否与任何身体部分重合
墙壁碰撞:检查头部是否超出游戏边界
任一条件满足即触发游戏结束

4. 用户交互设计

4.1 输入处理

this.addKeyListener(new KeyAdapter() {
            
    @Override
    public void keyPressed(KeyEvent e) {
            
        if (inStartMenu) {
            
            // 开始菜单按键处理
            switch (e.getKeyCode()) {
            
                case KeyEvent.VK_UP:
                    selectedSpeed = (selectedSpeed - 1 + 3) % 3;
                    repaint();
                    break;
                case KeyEvent.VK_DOWN:
                    selectedSpeed = (selectedSpeed + 1) % 3;
                    repaint();
                    break;
                case KeyEvent.VK_SPACE:
                    // 根据选择设置速度
                    switch (selectedSpeed) {
            
                        case 0: currentDelay = DELAY_SLOW; break;
                        case 1: currentDelay = DELAY_MEDIUM; break;
                        case 2: currentDelay = DELAY_FAST; break;
                    }
                    startGame();
                    break;
            }
        } else if (!running && e.getKeyCode() == KeyEvent.VK_SPACE) {
            
            // 重置游戏
            for (int i = 0; i < bodyParts; i++) {
            
                x[i] = (WIDTH/2 - i) * TILE_SIZE;
                y[i] = (HEIGHT/2) * TILE_SIZE;
            }
            bodyParts = 3;
            applesEaten = 0;
            direction = 'R';
            inStartMenu = true;
            repaint();
        } else if (running) {
            
            // 游戏中的按键处理
            switch (e.getKeyCode()) {
            
                case KeyEvent.VK_LEFT:
                    if (direction != 'R') direction = 'L';
                    break;
                case KeyEvent.VK_RIGHT:
                    if (direction != 'L') direction = 'R';
                    break;
                case KeyEvent.VK_UP:
                    if (direction != 'D') direction = 'U';
                    break;
                case KeyEvent.VK_DOWN:
                    if (direction != 'U') direction = 'D';
                    break;
            }
        }
    }
});

开始菜单:上下键选择速度,空格键确认
游戏中:方向键控制移动方向
游戏结束:空格键返回主菜单
防止180度转向(不能直接反向移动)

4.2 界面绘制

开始菜单
private void drawStartMenu(Graphics g) {
            
    // 半透明背景
    g.setColor(new Color(0, 0, 0, 200));
    g.fillRect(0, 0, getWidth(), getHeight());

    // 标题
    g.setColor(Color.GREEN);
    Font titleFont = new Font("微软雅黑", Font.BOLD, 50);
    g.setFont(titleFont);
    FontMetrics metrics = g.getFontMetrics();
    String title = "贪吃蛇游戏";
    g.drawString(title, (getWidth() - metrics.stringWidth(title)) / 2, getHeight() / 3);

    // 速度选择
    Font speedFont = new Font("微软雅黑", Font.BOLD, 30);
    g.setFont(speedFont);
    metrics = g.getFontMetrics();

    String[] speedOptions = {
            "慢速", "中速", "快速"};
    int startY = getHeight() / 2;

    for (int i = 0; i < speedOptions.length; i++) {
            
        if (i == selectedSpeed) {
            
            g.setColor(Color.YELLOW);
            g.fillRect(getWidth() / 2 - 100, startY + i * 40 - 25, 200, 35);
            g.setColor(Color.BLACK);
        } else {
            
            g.setColor(Color.WHITE);
        }
        g.drawString(speedOptions[i], (getWidth() - metrics.stringWidth(speedOptions[i])) / 2, startY + i * 40);
    }

    // 提示
    Font hintFont = new Font("微软雅黑", Font.PLAIN, 20);
    g.setFont(hintFont);
    metrics = g.getFontMetrics();
    g.setColor(Color.WHITE);
    String hint = "按空格键开始游戏";
    int hintX = (getWidth() - metrics.stringWidth(hint)) / 2;
    int hintY = getHeight() * 3 / 4;
    g.drawString(hint, hintX, hintY);
}
游戏界面
private void drawGame(Graphics g) {
            
    // 绘制游戏元素
    g.setColor(Color.RED);
    g.fillOval(appleX, appleY, TILE_SIZE, TILE_SIZE);

    for (int i = 0; i < bodyParts; i++) {
            
        if (i == 0) {
            
            g.setColor(Color.GREEN);
        } else {
            
            g.setColor(new Color(45, 180, 0));
        }
        g.fillRect(x[i], y[i], TILE_SIZE, TILE_SIZE);
    }

    g.setColor(Color.WHITE);
    Font font = new Font("微软雅黑", Font.BOLD, 20);
    g.setFont(font);
    g.drawString("得分: " + applesEaten, 10, 20);
}
游戏结束界面
private void drawGameOver(Graphics g) {
            
    // 半透明背景
    g.setColor(new Color(0, 0, 0, 150));
    g.fillRect(0, 0, getWidth(), getHeight());

    // 设置字体
    Font font = new Font("微软雅黑", Font.BOLD, 50);
    g.setFont(font);
    FontMetrics metrics = g.getFontMetrics();

    // Game Over文字
    g.setColor(Color.RED);
    String gameOverText = "游戏结束";
    g.drawString(gameOverText, (getWidth() - metrics.stringWidth(gameOverText)) / 2, getHeight() / 2 - 40);

    // 得分显示
    g.setColor(Color.WHITE);
    font = new Font("微软雅黑", Font.BOLD, 30);
    g.setFont(font);
    metrics = g.getFontMetrics();
    String scoreText = "得分: " + applesEaten;
    g.drawString(scoreText, (getWidth() - metrics.stringWidth(scoreText)) / 2, getHeight() / 2 + 20);

    // 重新开始提示
    font = new Font("微软雅黑", Font.PLAIN, 20);
    g.setFont(font);
    metrics = g.getFontMetrics();
    String restartText = "按空格键返回主菜单";
    g.drawString(restartText, (getWidth() - metrics.stringWidth(restartText)) / 2, getHeight() / 2 + 70);
}

5. 总结

本贪吃蛇游戏实现完整,功能完善,代码结构清晰。通过合理的类设计和状态管理,实现了流畅的游戏体验。游戏界面美观,操作简单,是学习Java Swing和游戏开发的优秀示例。后续可通过数据结构优化和功能扩展进一步提升游戏性能和可玩性。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容