Java 贪吃蛇游戏

这段 Java 代码实现了一个经典的贪吃蛇游戏。玩家可以使用键盘的上下左右箭头键控制蛇的移动方向,蛇会在游戏面板中移动并尝试吃掉随机生成的食物。每吃掉一个食物,蛇的身体会变长,玩家的得分也会增加。如果蛇撞到自己的身体或者撞到游戏面板的边界,游戏就会结束。

类和方法详细说明

类定义

java

public class SnakeGame extends JPanel implements ActionListener {

  • SnakeGame 类继承自 JPanel,用于创建游戏面板。
  • 实现了 ActionListener 接口,用于处理定时器触发的事件。
常量定义

java

static final int SCREEN_WIDTH = 600;
static final int SCREEN_HEIGHT = 600;
static final int UNIT_SIZE = 25;
static final int GAME_UNITS = (SCREEN_WIDTH * SCREEN_HEIGHT) / UNIT_SIZE;
static final int DELAY = 75;

  • SCREEN_WIDTH 和 SCREEN_HEIGHT:定义游戏面板的宽度和高度,单位为像素。
  • UNIT_SIZE:定义游戏中每个格子的大小,单位为像素。
  • GAME_UNITS:计算游戏面板中总的格子数量。
  • DELAY:定义游戏的延迟时间,单位为毫秒,用于控制蛇的移动速度。
成员变量定义

java

final int x[] = new int[GAME_UNITS];
final int y[] = new int[GAME_UNITS];
int bodyParts = 6;
int applesEaten;
int appleX;
int appleY;
char direction = 'R';
boolean running = false;
Timer timer;
Random random;

  • x[] 和 y[]:存储蛇的身体每个部分的 x 和 y 坐标。
  • bodyParts:蛇的初始身体长度。
  • applesEaten:记录蛇吃掉的食物数量,即玩家的得分。
  • appleX 和 appleY:食物的 x 和 y 坐标。
  • direction:蛇的移动方向,初始为向右('R')。
  • running:表示游戏是否正在运行的标志。
  • timer:定时器,用于控制游戏的更新。
  • random:随机数生成器,用于随机生成食物的位置。
构造函数

java

SnakeGame() {
    random = new Random();
    this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
    this.setBackground(Color.black);
    this.setFocusable(true);
    this.addKeyListener(new MyKeyAdapter());
    startGame();
}

  • 初始化随机数生成器。
  • 设置游戏面板的首选大小、背景颜色。
  • 使游戏面板可获得焦点,以便接收键盘事件。
  • 添加键盘事件监听器。
  • 调用 startGame() 方法开始游戏。
开始游戏方法

java

public void startGame() {
    newApple();
    running = true;
    timer = new Timer(DELAY, this);
    timer.start();
}

  • 调用 newApple() 方法生成新的食物。
  • 将 running 标志设置为 true,表示游戏开始运行。
  • 创建一个定时器,每隔 DELAY 毫秒触发一次 actionPerformed() 方法。
  • 启动定时器。
绘制组件方法

java

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    draw(g);
}

  • 重写 paintComponent() 方法,调用父类的 paintComponent() 方法进行默认绘制。
  • 调用 draw() 方法绘制游戏元素。
绘制游戏元素方法

java

public void draw(Graphics g) {
    if (running) {
        g.setColor(Color.red);
        g.fillOval(appleX, appleY, UNIT_SIZE, UNIT_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], UNIT_SIZE, UNIT_SIZE);
        }

        g.setColor(Color.red);
        g.setFont(new Font("Ink Free", Font.BOLD, 40));
        FontMetrics metrics = getFontMetrics(g.getFont());
        g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics.stringWidth("Score: " + applesEaten)) / 2, g.getFont().getSize());
    } else {
        gameOver(g);
    }
}

  • 如果游戏正在运行:
    • 将画笔颜色设置为红色,绘制一个圆形表示食物。
    • 遍历蛇的身体部分,将蛇头设置为绿色,身体其他部分设置为深绿色,绘制矩形表示蛇的身体。
    • 绘制当前得分,居中显示在游戏面板上方。
  • 如果游戏结束,调用 gameOver() 方法显示游戏结束信息。
生成新食物方法

java

public void newApple() {
    appleX = random.nextInt((int) (SCREEN_WIDTH / UNIT_SIZE)) * UNIT_SIZE;
    appleY = random.nextInt((int) (SCREEN_HEIGHT / UNIT_SIZE)) * UNIT_SIZE;
}

  • 使用随机数生成器随机生成食物的 x 和 y 坐标,确保食物的位置在游戏面板的格子内。
移动蛇的方法

java

public void move() {
    for (int i = bodyParts; i > 0; i--) {
        x[i] = x[i - 1];
        y[i] = y[i - 1];
    }

    switch (direction) {
        case 'U':
            y[0] = y[0] - UNIT_SIZE;
            break;
        case 'D':
            y[0] = y[0] + UNIT_SIZE;
            break;
        case 'L':
            x[0] = x[0] - UNIT_SIZE;
            break;
        case 'R':
            x[0] = x[0] + UNIT_SIZE;
            break;
    }
}

  • 遍历蛇的身体部分,将每个部分的坐标更新为前一个部分的坐标。
  • 根据蛇的移动方向,更新蛇头的坐标。
检查是否吃到食物的方法

java

public void checkApple() {
    if ((x[0] == appleX) && (y[0] == appleY)) {
        bodyParts++;
        applesEaten++;
        newApple();
    }
}

  • 检查蛇头的坐标是否与食物的坐标相同。
  • 如果相同,蛇的身体长度加 1,得分加 1,调用 newApple() 方法生成新的食物。
检查碰撞的方法

java

public void checkCollisions() {
    for (int i = bodyParts; i > 0; i--) {
        if ((x[0] == x[i]) && (y[0] == y[i])) {
            running = false;
        }
    }

    if (x[0] < 0) {
        running = false;
    }
    if (x[0] > SCREEN_WIDTH) {
        running = false;
    }
    if (y[0] < 0) {
        running = false;
    }
    if (y[0] > SCREEN_HEIGHT) {
        running = false;
    }

    if (!running) {
        timer.stop();
    }
}

  • 检查蛇头是否撞到自己的身体,如果是,将 running 标志设置为 false,表示游戏结束。
  • 检查蛇头是否撞到游戏面板的边界,如果是,将 running 标志设置为 false
  • 如果游戏结束,停止定时器。
游戏结束的方法

java

public void gameOver(Graphics g) {
    g.setColor(Color.red);
    g.setFont(new Font("Ink Free", Font.BOLD, 40));
    FontMetrics metrics1 = getFontMetrics(g.getFont());
    g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics1.stringWidth("Score: " + applesEaten)) / 2, g.getFont().getSize());

    g.setColor(Color.red);
    g.setFont(new Font("Ink Free", Font.BOLD, 75));
    FontMetrics metrics2 = getFontMetrics(g.getFont());
    g.drawString("Game Over", (SCREEN_WIDTH - metrics2.stringWidth("Game Over")) / 2, SCREEN_HEIGHT / 2);
}

  • 绘制玩家的最终得分,居中显示在游戏面板上方。
  • 绘制 “Game Over” 信息,居中显示在游戏面板中间。
定时器触发的方法

java

@Override
public void actionPerformed(ActionEvent e) {
    if (running) {
        move();
        checkApple();
        checkCollisions();
    }
    repaint();
}

  • 如果游戏正在运行,调用 move() 方法移动蛇,调用 checkApple() 方法检查是否吃到食物,调用 checkCollisions() 方法检查是否发生碰撞。
  • 调用 repaint() 方法重新绘制游戏面板。
键盘事件监听器类

java

public class MyKeyAdapter extends KeyAdapter {
    @Override
    public void keyPressed(KeyEvent e) {
        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;
        }
    }
}

  • MyKeyAdapter 类继承自 KeyAdapter,用于处理键盘事件。
  • 当用户按下键盘的上下左右箭头键时,根据当前蛇的移动方向,更新蛇的移动方向,避免蛇直接掉头。
主方法

java

public static void main(String[] args) {
    JFrame frame = new JFrame("Snake Game");
    SnakeGame gamePanel = new SnakeGame();
    frame.add(gamePanel);
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
}

  • 创建一个 JFrame 窗口,设置窗口标题为 “Snake Game”。
  • 创建 SnakeGame 类的实例,将其添加到窗口中。
  • 调整窗口大小以适应游戏面板。
  • 设置窗口关闭时退出程序。
  • 禁止用户调整窗口大小。
  • 将窗口居中显示。
  • 显示窗口。

完整代码

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Random;

// 主游戏类
public class SnakeGame extends JPanel implements ActionListener {
    // 定义游戏面板的大小
    static final int SCREEN_WIDTH = 600;
    static final int SCREEN_HEIGHT = 600;
    // 定义每个格子的大小
    static final int UNIT_SIZE = 25;
    // 定义游戏面板的格子数量
    static final int GAME_UNITS = (SCREEN_WIDTH * SCREEN_HEIGHT) / UNIT_SIZE;
    // 定义游戏的延迟时间,控制蛇的移动速度
    static final int DELAY = 75;
    // 存储蛇的身体坐标
    final int x[] = new int[GAME_UNITS];
    final int y[] = new int[GAME_UNITS];
    // 蛇的初始身体长度
    int bodyParts = 6;
    // 食物的数量
    int applesEaten;
    // 食物的 x 坐标
    int appleX;
    // 食物的 y 坐标
    int appleY;
    // 蛇的移动方向,'R' 表示向右
    char direction = 'R';
    // 游戏是否正在运行的标志
    boolean running = false;
    // 定时器,用于控制游戏的更新
    Timer timer;
    // 随机数生成器,用于随机生成食物的位置
    Random random;

    // 构造函数,初始化游戏
    SnakeGame() {
        random = new Random();
        this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
        this.setBackground(Color.black);
        this.setFocusable(true);
        this.addKeyListener(new MyKeyAdapter());
        startGame();
    }

    // 开始游戏的方法
    public void startGame() {
        // 生成新的食物
        newApple();
        running = true;
        // 创建定时器,每隔 DELAY 毫秒触发一次 actionPerformed 方法
        timer = new Timer(DELAY, this);
        timer.start();
    }

    // 绘制游戏组件的方法
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        draw(g);
    }

    // 绘制游戏元素的方法
    public void draw(Graphics g) {
        if (running) {
            // 绘制食物
            g.setColor(Color.red);
            g.fillOval(appleX, appleY, UNIT_SIZE, UNIT_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], UNIT_SIZE, UNIT_SIZE);
            }
            // 绘制当前得分
            g.setColor(Color.red);
            g.setFont(new Font("Ink Free", Font.BOLD, 40));
            FontMetrics metrics = getFontMetrics(g.getFont());
            g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics.stringWidth("Score: " + applesEaten)) / 2, g.getFont().getSize());
        } else {
            gameOver(g);
        }
    }

    // 生成新食物的方法
    public void newApple() {
        appleX = random.nextInt((int) (SCREEN_WIDTH / UNIT_SIZE)) * UNIT_SIZE;
        appleY = random.nextInt((int) (SCREEN_HEIGHT / UNIT_SIZE)) * UNIT_SIZE;
    }

    // 移动蛇的方法
    public void move() {
        for (int i = bodyParts; i > 0; i--) {
            x[i] = x[i - 1];
            y[i] = y[i - 1];
        }

        switch (direction) {
            case 'U':
                y[0] = y[0] - UNIT_SIZE;
                break;
            case 'D':
                y[0] = y[0] + UNIT_SIZE;
                break;
            case 'L':
                x[0] = x[0] - UNIT_SIZE;
                break;
            case 'R':
                x[0] = x[0] + UNIT_SIZE;
                break;
        }
    }

    // 检查是否吃到食物的方法
    public void checkApple() {
        if ((x[0] == appleX) && (y[0] == appleY)) {
            bodyParts++;
            applesEaten++;
            newApple();
        }
    }

    // 检查碰撞的方法
    public void checkCollisions() {
        // 检查蛇头是否撞到自己的身体
        for (int i = bodyParts; i > 0; i--) {
            if ((x[0] == x[i]) && (y[0] == y[i])) {
                running = false;
            }
        }
        // 检查蛇头是否撞到边界
        if (x[0] < 0) {
            running = false;
        }
        if (x[0] > SCREEN_WIDTH) {
            running = false;
        }
        if (y[0] < 0) {
            running = false;
        }
        if (y[0] > SCREEN_HEIGHT) {
            running = false;
        }

        if (!running) {
            timer.stop();
        }
    }

    // 游戏结束的方法
    public void gameOver(Graphics g) {
        // 绘制得分
        g.setColor(Color.red);
        g.setFont(new Font("Ink Free", Font.BOLD, 40));
        FontMetrics metrics1 = getFontMetrics(g.getFont());
        g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics1.stringWidth("Score: " + applesEaten)) / 2, g.getFont().getSize());
        // 绘制游戏结束信息
        g.setColor(Color.red);
        g.setFont(new Font("Ink Free", Font.BOLD, 75));
        FontMetrics metrics2 = getFontMetrics(g.getFont());
        g.drawString("Game Over", (SCREEN_WIDTH - metrics2.stringWidth("Game Over")) / 2, SCREEN_HEIGHT / 2);
    }

    // 定时器触发的方法,处理游戏逻辑
    @Override
    public void actionPerformed(ActionEvent e) {
        if (running) {
            move();
            checkApple();
            checkCollisions();
        }
        repaint();
    }

    // 键盘事件监听器类
    public class MyKeyAdapter extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            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;
            }
        }
    }

    // 主方法,启动游戏
    public static void main(String[] args) {
        JFrame frame = new JFrame("Snake Game");
        SnakeGame gamePanel = new SnakeGame();
        frame.add(gamePanel);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}    

你可能感兴趣的:(python,算法,开发语言)