其他分享
首页 > 其他分享> > 1、贪吃蛇游戏设计

1、贪吃蛇游戏设计

作者:互联网

WavesBrightt - 贪吃蛇 - 开发

0、游戏设计核心部分解析(后期补充)

1、游戏素材获取

1.1、图片素材获取

1.2、游戏概念

  1. 如何让游戏动起来?
    1. 首先我们了解任何一款游戏的时候,游戏是动画,蛇会动,无论是我们玩的游戏还是什么,都有一个概念,叫做帧游戏是一帧一帧运行的
    2. 那么我们的 时间片 足够小,例如一秒30帧,一秒60帧,这样的话,他一个静态的图片,他由于连续转动,也可以达到类似动画annotion的效果,能够让咱们的游戏起来
    3. 如何实现的呢,通过定时器实现,这个定时器让页面不断地刷新,从而让游戏动起来,达成动画的效果
  2. 键盘响应
    1. 我们需要根据键盘的监听,来对贪吃蛇进行操控
  3. 如何才做到上述的要求?

1.3、逻辑布局

2、绘制静态窗口

2.1、创建demo

这是一个普通的java项目

image-20220617004656840

2.2、项目结构

image-20220617005018418

2.3、GUI编程设计

	/**
     *  JFrame
     * @return 返回一个GUI窗体对象
     */
    public JFrame getJframe(){
        // 1、加载静态窗口,绘制静态窗口,实例化JFrame对象
        JFrame gameStart = new JFrame();
        // 2、设置窗口大小
        // x,y,width,height
        gameStart.setBounds(520,120,900,720);
        // 3、设置窗口标题
        gameStart.setTitle("WavesBright-SnakeGame");
        // 4、该窗口不可以手动调整大小
        gameStart.setResizable(false);
        // 5、设置窗口的关闭事件
        gameStart.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 6、填充面板对象 => 自定义面板对象
        gameStart.add(new JFrameGamePanel());
        // 7、设置让窗口进行展示
        gameStart.setVisible(true);
        return gameStart;
    }

3、外部数据类设计

4.1、概述

4.2、一个图片文件转换成ImageIcon的步骤

1、概述

image-20220623222616374

2、代码设计

image-20220617112508921

/**
     * 图片类 =URL,可以获取到图片的资源,定位图片地址
     * ps:通过getResource可以获取到我们当前项目下的图片,需要传递的参数为图片的相对路径
     */
    public static URL headerUrl = SnakeData.class.getResource("/statics/header.png");

    /**
     * 将我们URL定位到的图片地址绘制成一张图片
     * ImageIcon 图片
     * new ImageIcon(headerUrl),实例化图片对象,需要的参数为URL对象解析的图片地址
     * 这样我们就可以在面板Panle上绘制我们的图片了
     */
    public static ImageIcon header = new ImageIcon(headerUrl);

4.3、代码改进

1、概述

image-20220624082258389

2、设计

1、静态数组设计

image-20220617112824149

/**
     * 项目结构当中的静态图片数组
     */
    private static String[] imagesAddress = {
            "body.png",
            "down.png",
            "food.png",
            "header.png",
            "left.png",
            "right.png",
            "up.png",
    };

2、URL和ImageIcon的Map集合设计

image-20220617112904723

/**
     * @param staticsUrls 储的哈希表结构URL地址对象集合
     * @param staticsImages 存储的哈希表结构的imageIcon对象集合
     */
    public HashMap<String,URL> staticsUrls;
    public HashMap<String,ImageIcon> staticsImages;

3、URL集合的数据填充

  1. 实例化一个Map集合<String,URL>,获取当前类下的静态资源数组对象
  2. 数组进行迭代,对迭代项进行字符串切割=> split,因为是根据 点. 进行切割的,所以要注意转义字符的使用
  3. 在迭代过程当中实例化URL对象,将我们字符串切割的前缀 + "/statics/"进行拼接得到我们最后的URL实例化对象
    • image-20220617113209642
  4. 前缀设置为Map集合的键名URL设置为value值,这样我们整个数组的URL都获取到了
  5. 通过迭代这个URL集合,我们可以得到最终需要转换的ImageIcon对象集合
/**
     * 通过迭代我们的静态资源数组,将我们的图片存储到HashMap集合当中
     * @return 存储完毕的哈希表集合
     */
    public void setStaticsUrls(){
        // 采用哈希Map集合存储我们的URL对象数据
        HashMap<String,URL> mapUrls = new HashMap<>();
        String[] addresses = this.getImagesAddress();
        // 迭代我们当前内部对象当中的数组,我们需要进行字符串的拼接
        for (String address : addresses) {
            // 将我们内部的对象进行切割,以.为间隔,这里需要进行转译
            String[] fix = address.split("\\.");
            // 获取我们当中的前缀,前缀会用来做我们哈希表的key值(键指)
            String prefix = fix[0];
            /**
             * 图片类 =URL,可以获取到图片的资源,定位图片地址
             * ps:通过getResource可以获取到我们当前项目下的图片,需要传递的参数为图片的相对路径
             */
            URL url  = SnakeData.class.getResource("/statics/"+address);
            // 存储到Hash集合当中
            mapUrls.put(prefix,url);
        }
        this.staticsUrls = mapUrls;
    }

4、ImageIcon集合数据填充

  1. 迭代我们当前类的URL集合
    • image-20220617113522310
  2. 创建Map集合<String,ImageIcon>
    • image-20220617113530414
  3. 获取value值
    • image-20220617113540857
  4. 实例化ImageIcon对象,将value值传递进去
  5. 以URL的key值作为我们ImageIcon集合的键值,将实例化的ImageIcon传递进去
    • image-20220617113555191
/**
     * 迭代静态资源数组,得到我们最终的图片对象
     * @param
     */
    public void setStaticsImages(){
        // 获取到我们组装完毕的URL地址集合
        HashMap<String, URL> staticsUrls = this.staticsUrls;
        // 实例化我们当前的ImageIcons集合
        HashMap<String,ImageIcon> images = new HashMap<>();
        // 对其进行迭代
        for (String key : staticsUrls.keySet()) {
            /**
             * 将我们URL定位到的图片地址绘制成一张图片
             * ImageIcon 图片
             * new ImageIcon(headerUrl),实例化图片对象,需要的参数为URL对象解析的图片地址
             * 这样我们就可以在面板Panle上绘制我们的图片了
             */
            ImageIcon value = new ImageIcon(staticsUrls.get(key));
            // 添加到我们的ImageIcon集合当中
            images.put(key,value);
        }
        // 组装完毕返回即可
        this.staticsImages = images;
    }

5、设置构造器,此对象被new出来的时候加载我们上述设计的两个方法

image-20220617113642393

/**
     * 设置构造方法,实例化我们这个类的时候我们就可以加载到当中的
     * 设置的两个集合
     */
    public SnakeData(){
        this.setStaticsUrls();
        this.setStaticsImages();
    }

4、绘制小蛇

4.1、绘制游戏面板 => Jpanel

4.2、按键监听

4.3.继续绘制面板

1、概述

2、设置面板类的构造器,初始化小蛇各类参数信息

  1. 初始化小蛇的身体长度
  2. 初始化小蛇的头部坐标
  3. 初始化小蛇的第一节身体长度坐标
  4. 初始化小蛇第二节身体长度坐标
  5. 初始化小蛇的头部方向=> 默认为右边 => R

代码设计

/**
     * 初始化游戏数据
     */
    public void initGameData(){
        //蛇的身体长度初始化为3
        this.snakeLength = 3;
        // 原一维数组设计
        /*this.snakeLengthX[0] = 100; this.snakeLengthY[0] = 100;
        // 第一节身体的长度
        this.snakeLengthX[1] = 75; this.snakeLengthY[1] = 100;
        // 第二节身体的长度
        this.snakeLengthX[2] = 50; this.snakeLengthY[2] = 100;*/
        // 头部的长度
        this.coordinate[0][0]= 100;this.coordinate[0][1]= 100;
        // 第一节身体的长度
        this.coordinate[1][0]= 75;this.coordinate[1][1]= 100;
        // 第二节身体的长度
        this.coordinate[2][0]= 50;this.coordinate[2][1]= 100;
        // 初始化蛇的头部方向
        this.direction = "R";
        // 游戏是否开始
        Boolean gameStart = false;
    }

4.4、绘制面板内容

1、概述

2、代码设计

/**
     * 重新绘制面板,重写父类当中的方法 paintComponent
     * @param g 画笔,画图工具的画笔,我们的游戏界面绘制都是通过这只画笔对象来完成的
     */
    @Override
    protected void paintComponent(Graphics g) {
        /**
         * 调用了父类当中的方法,这个方法的目的是清屏
         * 我们游戏开始的时候可以通过这个方法来清屏
         */
        super.paintComponent(g);

        // 实例化我们的外部工具类
        snakeData = new SnakeData();

        // 设置背景颜色
        this.setBackground(Color.BLACK);

        /**
         * 绘制顶部的标题栏,计分板
         *      paintIcon()函数下有四个参数
         *      Component:需要绘制到哪个组件上?那肯定是我们当前的面板上=>this
         *      Graphics:绘制所需要的画笔对象
         *      剩下两个参数为x轴和y轴的距离
         * 这样我们的顶部计分栏就设计完毕了
         */
        snakeData.staticsImages.get("header").paintIcon(this,g,25,11);

        // 设置当前的游戏区域,通过画笔填充
        // 横向25,纵向75,这个75是顶部图片加间隔的距离,不是图片本身的高度
        g.fillRect(25,75,850,600);
        // 判断当前蛇头的方向
        switch(direction){
            case "U":
                // 绘制当前蛇头方向的图片
                snakeData.staticsImages.get("up").paintIcon(this,g,coordinate[0][0],coordinate[0][1]);
                break;
            case "D":
                snakeData.staticsImages.get("down").paintIcon(this,g,coordinate[0][0],coordinate[0][1]);
                break;
            case "L":
                snakeData.staticsImages.get("left").paintIcon(this,g,coordinate[0][0],coordinate[0][1]);
                break;
            case "R":
                snakeData.staticsImages.get("right").paintIcon(this,g,coordinate[0][0],coordinate[0][1]);
                break;
            default:
                break;
        }
        
        for(int i = 1; i< snakeLength; i++){
            /**
             * 方法改进,我们上述写身体的坐标位置实际上是把身体写死了
             * 身体的长度是不断变化的,不断变化那就要涉及到循环的处理
             *循环从1开始,那么循环一开始就是我们第一节。。。。第n节的位置
             */
            // 蛇的身体长度通过 => snakeLength 来控制
            snakeData.staticsImages.get("body").paintIcon(this,g,coordinate[i][0],coordinate[i][1]);
        }

        // 判断当前游戏是否开始
        if(!this.gameStart){
            // 绘制画笔颜色
            g.setColor(Color.WHITE);
            // 设置画笔字体,微软雅黑,粗体,40号大小
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("点击空格开始进行贪吃蛇游戏!",200,300);
        }
    }

5、键盘监听

5.1、设置键盘监听事件

1、概述

2、代码设计

image-20220617133358411

 /**
     * 按下按键时发生。
     * @param e
     */
    @Override
    public void keyPressed(KeyEvent e) {
        // 按住
        // 通过事件源对象,获取输入的按键信息
        int keyCode = e.getKeyCode();
        // 判断当前的keyCode是否是空格键
        if(keyCode == KeyEvent.VK_SPACE){
            // 将gameStart取反
            this.gameStart = !this.gameStart;
            // 刷新当前面板 => 重新绘制当前面板
            repaint();
        }
    }

5.2、完善构造器当中的方法

1、概述

  1. 获取键盘的监听事件,目的是为了让键盘输入焦点能够聚集在游戏上面
  2. 设置监听的对象是谁,KeyListener这个接口的实现类,也就是我们自定义的面板类对象,所以是this

2、代码修改

image-20220617133520646

/**
     * 画板构造器,初始化小蛇数据
     */
    public JFrameGamePanel(){
        // 初始化小蛇身体数据
        initGameData();
        // 获取键盘的监听事件,目的是为了让键盘输入焦点能够聚集在游戏上面
        this.setFocusable(true);
        // 设置监听的对象是谁,KeyListener这个接口的实现类
        this.addKeyListener(this);
    }

6、定时器Timer

6.1、概述

6.2、参数设计

0.1s刷新一次,监听对象为我们设计的这个面板对象

image-20220617134123001

/**
     *定时器
     * @param delay 多久刷新一次,毫秒级别
     * @param listener 监听的对象是谁?就是监听我们这个面板对象,实现了这个接口的面板对象
     */
    Timer timer = new Timer(100,this);

6.3、设置监听内容

1、概述

2、小蛇移动图解

image-20220617135214837

3、代码设计

/ 定时器,监听事件流动,让小蛇运动起来
    @Override
    public void actionPerformed(ActionEvent e) {

        // 游戏开始了
        if(gameStart){
            // 将面前的身体坐标赋值给后面的身体
            for(int i = snakeLength - 1; i > 0; i--){
                // 横坐标赋值
                coordinate[i][0] = coordinate[i-1][0];
                // 纵坐标赋值
                coordinate[i][1] = coordinate[i-1][1];
            }
            // 小蛇脑壳向前移动一位,25个坐标值
            coordinate[0][0] = coordinate[0][0] + 25;
            // 边界判断,避免飞出屏幕
            if(coordinate[0][0] >= 875){
                // 从头开始
                coordinate[0][0] = 25;
            }
            // 上述操作完毕之后,刷新我们的页面,不刷新没办法运行哈
            repaint();
        }
        this.timer.start();
    }

6.4、构造器改进,定时器启动=>start()

该方法为定时器的启动方法,不启动的话定时器是无法对面板进行刷新的

构造器改进

image-20220617191035235

 /**
     * 画板构造器,初始化小蛇数据
     */
    public JFrameGamePanel(){
        // 初始化小蛇身体数据
        initGameData();
        // 获取键盘的监听事件,目的是为了让键盘输入焦点能够聚集在游戏上面
        this.setFocusable(true);
        // 设置监听的对象是谁,KeyListener这个接口的实现类
        this.addKeyListener(this);
        // 定时器开始
        this.timer.start();
    }

7、控制小蛇的方向

7.1、概述

7.2、设计事件

1、空格

image-20220617224931720

2、上

image-20220617225012432

3、下

image-20220617224959150

4、左

image-20220617224944383

5、右

image-20220617224951933

7.3、小蛇身体的移动=>定时器代码修改

1、概述

2、代码图解

我们这里依旧采用switch进行判断,到达边界值的时候,进行了一点游戏改化,不把小蛇弄死,从另一边冒出来

2.1、小蛇朝右边移动

image-20220617225255059

2.2、小蛇朝左边移动

image-20220617225521091

2.3、小蛇朝上移动

image-20220617225526622

2.4、小蛇朝下移动

image-20220617225531120

3、代码设置

image-20220624091345687

// 蛇头行动方向判定
    public void snakeDirection(String direction){
        switch(direction){
            case "R":
                // 小蛇脑壳向右移动一位,25个坐标值
                coordinate[0][0] = coordinate[0][0] + 25;
                // 边界判断,避免飞出屏幕
                if(coordinate[0][0] >= 875){
                    // 从头开始
                    coordinate[0][0] = 25;
                }
                break;
            case "L":
                // 小蛇脑壳向左移动一位,25个坐标值
                coordinate[0][0] = coordinate[0][0] - 25;
                // 边界判断,避免飞出屏幕
                if(coordinate[0][0] <= 0){
                    // 从右侧初始位置开始
                    coordinate[0][0] = 850;
                }
                break;
            case "U":
                // 小蛇脑壳向上移动一位,25个坐标值
                coordinate[0][1] = coordinate[0][1] - 25;
                // 边界判断,避免飞出屏幕
                if(coordinate[0][1] <= 50){
                    // 从顶部初始位置开始
                    coordinate[0][1] = 675;
                }
                break;
            case "D":
                // 小蛇脑壳向前下移动一位,25个坐标值
                coordinate[0][1] = coordinate[0][1] + 25;
                // 边界判断,避免飞出屏幕
                if(coordinate[0][1] >= 675){
                    // 从顶部初始位置开始
                    coordinate[0][1] = 75;
                }
                break;
            default:
                break;
        }
    }

8、吃食物设计

8.1、概述

  1. 按照之前的开发模式,现在我们想添加一个食物就很简单了
  2. 设计当前这个食物的坐标,采用随机数种子进行设计 =>Random
  3. 构造方法当中初始化我们的食物坐标
  4. 面板绘制当中,绘制我们的食物
  5. 定时器的方法当中追加对食物的判断

8.2、新增变量,初始化数据方法的修改

1、新增变量

foodCoordinateX和foodCoordinateY

image-20220623010246078

2、init方法的修改

image-20220623010306188

3、代码修改

/**
     * 初始化游戏数据
     */
    public void initGameData(){
        //蛇的身体长度初始化为3
        this.snakeLength = 3;
        /*this.snakeLengthX[0] = 100; this.snakeLengthY[0] = 100;
        // 第一节身体的长度
        this.snakeLengthX[1] = 75; this.snakeLengthY[1] = 100;
        // 第二节身体的长度
        this.snakeLengthX[2] = 50; this.snakeLengthY[2] = 100;*/
        // 头部的长度
        this.coordinate[0][0]= 100;this.coordinate[0][1]= 100;
        // 第一节身体的长度
        this.coordinate[1][0]= 75;this.coordinate[1][1]= 100;
        // 第二节身体的长度
        this.coordinate[2][0]= 50;this.coordinate[2][1]= 100;
        // 初始化蛇的头部方向
        this.direction = "R";
        // 游戏是否开始
        this.gameStart = false;
        // 食物当前数组坐标
        this.foodCoordinateX = 25 + 25 * random.nextInt(34);
        // 纵坐标
        this.foodCoordinateY = 75 + 25 * random.nextInt(24);
    }

8.3、面板绘制新增内容

1、概述

加一行代码就可以了,生成图片对于现在的我们来说太简单了

2、代码修改

image-20220623011310439

 /**
     * 重新绘制面板,重写父类当中的方法 paintComponent
     * @param g 画笔,画图工具的画笔,我们的游戏界面绘制都是通过这只画笔对象来完成的
     */
    @Override
    protected void paintComponent(Graphics g) {
        /**
         * 调用了父类当中的方法,这个方法的目的是清屏
         * 我们游戏开始的时候可以通过这个方法来清屏
         */
        super.paintComponent(g);

        // 实例化我们的外部工具类
        snakeData = new SnakeData();

        // 设置背景颜色
        this.setBackground(Color.BLACK);

        /**
         * 绘制顶部的广告栏,计分板
         *      paintIcon()函数下有四个参数
         *      Component:需要绘制到哪个组件上?那肯定是我们当前的面板上=>this
         *      Graphics:绘制所需要的画笔对象
         *      剩下两个参数为x轴和y轴的距离
         * 这样我们的顶部计分栏就设计完毕了
         */
        snakeData.staticsImages.get("header").paintIcon(this,g,25,11);
        //
        // 设置当前的游戏区域,通过画笔填充
        // 横向25,纵向75,这个75是顶部图片加间隔的距离,不是图片本身的高度
        g.fillRect(25,75,850,600);
        // 判断当前蛇头的方向
        switch(direction){
            case "U":
                // 蛇的身体长度通过 => snakeLength 来控制
                snakeData.staticsImages.get("up").paintIcon(this,g,coordinate[0][0],coordinate[0][1]);
                break;
            case "D":
                // 蛇的身体长度通过 => snakeLength 来控制
                snakeData.staticsImages.get("down").paintIcon(this,g,coordinate[0][0],coordinate[0][1]);
                break;
            case "L":
                // 蛇的身体长度通过 => snakeLength 来控制
                snakeData.staticsImages.get("left").paintIcon(this,g,coordinate[0][0],coordinate[0][1]);
                break;
            case "R":
                // 蛇的身体长度通过 => snakeLength 来控制
                snakeData.staticsImages.get("right").paintIcon(this,g,coordinate[0][0],coordinate[0][1]);
                break;
            default:
                break;
        }
        // 生成当前蛇的身体部分
        for(int i = 1; i< snakeLength; i++){
            /**
             * 方法改进,我们上述写身体的坐标位置实际上是把身体写死了
             * 身体的长度是不断变化的,不断变化那就要涉及到循环的处理
             *循环从1开始,那么循环一开始就是我们第一节。。。。第n节的位置
             */
            // 蛇的身体长度通过 => snakeLength 来控制
            snakeData.staticsImages.get("body").paintIcon(this,g,coordinate[i][0],coordinate[i][1]);
        }

        // 生成当前食物
        snakeData.staticsImages.get("food").paintIcon(this,g,foodCoordinateX,foodCoordinateY);

        // 判断当前游戏是否开始
        if(!this.gameStart){
            // 绘制画笔颜色
            g.setColor(Color.WHITE);
            // 设置画笔字体,微软雅黑,粗体,40号大小
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("点击空格开始进行贪吃蛇游戏!",200,300);
        }
        // 当前小蛇是否死亡
        if(snakeStatus){
            // 绘制画笔颜色
            g.setColor(Color.RED);
            // 设置画笔字体,微软雅黑,粗体,40号大小
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("小蛇已经死亡,请重新开始游戏!",200,300);
        }

    }

8.4、定时器判定

1、概述

2、代码设计

image-20220623112321206

 // 定时器,监听事件流动,让小蛇运动起来
    @Override
    public void actionPerformed(ActionEvent e) {
        // 游戏开始了,并且当前小蛇没有死亡
        if(gameStart && !snakeStatus){
            // 将面前的身体坐标赋值给后面的身体
            for(int i = snakeLength - 1; i > 0; i--){
                // 横坐标赋值
                coordinate[i][0] = coordinate[i-1][0];
                // 纵坐标赋值
                coordinate[i][1] = coordinate[i-1][1];
            }
            // 根据当前操控方向位置,对蛇头位置的移动方向进行修改
            this.snakeDirection(this.direction);
            
            // 生成随机食物,如果当前蛇头的坐标和食物坐标重合
            if(coordinate[0][0] == foodCoordinateX && coordinate[0][1] == foodCoordinateY){
                // 身体长度 + 1
                snakeLength++;
                // 重新设置食物坐标
                foodCoordinateX = 25 + 25 * random.nextInt(34);
                foodCoordinateY = 75 + 25 * random.nextInt(24);
            }

            // 判断当前身体是否和脑壳重叠
            for(int i =1 ; i<snakeLength; i++){
                if(coordinate[0][0] == coordinate[i][0] && coordinate[0][1] == coordinate[i][1] ){
                    // 游戏失败
                    this.snakeStatus = true;
                }
            }

            // 上述操作完毕之后,刷新我们的页面,不刷新没办法运行哈
            repaint();
        }
        timer.start();
    }

9、小蛇死亡判定

9.1、概述

9.2、新增变量,初始化小蛇状态

image-20220623124817738

image-20220623124839845

9.3、画板当中绘制游戏失败的图标

image-20220623125017009

9.4、定时器当中新增死亡判断

image-20220623130055801

image-20220623125831597

9.5、点击空格重新开始游戏

image-20220623130227912

10、得分功能添加

10.1、概述

image-20220623171159664

  1. 新增得分功能,该功能可以计算当前蛇的身体长度和得分分数,默认吃一个食物得10分
  2. 新增全局变量score,该变量表示分数变量
  3. 数据初始化方法=> initGameData当中新增初始化分数
  4. 在画板当中绘制我们的得分项和长度判定
  5. 定时器函数当中追加得分分数加成

10.2、新增变量和初始化数据函数追加

image-20220623171509017

image-20220623171520604

10.3、面板绘制当中绘制当前得分项和身体长度

image-20220623171556654

10.4、定时器当中追加对分数的判定

1、概述

分数如何判定?肯定是吃到食物以后对我们的分数进行累加即可

2、代码追加

image-20220623171658978

11、bug产生

11.1、概述

image-20220623113027739

11.2、如何解决呢,有空解决

12、神秘力量

12.1、概述

image-20220623130844015

  1. 小蛇要吃到这个食物,需要通过输入键盘的事件来进行判定,但是输入键盘的目的是为了什么?修改当前蛇头的方向,他只是修改这个蛇头方向的字符串而已。
  2. 吃到食物以后,才会生成新的食物坐标
  3. 那么以上图为例,我如果要吃到这个食物,有很多种方式,我这里就选择,控制一个方向来吃掉这个食物
  4. 怎么自动输入?
  5. 先不考虑运行的算量关系,我相信我添加这么一个小功能,应该还是能做到的
  6. 我现在需要考虑的是,这个脚本,应该在什么时候运行?脚本运行的时机修改方向的时机是什么时候?
  7. 食物是一开始初始化变量的时候,生成的坐标,往后,吃到食物,生成坐标是在定时器的方法当中生成的
  8. 可以通过单方向修改吃到食物,但是会出现蛇的身体过长导致脑壳撞到身体

12.2、上手

This is 神秘力量 の 雏形

image-20220623172253614

看不懂是不是,没关系,一步一步来

12.3、脚本设计 => plugIn

1、概述

2、图解

2.1、蛇脑壳方向是上或者下情况的时候

image-20220623173215172

image-20220623174557345

2.2、蛇脑壳方向是左、右的时候

image-20220623174758550

image-20220623175115770

3、根据图解设计代码

脚本运行方向算法设计

/**
     * this is 外挂
     */
    public void plugIn(){
        // 让定时器来调用这个脚本
        // 我们肯定要计算食物坐标和小蛇脑壳坐标的差值,但是没有必要每次都计算
        // 定时器调用这个脚本的时候,计算方法运行一次,运行一次以后计算方法关闭
        // 当吃到食物的时候再让计算方法的变量重置即可
        // 当前第一次运算
        if(!plugin){
            // 获取当前蛇头的方向
            String nowDirection = this.direction;
            // 当前蛇头方向为上下
            if("U".equals(nowDirection)||"D".equals(nowDirection)){
                // 判断横坐标差值是否为正值or负值
                // 为正值,说明食物在右边
                if(foodCoordinateX - coordinate[0][0]>0){
                    // 下一次移动方向为右边
                    this.nextDirection ="R";
                }else{
                    // 下一次移动方向为左边
                    this.nextDirection ="L";
                }
            }else{
                if(foodCoordinateY - coordinate[0][1]>0){
                    // 下一次移动方向为下
                    this.nextDirection ="D";
                }else{
                    // 下一次移动方向为上
                    this.nextDirection ="U";
                }
            }
            // System.out.println("下一次移动方向为:" + this.nextDirection);
        }
    }

4、下一次的方向计算出来了,那么什么时候改变方向?

4.1、根据下一次运行方向判断

蛇头方向为上下,计算出下一次运动方向为左右

当蛇头的纵坐标值和食物的纵坐标值相等的时候,改变我们蛇头的方向

将 下一次运动方向的值 赋值给蛇头方向

image-20220623180119392

蛇头方向为左右的时候,那么下一次运行方向为上或者下

当蛇头的横坐标和食物重合的时候,就可以修改我们当前蛇头的方向了

image-20220623180523945

5、代码设计,蛇头什么时候修改方向

image-20220623180634037

// 判断下一次要移动的方向
        if("U".equals(nextDirection) || "D".equals(nextDirection)){
            // 纵坐标相等时,改变方向
            if(coordinate[0][0] == foodCoordinateX){
                this.direction = nextDirection;
            }
        }else{
            // 横坐标相等时,改变方向
            if(coordinate[0][1] == foodCoordinateY){
                this.direction = nextDirection;
            }
        }

6、脚本代码完整

/**
     * this is 外挂
     */
    public void plugIn(){
        // 让定时器来调用这个脚本
        // 我们肯定要计算食物坐标和小蛇脑壳坐标的差值,但是没有必要每次都计算
        // 定时器调用这个脚本的时候,计算方法运行一次,运行一次以后计算方法关闭
        // 当吃到食物的时候再让计算方法的变量重置即可
        // 当前第一次运算
        if(!plugin){
            // 获取当前蛇头的方向
            String nowDirection = this.direction;
            // 当前蛇头方向为上下
            if("U".equals(nowDirection)||"D".equals(nowDirection)){
                // 判断横坐标差值是否为正值or负值
                // 为正值,说明食物在右边
                if(foodCoordinateX - coordinate[0][0]>0){
                    // 下一次移动方向为右边
                    this.nextDirection ="R";
                }else{
                    // 下一次移动方向为左边
                    this.nextDirection ="L";
                }
            }else{
                if(foodCoordinateY - coordinate[0][1]>0){
                    // 下一次移动方向为下
                    this.nextDirection ="D";
                }else{
                    // 下一次移动方向为上
                    this.nextDirection ="U";
                }
            }
            // System.out.println("下一次移动方向为:" + this.nextDirection);
        }

        // 判断下一次要移动的方向
        if("U".equals(nextDirection) || "D".equals(nextDirection)){
            // 纵坐标相等时,改变方向
            if(coordinate[0][0] == foodCoordinateX){
                this.direction = nextDirection;
            }
        }else{
            // 横坐标相等时,改变方向
            if(coordinate[0][1] == foodCoordinateY){
                this.direction = nextDirection;
            }
        }

    }

标签:游戏,小蛇,贪吃蛇,coordinate,设计,面板,绘制,我们,图片
来源: https://www.cnblogs.com/wavesbright/p/data.html