JAVA程序设计:一篇文章教你完整写出贪吃蛇小游戏

1 运行展示


2 设计思路

        设计一个贪吃蛇游戏,我们应当遵循了一系列步骤来确保游戏的基本功能:蛇的移动、吃苹果、游戏结束判断、以及重新开始游戏。以下是设计思路的分步总结.

        1. 初始化游戏界面

  • 使用Swing组件:利用JFrameJPanel创建游戏窗口和绘图面板。
  • 设置游戏参数:定义蛇的大小、游戏区域的大小、苹果的位置等参数。
  • 初始化游戏状态:设置蛇的初始长度、位置、移动方向等。

        2. 游戏逻辑实现

  • 蛇的移动:通过数组记录蛇身体每一部分的位置,并在每次移动时更新这些位置。
  • 吃苹果:检测蛇头和苹果的位置,若重合则增加蛇的长度,并重新放置苹果。
  • 碰撞检测:检查蛇头是否触碰到边界或自身,若是,则游戏结束。

        3. 绘制游戏界面

  • 重写paintComponent方法:使用Graphics对象绘制蛇、苹果和游戏结束时的提示信息。
  • 使用计时器javax.swing.Timer用于控制游戏的更新频率,每个时间间隔都会重绘界面并更新游戏状态。

        4. 控制游戏

  • 键盘监听:实现KeyListener接口,根据用户的按键输入改变蛇的移动方向。
  • 暂停功能:通过监听特定的按键(如P键)来实现游戏的暂停和继续。

        5. 重新开始游戏

  • 添加按钮:在游戏面板中添加一个重新开始的按钮。
  • 事件监听:为按钮添加ActionListener,在游戏结束时显示按钮,并在点击时重置游戏状态。

3 技术列举

使用到的Java概念

  • 面向对象编程:通过类和对象来模拟游戏中的实体(如蛇、苹果)和行为(如移动、吃苹果)。
  • 事件驱动编程:使用事件监听器来响应用户的输入(键盘事件)和交互(按钮点击事件)。
  • 图形用户界面(GUI):使用Java Swing库来创建和管理窗口、按钮、绘图等GUI组件。
  • 多线程和并发:使用计时器(Timer)来控制游戏逻辑的周期性执行,这涉及到Java的并发编程。

4 核心功能实现思路

          在贪吃蛇游戏中,最主要的两个功能是蛇的移动游戏状态的管理(包括吃苹果和碰撞检测)。这两个功能是游戏运行的核心,决定了游戏的基本玩法和用户体验。

        1. 蛇的移动

       蛇的移动是贪吃蛇游戏的基础。玩家通过控制蛇的移动来吃苹果,游戏的乐趣和挑战性主要来自于如何控制蛇的移动路径和速度。

实现思路

  • 数据结构:使用两个数组分别存储蛇身体每一部分的x和y坐标。蛇头是数组的第一个元素,蛇尾是最后一个元素。
  • 移动逻辑:在每个时间间隔,根据蛇的当前移动方向更新蛇头的位置,然后让蛇身体的每一部分移动到前一部分的位置,实现蛇的整体移动。
  • 方向控制:通过监听键盘事件,允许玩家改变蛇的移动方向,但不能让蛇直接反向移动。

        2. 游戏状态的管理

        游戏状态的管理决定了游戏的进程和结束条件。它包括检测蛇是否吃到苹果以增长身体,以及蛇是否撞到自己或游戏边界导致游戏结束。

实现思路

  • 吃苹果
    • 检测碰撞:在蛇移动后,检查蛇头的位置是否与苹果的位置重合。如果是,表示蛇吃到了苹果。
    • 增长蛇身:增加蛇的长度,并在游戏区域内随机生成一个新的苹果位置。
  • 碰撞检测
    • 边界检测:检查蛇头是否移动出游戏区域的边界。如果是,游戏结束。
    • 自身碰撞检测:检查蛇头是否与蛇身的其他部分位置重合。如果是,同样游戏结束。
  • 游戏结束处理:当游戏结束时,停止游戏循环,可能显示游戏结束的信息,并提供重新开始游戏的选项。

        这两个功能的实现是构建一个可玩的贪吃蛇游戏的关键。它们不仅涉及到游戏的基本逻辑和用户交互,还需要考虑如何在界面上有效地反映游戏状态的变化,提供流畅和有趣的游戏体验。


5 核心功能代码实现

        1. 蛇的移动

private void move() {
        for (int z = dots; z > 0; z--) {
            x[z] = x[(z - 1)];
            y[z] = y[(z - 1)];
        }

        if (leftDirection) {
            x[0] -= DOT_SIZE;
        }

        if (rightDirection) {
            x[0] += DOT_SIZE;
        }

        if (upDirection) {
            y[0] -= DOT_SIZE;
        }

        if (downDirection) {
            y[0] += DOT_SIZE;
        }
    }

        2. 游戏状态的管理

                 1 吃苹果方法

private void checkApple() {
    if ((x[0] == apple_x) && (y[0] == apple_y)) {
        dots++;
        locateApple();
    }
}

                2 碰撞检测方法

private void checkCollision() {
    for (int z = dots; z > 0; z--) {
        if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
            inGame = false;
        }
    }

    if (y[0] >= HEIGHT) {
        inGame = false;
    }

    if (y[0] < 0) {
        inGame = false;
    }

    if (x[0] >= WIDTH) {
        inGame = false;
    }

    if (x[0] < 0) {
        inGame = false;
    }

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

6 全部代码实现

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

public class SnakeGame extends JPanel implements KeyListener, ActionListener {

    private final int WIDTH = 300;
    private final int HEIGHT = 300;
    private final int DOT_SIZE = 10;
    private final int ALL_DOTS = 900;
    private final int RANDOM_POSITION = 29;
    private final int DELAY = 140;
    private final int x[] = new int[ALL_DOTS];
    private final int y[] = new int[ALL_DOTS];
    // 在SnakeGame类中添加
    private JButton restartButton;
    private int dots;
    private int apple_x;
    private int apple_y;

    private boolean leftDirection = false;
    private boolean rightDirection = true;
    private boolean upDirection = false;
    private boolean downDirection = false;
    private boolean inGame = true;
    private boolean paused = false;

    private Timer timer;
    private Random random;

    private ArrayList<Integer> highScores = new ArrayList<>();

    public SnakeGame() {
        initBoard();
    }

    private void initBoard() {
        restartButton = new JButton("Restart");
        restartButton.setBounds(WIDTH / 2 - 50, HEIGHT / 2, 100, 30);
        restartButton.addActionListener(this);
        restartButton.setFocusable(false);
        restartButton.setVisible(false); // 初始时不显示按钮
        this.add(restartButton);
        addKeyListener(this);
        setBackground(Color.black);
        setFocusable(true);
        setPreferredSize(new Dimension(WIDTH, HEIGHT));
        initGame();
    }

    private void initGame() {
        dots = 3;
        for (int z = 0; z < dots; z++) {
            x[z] = 50 - z * 10;
            y[z] = 50;
        }
        locateApple();
        timer = new Timer(DELAY, this);
        timer.start();
    }

    private void restartGame() {
        dots = 3;
        for (int z = 0; z < dots; z++) {
            x[z] = 50 - z * 10;
            y[z] = 50;
        }
        rightDirection = true;
        leftDirection = false;
        upDirection = false;
        downDirection = false;
        inGame = true;
        restartButton.setVisible(false); // 重置游戏时隐藏按钮
        timer.start(); // 重新开始计时
        locateApple(); // 重新放置苹果
    }

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

    private void doDrawing(Graphics g) {
        if (inGame) {
            g.setColor(Color.red);
            g.fillOval(apple_x, apple_y, DOT_SIZE, DOT_SIZE);

            for (int z = 0; z < dots; z++) {
                if (z == 0) {
                    g.setColor(Color.green);
                    g.fillRect(x[z], y[z], DOT_SIZE, DOT_SIZE);
                } else {
                    g.setColor(new Color(45, 180, 0));
                    g.fillRect(x[z], y[z], DOT_SIZE, DOT_SIZE);
                }
            }
            Toolkit.getDefaultToolkit().sync();
        } else {
            gameOver(g);
        }
    }

    private void gameOver(Graphics g) {
        String msg = "Game Over";
        Font small = new Font("Helvetica", Font.BOLD, 14);
        FontMetrics metr = getFontMetrics(small);

        g.setColor(Color.white);
        g.setFont(small);
        g.drawString(msg, (WIDTH - metr.stringWidth(msg)) / 2, HEIGHT / 2);

        checkHighScore();
        displayHighScores(g);
        restartButton.setVisible(true); // 显示重新开始按钮
    }

    private void checkHighScore() {
        highScores.add(dots);
        Collections.sort(highScores, Collections.reverseOrder());

        while (highScores.size() > 3) {
            highScores.remove(highScores.size() - 1);
        }
    }

    private void displayHighScores(Graphics g) {
        g.drawString("High Scores:", 100, HEIGHT / 2 + 50);
        for (int i = 0; i < highScores.size(); i++) {
            g.drawString((i + 1) + ". " + highScores.get(i), 100, HEIGHT / 2 + 65 + i * 15);
        }
    }

    private void checkApple() {
        if ((x[0] == apple_x) && (y[0] == apple_y)) {
            dots++;
            locateApple();
        }
    }

    private void move() {
        for (int z = dots; z > 0; z--) {
            x[z] = x[(z - 1)];
            y[z] = y[(z - 1)];
        }

        if (leftDirection) {
            x[0] -= DOT_SIZE;
        }

        if (rightDirection) {
            x[0] += DOT_SIZE;
        }

        if (upDirection) {
            y[0] -= DOT_SIZE;
        }

        if (downDirection) {
            y[0] += DOT_SIZE;
        }
    }

    private void checkCollision() {
        for (int z = dots; z > 0; z--) {
            if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
                inGame = false;
            }
        }

        if (y[0] >= HEIGHT) {
            inGame = false;
        }

        if (y[0] < 0) {
            inGame = false;
        }

        if (x[0] >= WIDTH) {
            inGame = false;
        }

        if (x[0] < 0) {
            inGame = false;
        }

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

    private void locateApple() {
        int r = (int) (Math.random() * RANDOM_POSITION);
        apple_x = ((r * DOT_SIZE));

        r = (int) (Math.random() * RANDOM_POSITION);
        apple_y = ((r * DOT_SIZE));
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (inGame) {
            checkApple();
            checkCollision();
            move();
        } else if (e.getSource() == restartButton) {
            restartGame();
        }
        repaint();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();

        if ((key == KeyEvent.VK_LEFT) && (!rightDirection)) {
            leftDirection = true;
            upDirection = false;
            downDirection = false;
        }

        if ((key == KeyEvent.VK_RIGHT) && (!leftDirection)) {
            rightDirection = true;
            upDirection = false;
            downDirection = false;
        }

        if ((key == KeyEvent.VK_UP) && (!downDirection)) {
            upDirection = true;
            rightDirection = false;
            leftDirection = false;
        }

        if ((key == KeyEvent.VK_DOWN) && (!upDirection)) {
            downDirection = true;
            rightDirection = false;
            leftDirection = false;
        }

        if (key == KeyEvent.VK_P) {
            paused = !paused;
            if (paused) {
                timer.stop();
            } else {
                timer.start();
            }
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

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

7 总结

        本文提供了一个贪吃蛇游戏的完整Java实现,包括游戏设计思路关键功能的实现方法、以及完整的代码。游戏使用Java Swing库构建图形用户界面,通过面向对象编程、事件驱动编程、图形用户界面(GUI)设计、以及多线程和并发控制,实现了一个基本但功能完整的贪吃蛇游戏。游戏的核心功能包括蛇的移动吃苹果碰撞检测游戏状态管理、以及游戏的重新开始功能。

  • 19
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
抱歉,作为AI语言模型,我无法编写和运行Java程序。但是,我可以为您提供以下Java代码作为参考,以编写贪吃蛇小游戏: ``` import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.Random; public class SnakeGame extends JFrame implements ActionListener { //定义游戏区域的大小 private final int WIDTH = 600; private final int HEIGHT = 600; //定义每个方块的大小 private final int BLOCKSIZE = 20; //定义游戏区域中的方块数 private final int BLOCKS = 30; //定义蛇的初始长度 private final int INITLENGTH = 3; //定义蛇的方向 private final int RIGHT = 0; private final int LEFT = 1; private final int UP = 2; private final int DOWN = 3; //定义游戏用的计时器 private Timer timer; //定义游戏区域中的蛇 private Snake snake; //定义游戏区域中的食物 private Food food; //定义游戏是否结束 private boolean gameOver; public SnakeGame() { //设置窗口的大小和标题 setSize(WIDTH, HEIGHT); setTitle("贪吃蛇小游戏"); //设置窗口的关闭方式 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //创建游戏区域中的蛇和食物 snake = new Snake(INITLENGTH); food = new Food(); //创建游戏用的计时器 timer = new Timer(100, this); //启动计时器 timer.start(); //将游戏区域设置为可见 setVisible(true); } public void paint(Graphics g) { //调用父类的paint方法 super.paint(g); //绘制游戏区域中的蛇和食物 snake.paint(g); food.paint(g); } public void actionPerformed(ActionEvent e) { //判断游戏是否结束 if (gameOver) { //停止计时器 timer.stop(); //弹出游戏结束的提示框 JOptionPane.showMessageDialog(this, "游戏结束"); } else { //让蛇移动 snake.move(); //判断蛇是否吃到了食物 if (snake.eat(food)) { //重新生成食物 food.generate(); } //判断蛇是否撞墙或自己的身体 if (snake.hitWall() || snake.hitBody()) { //游戏结束 gameOver = true; } //重新绘制游戏区域 repaint(); } } private class Snake { //蛇的身体,用一个链表来表示 private LinkedList<Point> body; //蛇的方向 private int direction; public Snake(int length) { //创建蛇的身体 body = new LinkedList<Point>(); for (int i = 0; i < length; i++) { body.add(new Point(BLOCKS / 2 - i, BLOCKS / 2)); } //设置蛇的方向为右 direction = RIGHT; } public void paint(Graphics g) { //绘制蛇的身体 for (Point p : body) { g.fillRect(p.x * BLOCKSIZE, p.y * BLOCKSIZE, BLOCKSIZE, BLOCKSIZE); } } public void move() { //获得蛇头的位置 Point head = body.getFirst(); //根据方向移动蛇头 if (direction == RIGHT) { head = new Point(head.x + 1, head.y); } else if (direction == LEFT) { head = new Point(head.x - 1, head.y); } else if (direction == UP) { head = new Point(head.x, head.y - 1); } else if (direction == DOWN) { head = new Point(head.x, head.y + 1); } //将新的蛇头插入到链表的开头 body.addFirst(head); //删除链表的最后一个元素,即蛇尾 body.removeLast(); } public boolean eat(Food food) { //判断蛇是否吃到了食物 if (body.getFirst().equals(food.getPosition())) { //将食物添加到链表的开头 body.addFirst(food.getPosition()); return true; } else { return false; } } public boolean hitWall() { //判断蛇是否撞墙 Point head = body.getFirst(); if (head.x < 0 || head.x >= BLOCKS || head.y < 0 || head.y >= BLOCKS) { return true; } else { return false; } } public boolean hitBody() { //判断蛇是否撞到自己的身体 Point head = body.getFirst(); for (int i = 1; i < body.size(); i++) { if (head.equals(body.get(i))) { return true; } } return false; } public void setDirection(int direction) { //设置蛇的方向 this.direction = direction; } } private class Food { //食物的位置 private Point position; public Food() { //随机生成食物的位置 generate(); } public void paint(Graphics g) { //绘制食物 g.setColor(Color.RED); g.fillRect(position.x * BLOCKSIZE, position.y * BLOCKSIZE, BLOCKSIZE, BLOCKSIZE); } public void generate() { //随机生成食物的位置 Random rand = new Random(); int x = rand.nextInt(BLOCKS); int y = rand.nextInt(BLOCKS); position = new Point(x, y); } public Point getPosition() { //返回食物的位置 return position; } } public static void main(String[] args) { //创建游戏对象 new SnakeGame(); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤尘Java

感谢认可!感谢您的打赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值