实验二 贪吃蛇游戏的开发

1,项目概述

1.1项目目标和主要内容
目标:整体程序无bug,实现贪吃蛇游戏基本功能;

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

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

使用Java编程语言,在IntelliJ IDEA Community Edition上进行开发。

主要内容:屏幕上随机出现一个“食物”,称为豆子,上下左右控制“蛇”的移动,吃到“豆子”以后“蛇”的身体加长一点,得分增加,“蛇”碰到边界或,蛇头与蛇身相撞,蛇死亡,游戏结束。为游戏设计初始欢迎界面,游戏界面,游戏结束界面。

2.项目设计


3.程序运行结果及分析: 
 

分析;

可以实现贪吃蛇游戏的基本功能; 

具有计分(每吃一个食物加1分)功能,开始游戏计分为零。开始游戏界面,空格控制游戏的暂停/开始游戏功能,WASD控制方向。游戏结束界面。

完整代码

import java.awt.*;
import java.awt.event.*;
import java.util.Objects;
import java.util.Random;
import javax.swing.*;

class Grid {
    int x;
    int y;

    public Grid(int x0, int y0) {
        x = x0;
        y = y0;
    }
}

public class Snakes extends JComponent {
    private final int MAX_SIZE = 400;//蛇身体最长为400节
    private final Grid temp = new Grid(0, 0);
    private final Grid temp2 = new Grid(0, 0);
    private Grid head = new Grid(227, 170);//头部的位置初始化为(227,170)
    private final Grid[] body = new Grid[MAX_SIZE];
    private boolean first_launch = false;
    private int body_length = 5;//身体长度初始化为5
    private boolean isRun = true;
    private int RandomX, RandomY;
    private Thread run;
    private String direction = "R";//默认向右走
    private String current_direction = "R";//当前方向
    private final JLabel Score = new JLabel("0");
    private final JLabel Time = new JLabel("");
    private int hour = 0;
    private int min = 0;
    private int sec = 0;
    private boolean pause = false;


    public Snakes() {
        //布局
        JLabel label = new JLabel("POINT:");
        add(label);
        label.setBounds(5, 15, 80, 20);
        label.setFont(new Font("微软雅黑", Font.PLAIN, 15));
        add(Score);
        Score.setBounds(90, 15, 80, 20);
        Score.setFont(new Font("微软雅黑", Font.PLAIN, 15));
        JLabel label2 = new JLabel("TIME:");
        add(label2);
        label2.setBounds(5, 45, 80, 20);
        label2.setFont(new Font("微软雅黑", Font.PLAIN, 15));
        add(Time);
        Time.setBounds(90, 45, 80, 20);
        Time.setFont(new Font("微软雅黑", Font.PLAIN, 15));

        for (int i = 0; i < MAX_SIZE; i++) {
            body[i] = new Grid(0, 0);
        }

        addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                super.keyPressed(e);

                if (e.getKeyCode() == KeyEvent.VK_D) {
                    if (isRun && !current_direction.equals("L")) {
                        direction = "R";
                    }
                }
                if (e.getKeyCode() == KeyEvent.VK_A) {
                    if (isRun && !current_direction.equals("R")) {
                        direction = "L";
                    }
                }
                if (e.getKeyCode() == KeyEvent.VK_W) {
                    if (isRun && !current_direction.equals("D")) {
                        direction = "U";
                    }
                }
                if (e.getKeyCode() == KeyEvent.VK_S) {
                    if (isRun && !current_direction.equals("U")) {
                        direction = "D";
                    }
                }
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    direction = "R";//默认向右走
                    current_direction = "R";//当前方向
                    first_launch = false;
                    isRun = true;
                    body_length = 5;
                    head = new Grid(227, 100);
                    Score.setText("0");
                    hour = 0;
                    min = 0;
                    sec = 0;
                    for (int i = 0; i < MAX_SIZE; i++) {
                        body[i].x = 0;
                        body[i].y = 0;
                    }


                    System.out.println("Start again");
                }
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    if (!pause)//暂停
                    {
                        pause = true;
                        isRun = false;
                    } else//开始
                    {
                        pause = false;
                        isRun = true;
                    }
                }
            }
        });

        new Timer();

        setFocusable(true);
    }

    public void paintComponent(Graphics g1) {
        super.paintComponent(g1);
        Graphics2D g = (Graphics2D) g1;

        //画头
        g.setColor(Color.BLACK);
        g.fillRoundRect(head.x, head.y, 20, 20, 5, 5);
        if (!first_launch) {
            //初始化身体
            int x = head.x;
            for (int i = 0; i < body_length; i++) {
                x -= 22;//相邻两个方块的间距为2个像素,方块宽度都为20像素
                body[i].x = x;
                body[i].y = head.y;
                g.fillRoundRect(body[i].x, body[i].y, 20, 20, 5, 5);
            }
            //初始化食物位置
            ProduceRandom();
        } else {
            //每次刷新身体
            for (int i = 0; i < body_length; i++) {
                g.fillRoundRect(body[i].x, body[i].y, 20, 20, 5, 5);
            }

            if (EatFood())//被吃了重新产生食物
            {
                ProduceRandom();
            }
        }
        g.fillOval(RandomX, RandomY, 19, 19);
        first_launch = true;
        //墙
        g.setStroke(new BasicStroke(4, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        g.setBackground(Color.WHITE);
        g.drawRect(2, 77, 491, 469);

        //网格线
        for (int i = 1; i < 22; i++) {
            g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
            g.setColor(Color.WHITE);
            g.drawLine(5 + i * 22, 79, 5 + i * 22, 542);//墙宽为4,预留1个像素,以免蛇蹭墙。
            if (i <= 20) {
                g.drawLine(4, 80 + i * 22, 491, 80 + i * 22);
            }
        }
    }

    public void ProduceRandom() {
        boolean flag = true;
        Random rand = new Random();
        RandomX = (rand.nextInt(21) + 1) * 22 + 7;
        RandomY = (rand.nextInt(20) + 1) * 22 + 82;
        while (flag) {
            for (int i = 0; i < body_length; i++) {
                if (body[i].x == RandomX && body[i].y == RandomY) {
                    RandomX = (rand.nextInt(21) + 1) * 22 + 7;
                    RandomY = (rand.nextInt(20) + 1) * 22 + 82;
                    flag = true;
                    break;
                } else {
                    if (i == body_length - 1) {
                        flag = false;
                    }
                }
            }
        }
    }

    public void HitWall() {//判断是否撞墙
        if (Objects.equals(current_direction, "L")) {
            if (head.x < 7) {
                new Snake().start();
                isRun = false;
                int result = JOptionPane.showConfirmDialog(null, "Game over! Try again?", "提醒", JOptionPane.YES_NO_OPTION);
                if (result == JOptionPane.YES_NO_OPTION) {
                    direction = "R";//默认向右走
                    current_direction = "R";//当前方向
                    first_launch = false;
                    isRun = true;
                    body_length = 5;
                    head = new Grid(227, 170);
                    Score.setText("6");
                    hour = 0;
                    min = 0;
                    sec = 0;
                    for (int i = 0; i < MAX_SIZE; i++) {
                        body[i].x = 0;
                        body[i].y = 0;
                    }

                    System.out.println("Start again");
                }
            }
        }
        if (Objects.equals(current_direction, "R")) {
            if (head.x > 489) {
                new Snake().start();
                isRun = false;
                int result = JOptionPane.showConfirmDialog(null, "Game over! Try again?", "提醒", JOptionPane.YES_NO_OPTION);
                if (result == JOptionPane.YES_NO_OPTION) {
                    direction = "R";//默认向右走
                    current_direction = "R";//当前方向
                    first_launch = false;
                    isRun = true;
                    body_length = 5;
                    head = new Grid(227, 170);
                    Score.setText("0");
                    hour = 0;
                    min = 0;
                    sec = 0;
                    for (int i = 0; i < MAX_SIZE; i++) {
                        body[i].x = 0;
                        body[i].y = 0;
                    }


                    System.out.println("Start again");
                }
            }
        }
        if (Objects.equals(current_direction, "U")) {
            if (head.y < 94) {
                new Snake().start();
                isRun = false;
                int result = JOptionPane.showConfirmDialog(null, "Game over! Try again?", "提醒", JOptionPane.YES_NO_OPTION);
                if (result == JOptionPane.YES_NO_OPTION) {
                    direction = "R";//默认向右走
                    current_direction = "R";//当前方向
                    first_launch = false;
                    isRun = true;
                    body_length = 5;
                    head = new Grid(227, 100);
                    Score.setText("0");
                    hour = 0;
                    min = 0;
                    sec = 0;
                    for (int i = 0; i < MAX_SIZE; i++) {
                        body[i].x = 0;
                        body[i].y = 0;
                    }
                    System.out.println("Start again");
                }
            }
        }
        if (Objects.equals(current_direction, "D")) {
            if (head.y > 542) {
                new Snake().start();
                isRun = false;
                int result = JOptionPane.showConfirmDialog(null, "Game over! Try again?", "提醒", JOptionPane.YES_NO_OPTION);
                if (result == JOptionPane.YES_NO_OPTION) {
                    direction = "R";//默认向右走
                    current_direction = "R";//当前方向
                    first_launch = false;
                    isRun = true;
                    body_length = 5;
                    head = new Grid(227, 170);
                    Score.setText("0");
                    hour = 0;
                    min = 0;
                    sec = 0;
                    for (int i = 0; i < MAX_SIZE; i++) {
                        body[i].x = 0;
                        body[i].y = 0;
                    }
                    System.out.println("Start again");
                }
            }
        }
    }

    public void HitSelf() {//判断是否撞到自己身上
        for (int i = 0; i < body_length; i++) {
            if (body[i].x == head.x && body[i].y == head.y) {
                new Snake().start();
                isRun = false;
                int result = JOptionPane.showConfirmDialog(null, "Game over! Try again?", "提醒", JOptionPane.YES_NO_OPTION);
                if (result == JOptionPane.YES_NO_OPTION) {
                    direction = "R";//默认向右走
                    current_direction = "R";//当前方向
                    first_launch = false;
                    isRun = true;
                    body_length = 5;
                    head = new Grid(227, 170);
                    Score.setText("0");
                    hour = 0;
                    min = 0;
                    sec = 0;
                    for (int j = 0; j < MAX_SIZE; j++) {
                        body[j].x = 0;
                        body[j].y = 0;
                    }
                    System.out.println("Start again");
                }
                break;
            }
        }
    }

    public boolean EatFood() {

        return head.x == RandomX && head.y == RandomY;
    }
public int s=0;
    public void Thread() {
        long millis = 300;//每隔300毫秒刷新一次
        run = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(millis);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }

                if (!pause) {
                    temp.x = head.x;
                    temp.y = head.y;
                    //头部移动
                    if (Objects.equals(direction, "L")) {
                        head.x -= 22;
                    }
                    if (Objects.equals(direction, "R")) {
                        head.x += 22;
                    }
                    if (Objects.equals(direction, "U")) {
                        head.y -= 22;
                    }
                    if (Objects.equals(direction, "D")) {
                        head.y += 22;
                    }
                    current_direction = direction;//刷新当前前进方向
                    //身体移动
                    for (int i = 0; i < body_length; i++) {
                        temp2.x = body[i].x;
                        temp2.y = body[i].y;
                        body[i].x = temp.x;
                        body[i].y = temp.y;
                        temp.x = temp2.x;
                        temp.y = temp2.y;
                    }

                    if (EatFood()) {
                        body_length++;
                        s++;
                        body[body_length - 1].x = temp2.x;
                        body[body_length - 1].y = temp2.y;
                        Score.setText("" + (s));
                        new Snake().start();
                    }

                    repaint();

                    HitWall();
                    HitSelf();
                }
            }
        });

        run.start();
    }

    //计时器类
    class Timer extends Thread {
        public Timer() {

            this.start();
        }

        @Override
        public void run() {
            while (true) {
                if (isRun) {
                    sec += 1;
                    if (sec >= 60) {
                        sec = 0;
                        min += 1;
                    }
                    if (min >= 60) {
                        min = 0;
                        hour += 1;
                    }
                    showTime();
                }

                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }

        private void showTime() {
            String strTime;
            if (hour < 10)
                strTime = "0" + hour + ":";
            else
                strTime = "" + hour + ":";

            if (min < 10)
                strTime = strTime + "0" + min + ":";
            else
                strTime = strTime + "" + min + ":";

            if (sec < 10)
                strTime = strTime + "0" + sec;
            else
                strTime = strTime + "" + sec;

            //在窗体上设置显示时间
            Time.setText(strTime);
        }
    }
    public static void main(String[] args) {
        Snakes t = new Snakes();
        t.Thread();
        JFrame game = new JFrame();
        game.setTitle("贪吃蛇.GluttonousSnake");
        game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        game.setSize(510, 590);
        game.setResizable(false);
        game.setLocationRelativeTo(null);
        game.add(t);
        game.setVisible(true);
    }
}

class Snake extends Thread {

    public Snake() {
    }
}


 


 
4.总结
4.1 项目的难点和关键点
显示界面需要JFrame和Jpanel来实现。使用一个全局变量记录当前蛇的长度,方便绘图时画出正确的蛇。在做碰撞检测时,只需要将蛇头节点的坐标和蛇身的坐标、食物的坐标、墙的坐标进行比较即可。当蛇在移动时,需要将储存蛇的数组的每一个元素赋给后一个元素,最后计算首元素,此时最后一个元素的值被丢弃。若吃到食物,则将最后一个元素继续向后赋值,同时设置蛇的长度加一。
4.2 项目的评价:
贪吃蛇的主要功能可以很好的实现,在其基础上加入有趣的图片使得游戏更具有可玩性。
4.3 心得体会
通过该项目,练习了frame框架和GUI编程的用法。
5.参考文献
用Java开发贪食蛇小游戏@阿雪狼

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值