计算机软件实习项目二 基于Java/Swing的贪吃蛇小游戏

一、项目要求:

1)实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子。玩家能利用上下左右键控制的移动,“蛇”吃到“豆子”后“蛇”身体加长一节,得分增加,“蛇”碰到边界或蛇头与蛇身相撞,“蛇”死亡,游戏结束。

2)进行交互界面的设计,要有开始键、暂停键和停止退出的选项,能够控制游戏进程。对蛇吃到豆子进行分值计算,可以设置游戏速度,游戏音乐等拓展元素。

二、 算法思想:

 

三、代码实现:

  •    (游戏主体框架).MainFrame类:
package Proj_GreedySnake;
/*
 *主框架
 *
 * 已知bug:
 * 方向按键按的太快,蛇会吃到自己
 * */

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;

//继承父类JFrame
public class MainFrame extends JFrame {
    private JPanel Gamepanel;
    private JLabel scorelab;

    private Snake snake;//声明一个蛇的主体
    private Timer timer;//声明一个计时器
    private SnakeNode Food;//声明食物

    private int score = 0;
    private int speed;//初始为normal

    public MainFrame() throws HeadlessException {
        //设置得分版
        initSideBar();
        //设置窗体
        setTitle("贪吃蛇");
        setResizable(false);
        setSize(615 + 200, 640);
        setLayout(null);
        setLocationRelativeTo(null);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setFocusable(true);//设置窗体可获取焦点,若为false,按钮与按键事件会出现冲突

        //设置游戏速度
        setspeed();
        //设置网格,绘制蛇身,食物等操作
        initGamePanel();
        //初始化设置蛇身
        initSnake();
        //初始化设置计时器
        initTimer();
        //设置操纵蛇的方向的键盘事件响应
        setKeyboardListenner();
        //绘制食物到网格上
        initFood();
    }


    private void setspeed() {
        JOptionPane.showMessageDialog(null, "Set your speed",
                "GreedySnake", JOptionPane.WARNING_MESSAGE);
        String[] options = {"easy", "normal", "hard"};
        String temp = (String) JOptionPane.showInputDialog(null, "Choose your rank (don't press cancel butoon)",
                "Set speed", JOptionPane.INFORMATION_MESSAGE, null, options, options[1]);
        switch (temp) {
            case "easy" -> speed = 300;
            case "normal" -> speed = 100;
            case "hard" -> speed = 50;
        }
    }

    private void initFood() {
        Food = new SnakeNode();
        Food.randomLocation();
    }

    private void setKeyboardListenner() {
        addKeyListener(new KeyAdapter() {
            @Override
            //设置每个按钮对应的响应事件(设置方向按钮),同时设置两个反方向的按钮不能一起响应
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()) {
                    case KeyEvent.VK_UP:
                        if (snake.getDirection() != Direction.DOWN)
                            snake.setDirection(Direction.UP);
                        break;
                    case KeyEvent.VK_DOWN:
                        if (snake.getDirection() != Direction.UP)
                            snake.setDirection(Direction.DOWN);
                        break;
                    case KeyEvent.VK_LEFT:
                        if (snake.getDirection() != Direction.RIGHT)
                            snake.setDirection(Direction.LEFT);
                        break;
                    case KeyEvent.VK_RIGHT:
                        if (snake.getDirection() != Direction.LEFT)
                            snake.setDirection(Direction.RIGHT);
                        break;
                }
            }
        });
    }

    private void initTimer() {
        timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                //蛇移动
                snake.move();
                //蛇吃食物
                SnakeNode head = snake.getBody().getFirst();
                if (head.getX() == Food.getX() && head.getY() == Food.getY()) {
                    snake.eatFood();
                    Food.randomLocation();
                    score += 10;
                    scorelab.setText("得分: " + score);
                }
                Gamepanel.repaint();//刷新网格
                //若蛇死了,游戏结束
                if (!snake.isLiving()) {
                    JOptionPane.showMessageDialog(null, "Game Over"+'\n'+"Your score: "+score, "GreedySnake", JOptionPane.WARNING_MESSAGE);
                    timer.cancel();//清除本次定时器任务
                }
            }
        };
        //设置定时任务的执行周期(设置蛇跑的速度)
        timer.scheduleAtFixedRate(timerTask, 0, speed);

    }

    private void initSnake() {
        snake = new Snake();
    }

    private void initGamePanel() {
        Image backImg = ImageUtil.getImage("Proj_GreedySnake/img/background.jpg");
        Image food = ImageUtil.getImage("Proj_GreedySnake/img/food.png");
        Image head = ImageUtil.getImage("Proj_GreedySnake/img/doge.png");

        Gamepanel = new JPanel() {
            @Override
            //绘制网格与蛇身
            public void paint(Graphics g) {
                //刷新网格前,要清除上一次绘制的网格
                g.clearRect(0, 0, 615, 640);
                //绘制背景图
                g.drawImage(backImg, 0, 0, null);
                //绘制网格,如果只想保留背景图可把这段代码删掉
                for (int i = 0; i <= 40; i++) {
                    g.drawLine(0, i * 15, 600, i * 15);
                }
                for (int i = 0; i <= 40; i++) {
                    g.drawLine(i * 15, 0, i * 15, 600);
                }
                //把蛇身绘制上去
                LinkedList<SnakeNode> body = snake.getBody();
                SnakeNode Head = snake.getBody().getFirst();
                g.drawImage(head, Head.getX() * 15, Head.getY() * 15, 15, 15, this);
                for (int i = 1; i < body.size(); i++) {
                    g.setColor(Color.ORANGE);
                    g.fillRoundRect(body.get(i).getX() * 15, body.get(i).getY() * 15, 15, 15, 10, 10);//获取每个蛇身节点的位置,并绘制

                }

                //在绘制食物前先检查蛇身和食物是否重叠
                for (SnakeNode i : snake.getBody()) {
                    if (Food.getX() == i.getX() && Food.getY() == i.getY())
                        Food.randomLocation();
                }
                //绘制食物
                g.drawImage(food, Food.getX() * 15, Food.getY() * 15, 20, 20, null);

            }
        };
        Gamepanel.setSize(615, 640);
        add(Gamepanel);
    }

    private void initSideBar() {
        JButton restart;
        JButton pause;
        JButton keepGo;

        //设置暂停按钮
        pause = new JButton("暂停");
        pause.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                timer.cancel();
                requestFocus();
            }
        });
        pause.setBounds(675, 350, 100, 30);
        add(pause);

        //设置继续按钮
        keepGo = new JButton("继续");
        keepGo.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                timer.cancel();
                initTimer();
                requestFocus();
            }
        });
        keepGo.setBounds(675, 400, 100, 30);
        add(keepGo);

        //设置重开按钮
        restart = new JButton("重新开始");
        restart.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                timer.cancel();
                Food.randomLocation();
                JOptionPane.showMessageDialog(null, "Restart", "GreedySnake", JOptionPane.WARNING_MESSAGE);
                //选择难度
                setspeed();
                //重新初始化蛇
                initSnake();
                //初始化计时器任务
                initTimer();
                //设置点击按钮后,窗口重新获得焦点,否则会与键盘事件冲突
                requestFocus();
                //重新初始化游戏得分
                score = 0;
                scorelab.setText("得分:0");
            }
        });
        restart.setBounds(675, 300, 100, 30);
        add(restart);

        //设置得分框
        scorelab = new JLabel("得分:0");
        scorelab.setFont(new Font("wordtype", Font.BOLD, 15));
        scorelab.setBounds(675, 250, 100, 50);
        add(scorelab);

    }

    public static void main(String[] args) {
        new MainFrame();
    }
}

  • 创建一个Snake类,构造蛇身以及定义蛇的行为和状态:
package Proj_GreedySnake;

import java.util.LinkedList;

/*
 * 使用LinkList构造蛇身
 * 蛇的移动,蛇吃食物和状态检测
 * */
public class Snake {
    private LinkedList<SnakeNode> body;//设的身体
    private Direction direction = Direction.UP;//蛇运动的方向,默认向上

    public LinkedList<SnakeNode> getBody() {
        return body;
    }

    public void setBody(LinkedList<SnakeNode> body) {
        this.body = body;
    }

    public Snake() {
        initSnake();
    }

    public Direction getDirection() {
        return direction;
    }

    public void setDirection(Direction direction) {
        this.direction = direction;
    }

    //初始化蛇身,
    private void initSnake() {
        body = new LinkedList<>();
        //为蛇添加三个SnakeNode,即蛇的身长为3
        body.add(new SnakeNode(20, 15));
        body.add(new SnakeNode(20, 16));
        body.add(new SnakeNode(20, 17));
    }

    //声明一个蛇移动的方法,思路是获取蛇的头部坐标,当进行移动时蛇头坐标发生变化,向蛇头增加一个新的SnakeNode,蛇尾删除节点
    public void move() {
        //如果蛇还活着可以移动
        if (isLiving()) {
            SnakeNode head = body.getFirst();
            switch (direction) {
                case UP -> body.addFirst(new SnakeNode(head.getX(), head.getY() - 1));
                case DOWN -> body.addFirst(new SnakeNode(head.getX(), head.getY() + 1));
                case LEFT -> body.addFirst(new SnakeNode(head.getX() - 1, head.getY()));
                case RIGHT -> body.addFirst(new SnakeNode(head.getX() + 1, head.getY()));
            }
            body.removeLast();
        }
    }

    public void eatFood() {
        SnakeNode head = body.getFirst();
        switch (direction) {
            case UP -> body.addFirst(new SnakeNode(head.getX(), head.getY() - 1));
            case DOWN -> body.addFirst(new SnakeNode(head.getX(), head.getY() + 1));
            case LEFT -> body.addFirst(new SnakeNode(head.getX() - 1, head.getY()));
            case RIGHT -> body.addFirst(new SnakeNode(head.getX() + 1, head.getY()));
        }
    }

    //判断蛇的死亡状态
    public boolean isLiving() {
        boolean isliving = true;
        //如果蛇碰到边界,则isLiving置false
        SnakeNode head = body.getFirst();
        if (head.getX() < 0 || head.getY() < 0 || head.getX() >= 40 || head.getY() >= 40) {
            isliving = false;
        }

        //如果蛇碰到自己身体,也会死亡
        for (int i = 1; i < body.size(); i++) {
            SnakeNode node = body.get(i);
            if (head.getX() == node.getX() && head.getY() == node.getY()) {
                isliving = false;
                break;
            }
        }
        return isliving;
    }

}
  • 创建一个SnakeNode类,获取蛇的坐标,以及为食物生成随机坐标:
package Proj_GreedySnake;
/*
* 创建坐标节点记录蛇的身体的坐标
* */
import java.util.Random;

public class SnakeNode {
    private int x;
    private int y;

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public SnakeNode(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public SnakeNode() {

    }

    //在格子内生成随机坐标
    public void randomLocation(){
        Random random = new Random();
        this.x= random.nextInt(40);
        this.y= random.nextInt(40);
    }
}
  • 创建一个枚举类,设定方向参数
package Proj_GreedySnake;
/*
* 创建枚举类型,为蛇的移动方向设置参量
* */
public enum Direction {
    //方向枚举
    UP,DOWN,RIGHT,LEFT
}
  • 创建一个图片获取类,以获取图片素材
package Proj_GreedySnake;


import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

/**
 * 根据图片的相对路径获取图片
 */
public class ImageUtil {
    // @param imagePath, @return 图片
    public static Image getImage(String imagePath) {
        URL url = ImageUtil.class.getClassLoader().getResource(imagePath);
        BufferedImage img = null;
        try {
            img = ImageIO.read(url);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return img;
    }
}

四、成品展示:

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值