作为Java课程的小作业,不想做太复杂的管理系统,就选了贪吃蛇这个经典小游戏。全程用Swing做界面,没用到任何第三方库,花了三天时间搞定了基本玩法。虽然没上线也没推广,纯当练手项目,但开发过程中踩了不少坑,记录下来说不定能帮到同样在学Java的同学。

请添加图片描述
在开发过程中,我尝试结合飞算JavaAI辅助开发,显著提升了开发效率,尤其是在代码优化和问题排查方面提供了不少帮助。

先确定核心功能

贪吃蛇的核心玩法玩法其实很简单,我梳理了必须实现的功能:

  • 蛇能自己移动,方向可以通过方向键控制
  • 随机生成食物,蛇吃到食物后身体变长
  • 撞到边界或自己的身体就算游戏结束
  • 显示当前分数(吃一个食物得10分)
  • 支持暂停/继续和重新开始游戏

技术选型

纯Java原生实现,没用到任何框架:

  • 界面:Swing(JFrame、JPanel这些基础组件)
  • 绘图:Graphics类(绘制蛇、食物和分数)
  • 事件监听:KeyListener(处理方向键输入)
  • 线程:Timer(控制蛇的移动速度)

核心功能实现过程

1. 数据模型设计

首先借助飞算JavaAI设计蛇和食物的数据结构,AI给出的方案非常合理,尤其是在面向对象设计方面给了我很多启发:

// 蛇的身体由多个节点组成,每个节点有x和y坐标
class SnakeNode {
    private int x;  // x坐标
    private int y;  // y坐标
    
    public SnakeNode(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    // getters and setters
    public int getX() { return x; }
    public int getY() { return y; }
    public void setX(int x) { this.x = x; }
    public void setY(int y) { this.y = y; }
    
    // 飞算JavaAI建议添加的equals方法,方便后续判断碰撞
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        SnakeNode snakeNode = (SnakeNode) obj;
        return x == snakeNode.x && y == snakeNode.y;
    }
    
    // 飞算JavaAI建议添加的hashCode方法
    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

// 游戏数据模型
class GameModel {
    // 游戏区域大小(格子数)
    public static final int GRID_WIDTH = 30;
    public static final int GRID_HEIGHT = 20;
    // 每个格子的像素大小
    public static final int GRID_SIZE = 20;
    
    private List<SnakeNode> snake;  // 蛇的身体
    private SnakeNode food;         // 食物
    private int direction;          // 蛇的移动方向(上、下、左、右)
    private int score;              // 分数
    private boolean isGameOver;     // 游戏是否结束
    private boolean isPaused;       // 游戏是否暂停
    private int speed;              // 游戏速度,飞算JavaAI建议添加
    
    // 方向常量
    public static final int UP = 0;
    public static final int DOWN = 1;
    public static final int LEFT = 2;
    public static final int RIGHT = 3;
    
    public GameModel() {
        // 初始化蛇(从中间开始,长度为3)
        snake = new LinkedList<>();
        snake.add(new SnakeNode(GRID_WIDTH/2, GRID_HEIGHT/2));
        snake.add(new SnakeNode(GRID_WIDTH/2 - 1, GRID_HEIGHT/2));
        snake.add(new SnakeNode(GRID_WIDTH/2 - 2, GRID_HEIGHT/2));
        
        // 初始方向向右
        direction = RIGHT;
        
        // 生成第一个食物
        generateFood();
        
        // 初始化分数和游戏状态
        score = 0;
        isGameOver = false;
        isPaused = false;
        speed = 150;  // 初始速度,飞算JavaAI建议添加
    }
    
    // 生成食物(确保不会出现在蛇身上)
    public void generateFood() {
        Random random = new Random();
        int x, y;
        boolean onSnake;
        
        do {
            onSnake = false;
            x = random.nextInt(GRID_WIDTH);
            y = random.nextInt(GRID_HEIGHT);
            
            // 检查食物是否生成在蛇身上
            // 飞算JavaAI建议使用contains方法简化代码
            onSnake = snake.contains(new SnakeNode(x, y));
        } while (onSnake);
        
        food = new SnakeNode(x, y);
    }
    
    // 蛇移动一步
    public void move() {
        if (isGameOver || isPaused) return;
        
        // 获取蛇头
        SnakeNode head = snake.get(0);
        int newX = head.getX();
        int newY = head.getY();
        
        // 根据方向计算新头部位置
        switch (direction) {
            case UP:
                newY--;
                break;
            case DOWN:
                newY++;
                break;
            case LEFT:
                newX--;
                break;
            case RIGHT:
                newX++;
                break;
        }
        
        // 检查是否撞墙
        if (newX < 0 || newX >= GRID_WIDTH || newY < 0 || newY >= GRID_HEIGHT) {
            isGameOver = true;
            return;
        }
        
        // 检查是否撞到自己
        SnakeNode newHead = new SnakeNode(newX, newY);
        if (snake.contains(newHead)) {
            isGameOver = true;
            return;
        }
        
        // 添加新头部
        snake.add(0, newHead);
        
        // 检查是否吃到食物
        if (newX == food.getX() && newY == food.getY()) {
            // 吃到食物,加分并生成新食物
            score += 10;
            generateFood();
            // 飞算JavaAI建议:随着分数增加提高速度
            increaseSpeed();
        } else {
            // 没吃到食物,移除尾部
            snake.remove(snake.size() - 1);
        }
    }
    
    // 飞算JavaAI建议添加的速度增加方法
    private void increaseSpeed() {
        // 每得50分,速度提升10%
        if (score % 50 == 0 && speed > 50) {
            speed = (int)(speed * 0.9);
        }
    }
    
    // 改变方向(不能直接反向)
    public void changeDirection(int newDirection) {
        // 比如当前向左,不能直接向右
        if ((direction == LEFT && newDirection == RIGHT) || 
            (direction == RIGHT && newDirection == LEFT) || 
            (direction == UP && newDirection == DOWN) || 
            (direction == DOWN && newDirection == UP)) {
            return;
        }
        direction = newDirection;
    }
    
    // 重置游戏
    public void reset() {
        snake.clear();
        snake.add(new SnakeNode(GRID_WIDTH/2, GRID_HEIGHT/2));
        snake.add(new SnakeNode(GRID_WIDTH/2 - 1, GRID_HEIGHT/2));
        snake.add(new SnakeNode(GRID_WIDTH/2 - 2, GRID_HEIGHT/2));
        direction = RIGHT;
        generateFood();
        score = 0;
        isGameOver = false;
        isPaused = false;
        speed = 150;  // 重置速度
    }
    
    // getters and setters
    public List<SnakeNode> getSnake() { return snake; }
    public SnakeNode getFood() { return food; }
    public int getScore() { return score; }
    public boolean isGameOver() { return isGameOver; }
    public boolean isPaused() { return isPaused; }
    public void setPaused(boolean paused) { isPaused = paused; }
    public int getSpeed() { return speed; }  // 飞算JavaAI建议添加
}

2. 游戏界面和绘制逻辑

在实现界面绘制时,飞算JavaAI帮我优化了绘图逻辑,解决了画面闪烁问题:

class GamePanel extends JPanel {
    private GameModel model;
    // 颜色定义
    private static final Color SNAKE_COLOR = new Color(76, 175, 80);    // 绿色
    private static final Color FOOD_COLOR = new Color(244, 67, 54);     // 红色
    private static final Color BG_COLOR = new Color(230, 230, 230);     // 浅灰色
    private static final Color TEXT_COLOR = new Color(33, 33, 33);      // 深灰色
    private static final Color BORDER_COLOR = new Color(200, 200, 200); // 边框颜色
    
    // 飞算JavaAI建议添加双缓冲解决闪烁问题
    private Image bufferImage;
    private Graphics bufferGraphics;
    
    public GamePanel(GameModel model) {
        this.model = model;
        // 设置面板大小
        setPreferredSize(new Dimension(
            GameModel.GRID_WIDTH * GameModel.GRID_SIZE,
            GameModel.GRID_HEIGHT * GameModel.GRID_SIZE
        ));
        // 设置背景色
        setBackground(BG_COLOR);
        // 飞算JavaAI建议:设置焦点,确保键盘事件能被捕获
        setFocusable(true);
        requestFocusInWindow();
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        // 飞算JavaAI建议使用双缓冲绘制
        if (bufferImage == null) {
            bufferImage = createImage(getWidth(), getHeight());
            bufferGraphics = bufferImage.getGraphics();
        }
        
        // 清空缓冲
        bufferGraphics.setColor(BG_COLOR);
        bufferGraphics.fillRect(0, 0, getWidth(), getHeight());
        
        // 绘制网格线,飞算JavaAI建议添加
        drawGrid(bufferGraphics);
        
        // 绘制蛇
        bufferGraphics.setColor(SNAKE_COLOR);
        for (SnakeNode node : model.getSnake()) {
            bufferGraphics.fillRect(
                node.getX() * GameModel.GRID_SIZE,
                node.getY() * GameModel.GRID_SIZE,
                GameModel.GRID_SIZE - 1,  // 减1是为了让蛇身体节点之间有缝隙
                GameModel.GRID_SIZE - 1
            );
        }
        
        // 绘制食物
        bufferGraphics.setColor(FOOD_COLOR);
        SnakeNode food = model.getFood();
        bufferGraphics.fillOval(
            food.getX() * GameModel.GRID_SIZE,
            food.getY() * GameModel.GRID_SIZE,
            GameModel.GRID_SIZE - 1,
            GameModel.GRID_SIZE - 1
        );
        
        // 绘制分数和速度信息
        bufferGraphics.setColor(TEXT_COLOR);
        bufferGraphics.setFont(new Font("Arial", Font.BOLD, 16));
        bufferGraphics.drawString("分数: " + model.getScore(), 10, 20);
        bufferGraphics.drawString("速度: " + (1000/model.getSpeed()) + "步/秒", 120, 20);
        
        // 游戏结束时显示提示
        if (model.isGameOver()) {
            drawCenteredString(bufferGraphics, "游戏结束!", getWidth()/2, getHeight()/2, 
                              new Font("Arial", Font.BOLD, 24));
            drawCenteredString(bufferGraphics, "按R键重新开始", getWidth()/2, getHeight()/2 + 30,
                              new Font("Arial", Font.PLAIN, 16));
        }
        
        // 暂停时显示提示
        if (model.isPaused() && !model.isGameOver()) {
            drawCenteredString(bufferGraphics, "已暂停", getWidth()/2, getHeight()/2,
                              new Font("Arial", Font.BOLD, 24));
            drawCenteredString(bufferGraphics, "按P键继续", getWidth()/2, getHeight()/2 + 30,
                              new Font("Arial", Font.PLAIN, 16));
        }
        
        // 将缓冲图像绘制到屏幕
        g.drawImage(bufferImage, 0, 0, this);
    }
    
    // 飞算JavaAI建议添加的绘制网格线方法
    private void drawGrid(Graphics g) {
        g.setColor(BORDER_COLOR);
        // 绘制水平线
        for (int y = 0; y <= GameModel.GRID_HEIGHT; y++) {
            g.drawLine(0, y * GameModel.GRID_SIZE, 
                      GameModel.GRID_WIDTH * GameModel.GRID_SIZE, 
                      y * GameModel.GRID_SIZE);
        }
        // 绘制垂直线
        for (int x = 0; x <= GameModel.GRID_WIDTH; x++) {
            g.drawLine(x * GameModel.GRID_SIZE, 0, 
                      x * GameModel.GRID_SIZE, 
                      GameModel.GRID_HEIGHT * GameModel.GRID_SIZE);
        }
    }
    
    // 飞算JavaAI建议添加的居中绘制字符串方法
    private void drawCenteredString(Graphics g, String text, int x, int y, Font font) {
        g.setFont(font);
        FontMetrics metrics = g.getFontMetrics(font);
        int textX = x - metrics.stringWidth(text) / 2;
        int textY = y + (metrics.getAscent() - metrics.getDescent()) / 2;
        g.drawString(text, textX, textY);
    }
    
    @Override
    public void update(Graphics g) {
        // 重写update方法,避免闪烁
        paint(g);
    }
}

3. 游戏主框架和事件处理

主框架实现时,飞算JavaAI帮我优化了事件处理和游戏循环逻辑:

public class SnakeGame extends JFrame {
    private GameModel model;
    private GamePanel panel;
    private Timer timer;  // 控制游戏刷新
    private int highScore;  // 最高分记录,飞算JavaAI建议添加
    
    public SnakeGame() {
        // 初始化模型和面板
        model = new GameModel();
        panel = new GamePanel(model);
        
        // 加载最高分,飞算JavaAI建议添加
        loadHighScore();
        
        // 设置窗口
        setTitle("贪吃蛇");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setResizable(false);  // 窗口不可 resize
        
        // 飞算JavaAI建议添加菜单栏
        createMenuBar();
        
        add(panel);
        pack();
        setLocationRelativeTo(null);  // 窗口居中显示
        
        // 初始化定时器,控制蛇的移动速度
        initTimer();
        
        // 添加键盘监听
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                handleKeyPress(e);
            }
        });
        
        // 窗口关闭时保存最高分,飞算JavaAI建议添加
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                saveHighScore();
            }
        });
    }
    
    // 飞算JavaAI建议添加的创建菜单栏方法
    private void createMenuBar() {
        JMenuBar menuBar = new JMenuBar();
        
        JMenu gameMenu = new JMenu("游戏");
        JMenuItem newGameItem = new JMenuItem("新游戏");
        newGameItem.addActionListener(e -> resetGame());
        
        JMenuItem exitItem = new JMenuItem("退出");
        exitItem.addActionListener(e -> {
            saveHighScore();
            System.exit(0);
        });
        
        gameMenu.add(newGameItem);
        gameMenu.addSeparator();
        gameMenu.add(exitItem);
        
        JMenu helpMenu = new JMenu("帮助");
        JMenuItem aboutItem = new JMenuItem("关于");
        aboutItem.addActionListener(e -> showAboutDialog());
        
        JMenuItem controlsItem = new JMenuItem("操作说明");
        controlsItem.addActionListener(e -> showControlsDialog());
        
        helpMenu.add(controlsItem);
        helpMenu.add(aboutItem);
        
        menuBar.add(gameMenu);
        menuBar.add(helpMenu);
        
        setJMenuBar(menuBar);
    }
    
    // 初始化定时器
    private void initTimer() {
        timer = new Timer(model.getSpeed(), e -> {
            model.move();
            // 检查是否刷新速度
            if (timer.getDelay() != model.getSpeed()) {
                timer.setDelay(model.getSpeed());
            }
            // 检查是否打破最高分
            if (model.getScore() > highScore) {
                highScore = model.getScore();
            }
            panel.repaint();
        });
        timer.start();
    }
    
    // 处理键盘事件
    private void handleKeyPress(KeyEvent e) {
        int key = e.getKeyCode();
        
        // 方向控制
        switch (key) {
            case KeyEvent.VK_UP:
            case KeyEvent.VK_W:  // 支持WASD控制,飞算JavaAI建议添加
                model.changeDirection(GameModel.UP);
                break;
            case KeyEvent.VK_DOWN:
            case KeyEvent.VK_S:
                model.changeDirection(GameModel.DOWN);
                break;
            case KeyEvent.VK_LEFT:
            case KeyEvent.VK_A:
                model.changeDirection(GameModel.LEFT);
                break;
            case KeyEvent.VK_RIGHT:
            case KeyEvent.VK_D:
                model.changeDirection(GameModel.RIGHT);
                break;
            case KeyEvent.VK_P:  // 暂停/继续
            case KeyEvent.VK_SPACE:  // 空格也可暂停,飞算JavaAI建议添加
                model.setPaused(!model.isPaused());
                panel.repaint();
                break;
            case KeyEvent.VK_R:  // 重新开始
                resetGame();
                break;
            case KeyEvent.VK_ESCAPE:  // 退出游戏,飞算JavaAI建议添加
                saveHighScore();
                System.exit(0);
                break;
        }
    }
    
    // 重置游戏
    private void resetGame() {
        model.reset();
        timer.setDelay(model.getSpeed());
        panel.repaint();
    }
    
    // 飞算JavaAI建议添加的加载最高分方法
    private void loadHighScore() {
        try {
            File file = new File("highscore.dat");
            if (file.exists()) {
                try (BufferedReader br = new BufferedReader(new FileReader(file))) {
                    String scoreStr = br.readLine();
                    highScore = Integer.parseInt(scoreStr);
                }
            } else {
                highScore = 0;
            }
        } catch (Exception e) {
            highScore = 0;
            System.err.println("加载最高分失败: " + e.getMessage());
        }
    }
    
    // 飞算JavaAI建议添加的保存最高分方法
    private void saveHighScore() {
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("highscore.dat"))) {
            bw.write(String.valueOf(highScore));
        } catch (Exception e) {
            System.err.println("保存最高分失败: " + e.getMessage());
        }
    }
    
    // 飞算JavaAI建议添加的显示关于对话框方法
    private void showAboutDialog() {
        JOptionPane.showMessageDialog(this,
            "贪吃蛇 v1.0\nJava课程练手项目",
            "关于",
            JOptionPane.INFORMATION_MESSAGE);
    }
    
    // 飞算JavaAI建议添加的显示操作说明方法
    private void showControlsDialog() {
        JOptionPane.showMessageDialog(this,
            "方向键或WASD: 控制蛇移动\n" +
            "空格或P: 暂停/继续游戏\n" +
            "R: 重新开始游戏\n" +
            "ESC: 退出游戏",
            "操作说明",
            JOptionPane.INFORMATION_MESSAGE);
    }
    
    public static void main(String[] args) {
        // 在事件调度线程中启动游戏
        SwingUtilities.invokeLater(() -> {
            new SnakeGame().setVisible(true);
        });
    }
}

飞算JavaAI在开发中的应用体验

在开发这个贪吃蛇游戏的过程中,飞算JavaAI作为辅助工具提供了不少帮助:

  1. 代码优化建议:AI能够识别出代码中可以优化的部分,比如使用contains方法简化碰撞检测,添加equals和hashCode方法提高对象比较效率等。

  2. 功能扩展建议:AI不仅局限于实现基本功能,还提出了很多扩展建议,如添加最高分记录、支持WASD控制、增加游戏难度递增机制等,让游戏更完善。

  3. 最佳实践指导:在Swing界面开发方面,AI提供了双缓冲绘图、避免界面闪烁等最佳实践,解决了我开发中遇到的实际问题。

  4. 代码规范提示:AI建议的代码结构更清晰,命名更规范,帮助我养成良好的编程习惯。

  5. 问题排查帮助:当遇到蛇移动不同步、食物生成在蛇身上等问题时,AI能够快速定位原因并提供解决方案。

当然,AI的建议也需要筛选和判断,不能盲目采纳。在实际开发中,我会结合自己的理解和需求,选择性地应用AI提供的建议。

可以进一步优化的地方

虽然在飞算JavaAI的帮助下已经实现了不少功能,但还有可以完善的地方:

  1. 添加多种游戏模式(如限时模式、障碍模式)
  2. 实现更丰富的食物系统(特殊食物有不同效果)
  3. 添加音效和背景音乐
  4. 优化界面,支持皮肤切换
  5. 实现联网排行榜功能

总体来说,这个贪吃蛇项目是一次很好的Java练手经历。借助飞算JavaAI,我不仅提高了开发效率,还学到了很多Java编程的最佳实践。看到自己开发的小游戏能够正常运行,并且有不错的游戏体验,确实很有成就感。长大,还是挺有成就感的。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐