实验二 贪吃蛇游戏开发

项目目标和主要内容

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

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

项目的主要功能

程序流程图及描述

 

Snake类

用来构架窗口并在上面添加画布,后续蛇的移动和食物的生成都在画布上显示。

package Snake;
import javax.swing.JFrame;
public class Snake {
    public static void main(String[] args) {
//框架三步骤
        JFrame frame = new JFrame("贪吃蛇");
        frame.setBounds(10,10,850,750);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//先建的框架 然后在框架上铺上画布 就是SnakePanel.java类
//后续的所有工作都在这个画布上画
        SnakePanel panel = new SnakePanel();
        frame.add(panel);
        frame.setVisible(true);
    }
}

SnakePanel类

含有构造函数给类初始化

paint函数,在画布上画需要的事物

setup函数,用来重置和初始化蛇

KeyListener函数,用来实施键盘监听

ActionListener函数,用来事件响应

事件开始后开始paint事物。当键盘响应时,代表蛇发生了移动,这个移动只是表面上的移动,真正的蛇移动是通过repaint后才显示在画布上。蛇吃到食物的判断:蛇头xy坐标等于食物生成的xy坐标。蛇吃到食物后食物会repaint。蛇是否死亡的判断是:蛇头的xy坐标是否超出画布或者蛇头xy坐标是否等于自己身体xy坐标。

package Snake;

import java.awt.Color; // eclipse 用 shift+ctrl+O 自动添加包、类
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

import javax.swing.*;

public class SnakePanel extends JPanel implements KeyListener, ActionListener {
    //图片导入方法: 直接拖住 然后添加到工程的文件夹中 这个方便 但是不规范
// 可以在工程文件下建立一个专门放图片的文件夹 我的是image 这样看起来清晰 但是路径要写清楚 文件夹/图片名
    ImageIcon up = new ImageIcon("F:\\src\\Snake\\image up.png");
    ImageIcon down = new ImageIcon("F:\\src\\Snake\\image down.png");
    ImageIcon right = new ImageIcon("F:\\src\\Snake\\image right.png");
    ImageIcon left = new ImageIcon("F:\\src\\Snake\\image left.png");
    ImageIcon snake = new ImageIcon("Snake/image snake.png");
    ImageIcon food = new ImageIcon("F:\\src\\Snake\\image food.png");
    ImageIcon body = new ImageIcon("F:\\src\\Snake\\image body.png");

    //所有要用的变量都定义在一起 最好放在方法前面 方便查找
    int[] snakex = new int[850]; //放蛇的数组
    int[] snakey = new int[750];
    Random rand = new Random();  // 随机产生食物
    int foodx = rand.nextInt(32) * 25 + 25;  //放事物的数组
    int foody = rand.nextInt(28) * 25 + 25;
    int len = 3;    // 蛇身体的长度
    int score = 0;  //游戏积分
    String direction = "R";   //蛇头的方向

    boolean isStarted = false;  //判断游戏是否开始  关系到后面的空格键事件以及 Timer是否开始
    boolean isFailed = false;  //判断游戏是否失败 即蛇头是否撞到身体

    //蛇移动的原理:就是不断的重画 即下面的paint()函数 重画的频率由timer控制 单位是毫秒
    Timer timer = new Timer(150, this); //控制蛇的移动的速度


    public SnakePanel() {  //构造函数 即给类初始化  即运行工程后的初始状态
        this.setFocusable(true);  //选中焦点 即对画布 上的按钮起作用
        this.addKeyListener(this);  //键盘监听 即你按键盘的时候我们的程序有反映
        setup();     //重置 初始化
        timer.start();   //Timer类的启动 相当于定闹钟 然后工程开始repaint 蛇才能运动

    }

    public void paint(Graphics g) {  //此函数相当于一支画笔 在panel画布上画各种东西(蛇 事物 文字等)
        paintComponent(g);
        this.setBackground(Color.black);
        snake.paintIcon(this, g, 25, 11);  //上面导入的图片就是用paintIcon函数来画出来 这是在画标题图片
        g.fillRect(0, 0, 850, 750);  //填写指定的矩形

        //画蛇头
        if (direction.equals("R")) { //键盘监听后对蛇头方向的判断后 这这里画出
            right.paintIcon(this, g, snakex[0], snakey[0]);
        } else if (direction.equals("L")) {
            left.paintIcon(this, g, snakex[0], snakey[0]);
        } else if (direction.equals("U")) {
            up.paintIcon(this, g, snakex[0], snakey[0]);
        } else if (direction.equals("D")) {
            down.paintIcon(this, g, snakex[0], snakey[0]);
        }

        //画蛇身体
        for (int i = 1; i < len; i++) { //身体的画发就是把body.png图片画在数组中
            body.paintIcon(this, g, snakex[i], snakey[i]);
        }

        if (!isStarted) {  //数据结构中判读出没开始  就在这里画出下面的东西 即出现一行提示字
            g.setColor(Color.red);//每次画东西想要不懂的颜色 都要重新 占墨水 即重新给画笔设置颜色
            g.setFont(new Font("宋体", Font.BOLD, 30)); //同样 设置画笔的字体
            g.drawString("按空格键开始游戏!", 300, 300);//然后画出文字 后面的300 300 是指文字出现的位置
        }

        if (isFailed) {  //数据结构判断失败了 就在这里画出下面的东西 每次画东西都是基于数据的判断 即这是画表面
            g.setColor(Color.red);
            g.setFont(new Font("宋体", Font.BOLD, 30));
            g.drawString("游戏失败!", 300, 300);
        }

        food.paintIcon(this, g, foodx, foody);  //画食物

        g.setColor(Color.white);   //重置画笔 画分数表
        g.setFont(new Font("微软雅黑", Font.BOLD, 15));
        g.drawString("Score:" + score, 750, 25);
        g.drawString("Length:" + len, 750, 50);

    }

    public void setup() { //重置初始化函数
        len = 3;
        score = 0;
        isStarted = false;
        isFailed = false;
        direction = "R";   //蛇头默认方向为右
        snakex[0] = 200;  //蛇刚出发的位置
        snakey[0] = 300;
        snakex[1] = 175;
        snakey[1] = 300;
        snakex[2] = 150;
        snakey[2] = 300;
    }


    //这三个函数是在实现KeyListener类时生成的函数 作用是监听键盘 我们是需要用第二 即键盘按下时的反映
    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void keyPressed(KeyEvent e) { //键盘按下时 工程需要的操作
        int keyCode = e.getKeyCode();  //获取按键的keycode值 每个按键都有不同的值
        if (keyCode == KeyEvent.VK_SPACE) {   //判断按键是否为空格键 VK_SPACE就是空格键默认的值
            if (isFailed) {
                setup();   //如果是空格键且游戏结束了 就调用这个函数重置初始化
            } else {
                isStarted = !isStarted; //如果是空格键且游戏没结束了 判断变量变成相反量
            }


        } else if (keyCode == KeyEvent.VK_UP && direction != "D") { //如果不是空格键而是方向建 则蛇头变成相应的方向
            direction = "U";
        } else if (keyCode == KeyEvent.VK_DOWN && direction != "U") {
            direction = "D";
        } else if (keyCode == KeyEvent.VK_RIGHT && direction != "L") {
            direction = "R";
        } else if (keyCode == KeyEvent.VK_LEFT && direction != "R") {
            direction = "L";
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void actionPerformed(ActionEvent e) { //这个函数是在实现ActionListener类时生成的功能 当事件发生时,当事件发生时调用
        //再定1个闹钟   事件开始是以闹钟响起开始 即 开始paint
        timer.start();

        //先移动数据 只有数据发生变化了 画笔才能画出相应的画
        if (isStarted && !isFailed) {
            for (int i = len; i > 0; i--) { //蛇身体的移动 数据的变化 真正的移动在下面的repaint中实现
                snakex[i] = snakex[i - 1];
                snakey[i] = snakey[i - 1];
            }

            //蛇头的移动 数据的变化 真正的变化在下面的repaint 中的实现
            if (direction.equals("R")) {
                snakex[0] = snakex[0] + 25;
                //四个if语句 判断蛇头是否超出画布
                if (snakex[0] >= 850) {
                    isFailed = true;
                }
            } else if (direction.equals("L")) {
                snakex[0] = snakex[0] - 25;
                if (snakex[0] <= 0) {
                    isFailed = true;
                }
            } else if (direction.equals("U")) {
                snakey[0] = snakey[0] - 25;
                if (snakey[0] <= 0) {
                    isFailed = true;
                }
            } else if (direction.equals("D")) {
                snakey[0] = snakey[0] + 25;
                if (snakey[0] >= 750) {
                    isFailed = true;
                }
            }

            if (snakex[0] == foodx && snakey[0] == foody) {  //判断蛇是否吃到食物
                len++;
                score++;
                foodx = rand.nextInt(32) * 25 + 25;//食物出现的 x y坐标 在画布内
                foody = rand.nextInt(28) * 25 + 25;
            }

            for (int i = 1; i < len; i++) {  //判断蛇头是否撞到蛇身体
                if (snakex[0] == snakex[i] && snakey[0] == snakey[i]) {
                    isFailed = true; //如果撞到 判断游戏失败的变量变为真 即游戏失败
                }
            }
        }
        //重画
        repaint();
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值