用java写一个贪吃蛇小游戏(源码在最后)

一、引入

涉及技能:

  1. 循环、分支
  2. 方法的抽取
  3. 数组的使用
  4. 面向对象
  5. 继承,子类方法的重写
  6. 接口,接口的实现
  7. GUI(图像化界面编程)

GUI中的组件:
7.1 窗口
7.2 弹窗
7.3 面板
7.4 文本框
7.5 列表框
7.6 按钮
7.7 图片
7.8 交互的事件:监听事件(鼠标事件,键盘事件)

GUI技术,不流行了!!!界面太简单粗糙!!

已经被淘汰了,为什么还学?

  1. 提起兴趣
  2. 分层思想
  3. 锻炼监听器的思想
  4. 举一反三(蛇的图片换成其他图片,食物的图片换成其他图片)
  5. 工作中用到

注意:
学的是 思想!!!

二、原理图

小蛇素材图像素为25*25,因此在对应xy轴上的数值也以25为步长。
在这里插入图片描述

三、进入正题

1.打开IDEA工具,新建一个模块module。
在这里插入图片描述
2.选择java,点next。
在这里插入图片描述
3.取一个名字叫TestSnake,然后点击finish。
在这里插入图片描述
4.打开新建好的TestSnake,在src下新建一个文件夹,取名为images。
在这里插入图片描述
在这里插入图片描述
5.将所有的素材图片复制到images文件夹下。
在这里插入图片描述
在这里插入图片描述

四、将图片加载到程序中

1.在src下再新建一个文件夹com.game,在这个文件夹下新建一个类Images,用于获取图片。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.打开Images类,将以下代码写入,封装图片,后续使用时只要类名+图片即可。

import javax.swing.*;
import java.net.URL;

/*用来获取游戏中所有涉及的图片*/
public class Images {
    
    
/*面向对象思想:将图片进行封装,封装为一个对象,在程序中通过操纵这个对象来操纵图片*/

    /*将图片的路径封装为一个对象:*/
//    class获取字节码
//    getResource获取图片路径
//    在菜单Build下选中第二个Build Module,重写构建一下,生成对应的字节码信息
//    在out文件夹下找图片路径
//    '/'指代相对路径,相对file:/C:/Users/ASUS/Desktop/贪吃蛇小游戏/out/production/TestSnake/而言
    public static URL bodyURL=Images.class.getResource("/images/body.png");

    /*将图片封装为程序中一个对象:*/
//    ImageIcon构造器,可以直接把url传进去
    public static ImageIcon bodyImg=new ImageIcon(bodyURL);

    /*同理,将剩下的图片都封装:*/
    public static URL downURL=Images.class.getResource("/images/down.png");
    public static ImageIcon downImg=new ImageIcon(downURL);

    public static URL foodURL=Images.class.getResource("/images/food.png");
    public static ImageIcon foodImg=new ImageIcon(foodURL);

    public static URL headerURL=Images.class.getResource("/images/header.png");
    public static ImageIcon headerImg=new ImageIcon(headerURL);

    public static URL leftURL=Images.class.getResource("/images/left.png");
    public static ImageIcon leftImg=new ImageIcon(leftURL);

    public static URL rightURL=Images.class.getResource("/images/right.png");
    public static ImageIcon rightImg=new ImageIcon(rightURL);

    public static URL upURL=Images.class.getResource("/images/up.png");
    public static ImageIcon upImg=new ImageIcon(upURL);



}

3.关于代码注释中的“在菜单Build下选中第二个Build Module,重写构建一下,生成对应的字节码信息”、“ 在out文件夹下找图片路径”的操作,具体步骤如下:
在这里插入图片描述

五、创建窗体

1.在com.game文件夹下新建一个类,叫StartGame。
在这里插入图片描述
在这里插入图片描述
2.将以下代码写入StartGame类中。

package com.game;

import javax.swing.*;
import java.awt.*;

public class StartGame {
    
    
    public static void main(String[] args) {
    
    
        /*1.创建一个窗体:*/
        JFrame jf=new JFrame();
        /*2.给窗体设置一个标题:*/
        jf.setTitle("贪吃蛇小游戏 by:cym");
        /*3.设置窗体弹出的坐标,对应窗体的宽和高:*/
//        获取屏幕的宽、高
        int width = Toolkit.getDefaultToolkit().getScreenSize().width;
        int height = Toolkit.getDefaultToolkit().getScreenSize().height;
        jf.setBounds((width-800)/2,(height-800)/2,800,800);
        /*4.设置窗体大小不可调:*/
        jf.setResizable(false);
        /*5.关闭窗口的同时,程序随之关闭:*/
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        /*默认情况下窗体隐藏,需要进行显现(最好放在最后):*/
        jf.setVisible(true);

    }
}

3.计算窗体在屏幕居中的方法如下:
在这里插入图片描述

六、创建面板

1.新建一个类GamePanel。
在这里插入图片描述
在这里插入图片描述
2.将以下代码写入GamePanel类中。

package com.game;

import javax.swing.*;
import java.awt.*;

/*面板*/
//继承JPanel才具备面板的功能,才成为一个面板
public class GamePanel extends JPanel {
    
    
    /*此方法特殊,就相当于图形版的main方法,自动调用*/
    @Override
    protected void paintComponent(Graphics g) {
    
    
        super.paintComponent(g);
        /*1.填充背景颜色:*/
//        颜色由rgb(red green blue)组成
//        可通过点击左边行号旁的色块进行颜色修改
        this.setBackground(new Color(243, 239, 190, 255));

        /*2.画头部的图片header.png:*/
//        Images.headerImg:调用图片类中的header.png图片
//        paintIcon()方法将图片在面板中绘制出来
//        this:当前面板
//        g:使用的画笔
//        x、y:对应坐标轴
        Images.headerImg.paintIcon(this, g, 15, 10);

        /*3.画一个矩形:*/
//        先调节画笔颜色:
        g.setColor(new Color(195, 205, 246));
//        fillRect填充矩形
        g.fillRect(15, 70, 750, 670);

    }
}

3.在StartGame类中添加第6 步创建面板,将面板添加到窗体中。

package com.game;

import javax.swing.*;
import java.awt.*;
/*窗体*/
public class StartGame {
    
    
    public static void main(String[] args) {
    
    
        /*1.创建一个窗体:*/
        JFrame jf = new JFrame();
        /*2.给窗体设置一个标题:*/
        jf.setTitle("贪吃蛇小游戏 by:cym");
        /*3.设置窗体弹出的坐标,对应窗体的宽和高:*/
//        获取屏幕的宽、高
        int width = Toolkit.getDefaultToolkit().getScreenSize().width;
        int height = Toolkit.getDefaultToolkit().getScreenSize().height;
        jf.setBounds((width - 800) / 2, (height - 800) / 2, 800, 800);
        /*4.设置窗体大小不可调:*/
        jf.setResizable(false);
        /*5.关闭窗口的同时,程序随之关闭:*/
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        /*6.创建面板:*/
        GamePanel gp=new GamePanel();
//        将面板放入窗体中:
        jf.add(gp);

        /*默认情况下窗体隐藏,需要进行显现(最好放在最后):*/
        jf.setVisible(true);


    }
}

七、画静态小蛇

1.以这个图为例,做为一个默认小蛇,一个头,两节身子。
在这里插入图片描述
2.在面板类中的paintComponent()方法外部,写入以下代码。

/*定义两个数组:
     * 一个数组,专门存储蛇的x轴坐标
     * 一个数组,专门存储蛇的y轴坐标
     * */
    int[] snakeX=new int[200];
    int[] snakeY=new int[200];
    /*定义蛇的长度:*/
    int length;

    /*自定义一个初始化方法,初始化小蛇的坐标:*/
    public void init(){
    
    
        /*初始化蛇的长度:*/
        length=3;
        /*初始化蛇头坐标:*/
        snakeX[0]=175;
        snakeY[0]=275;
        /*初始化第一节身子坐标:*/
        snakeX[1]=150;
        snakeY[1]=275;
        /*初始化第二节身子坐标:*/
        snakeX[2]=125;
        snakeY[2]=275;

    }

    /*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
    public GamePanel(){
    
    
        init();
    }

3.在面板类中的paintComponent()方法内部,写第4步画小蛇,详见以下代码。

 /*4.画小蛇:*/
//        画蛇头:
        Images.rightImg.paintIcon(this,g,snakeX[0],snakeY[0]);
        画第一节身子:
//        Images.bodyImg.paintIcon(this,g,snakeX[1],snakeY[1]);
        画第二节身子:
//        Images.bodyImg.paintIcon(this,g,snakeX[2],snakeY[2]);

//        用循环来画蛇身子:
        for(int i=1;i<length;i++){
    
    
           Images.bodyImg.paintIcon(this,g,snakeX[i],snakeY[i]);

        }

八、将蛇头按照行走方向改变

1.定义蛇头的方向,代码如下。

/*定义两个数组:
     * 一个数组,专门存储蛇的x轴坐标
     * 一个数组,专门存储蛇的y轴坐标
     * */
    int[] snakeX=new int[200];
    int[] snakeY=new int[200];
    /*定义蛇的长度:*/
    int length;

    /*定义蛇的行走方向:*/
    String direction;

    /*自定义一个初始化方法,初始化小蛇的坐标:*/
    public void init(){
    
    
        /*初始化蛇的长度:*/
        length=3;
        /*初始化蛇头坐标:*/
        snakeX[0]=175;
        snakeY[0]=275;
        /*初始化第一节身子坐标:*/
        snakeX[1]=150;
        snakeY[1]=275;
        /*初始化第二节身子坐标:*/
        snakeX[2]=125;
        snakeY[2]=275;

        /*初始化蛇头的方向为右:*/
        direction="R";//上U 下D 左L 右R

    }

2.根据不同方向画蛇头,代码如下。

 /*4.画小蛇:*/
//        画蛇头:
        if("R".equals(direction)){
    
    
           Images.rightImg.paintIcon(this,g,snakeX[0],snakeY[0]);
        }
        if("L".equals(direction)){
    
    
           Images.leftImg.paintIcon(this,g,snakeX[0],snakeY[0]);
        }
        if("U".equals(direction)){
    
    
           Images.upImg.paintIcon(this,g,snakeX[0],snakeY[0]);
        }
        if("D".equals(direction)){
    
    
           Images.downImg.paintIcon(this,g,snakeX[0],snakeY[0]);
        }

九、加入监听,空格来控制游戏是否开始

具体代码如下:

 /*游戏只有两个状态:开始、暂停*/
    boolean isStart=false;//默认游戏为暂停状态
/*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
    public GamePanel(){
    
    
        init();
//        将焦点定位在当前操作的面板上
        this.setFocusable(true);
//        加入监听:
//        addKeyListener表示加入键盘监听
        this.addKeyListener(new KeyAdapter() {
    
    //涉及到适配器模式
//            监听键盘按下操作
            @Override
            public void keyPressed(KeyEvent e) {
    
    
                super.keyPressed(e);
                int keyCode = e.getKeyCode();
//                System.out.println(keyCode);//空格对应32
                if (keyCode==32){
    
    
//                    System.out.println("点击了空格");
                    isStart=!isStart;//点击了空格,将isStart变成相反的状态
                    repaint();//重绘,调用paintComponent方法
                }

            }
        });
    }
 /*5.如果游戏是暂停的,界面中间有一个提示语*/
        if(isStart==false){
    
    
            /*画一个文字:*/
//            画笔设置颜色
            g.setColor(new Color(250, 3, 3));
//            设置文字的字体、加粗、大小
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
//            画文字:
//            drawString()方法,画字符串
//            文字内容、xy轴坐标
            g.drawString("点击空格开始游戏",250,330);
            
        }

十、小蛇向右动起来

具体代码如下:

/*加入一个定时器:*/
    Timer timer;
/*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
    public GamePanel(){
    
    
        init();
//        将焦点定位在当前操作的面板上
        this.setFocusable(true);
//        加入监听:
//        addKeyListener表示加入键盘监听
        this.addKeyListener(new KeyAdapter() {
    
    //涉及到适配器模式
//            监听键盘按下操作
            @Override
            public void keyPressed(KeyEvent e) {
    
    
                super.keyPressed(e);
                int keyCode = e.getKeyCode();
//                System.out.println(keyCode);//空格对应32
                if (keyCode==32){
    
    
//                    System.out.println("点击了空格");
                    isStart=!isStart;//点击了空格,将isStart变成相反的状态
                    repaint();//重绘,调用paintComponent方法
                }

            }
        });

        /*对定时器做初始化:*/
//        延迟毫秒数、一个ActionListener类对象
        timer=new Timer(100, new ActionListener() {
    
    //创建ActionListener对象,一个匿名内部类
//            ActionListener是 事件监听
//            相当于每100ms监听你是否发生一个动作
//            具体动作放入actionPerformed中
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                 if(isStart){
    
    //游戏开始状态下才动
//                     由最后一节身子往前一节身子上动
                     for(int i=length-1;i>0;i--){
    
    
                         snakeX[i]=snakeX[i-1];
                         snakeY[i]=snakeY[i-1];
                     }
//                     动头的位置
                     snakeX[0]+=25;
//                     防止蛇超出边界:
                     if(snakeX[0]>750){
    
    
                        snakeX[0]=25;
                     }
                     repaint();//重绘,根据新的蛇头蛇身的位置画
                 }
            }
        });
//        启动计时器
        timer.start();
    }

十一、小蛇上下左右动起来

代码如下:

 /*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
    public GamePanel(){
    
    
        init();
//        将焦点定位在当前操作的面板上
        this.setFocusable(true);
//        加入监听:
//        addKeyListener表示加入键盘监听
        this.addKeyListener(new KeyAdapter() {
    
    //涉及到适配器模式
//            监听键盘按下操作
            @Override
            public void keyPressed(KeyEvent e) {
    
    
                super.keyPressed(e);
                int keyCode = e.getKeyCode();
//                System.out.println(keyCode);//空格对应32
                /*监听空格:*/
                if (keyCode==KeyEvent.VK_SPACE){
    
    //将空格32封装起来
//                    System.out.println("点击了空格");
                    isStart=!isStart;//点击了空格,将isStart变成相反的状态
                    repaint();//重绘,调用paintComponent方法
                }
                /*监听向上箭头:*/
                if (keyCode==KeyEvent.VK_UP){
    
    
                    direction="U";

                }
                /*监听向下箭头:*/
                if (keyCode==KeyEvent.VK_DOWN){
    
    
                    direction="D";
                }
                /*监听向左箭头:*/
                if (keyCode==KeyEvent.VK_LEFT){
    
    
                    direction="L";
                }
                /*监听向右箭头:*/
                if (keyCode==KeyEvent.VK_RIGHT){
    
    
                    direction="R";
                }

            }
        });

        /*对定时器做初始化:*/
//        延迟毫秒数、一个ActionListener类对象
        timer=new Timer(100, new ActionListener() {
    
    //创建ActionListener对象,一个匿名内部类
//            ActionListener是 事件监听
//            相当于每100ms监听你是否发生一个动作
//            具体动作放入actionPerformed中
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                 if(isStart){
    
    //游戏开始状态下才动
//                     由最后一节身子往前一节身子上动
                     for(int i=length-1;i>0;i--){
    
    
                         snakeX[i]=snakeX[i-1];
                         snakeY[i]=snakeY[i-1];
                     }
//                     动头的位置
                     if("R".equals(direction)){
    
    
                         snakeX[0]+=25;
                     }
                     if("L".equals(direction)){
    
    
                         snakeX[0]-=25;
                     }
                     if("U".equals(direction)){
    
    
                         snakeY[0]-=25;
                     }
                     if("D".equals(direction)){
    
    
                         snakeY[0]+=25;
                     }

//                     防止蛇超出边界:
                     if(snakeX[0]>745){
    
    
                        snakeX[0]=25;
                     }
                     if (snakeX[0]<25) {
    
    
                         snakeX[0]=745;
                     }
                     if(snakeY[0]<70){
    
    
                         snakeY[0]=700;
                     }
                     if(snakeY[0]>700){
    
    
                         snakeY[0]=70;
                     }
                     repaint();//重绘,根据新的蛇头蛇身的位置画
                 }
            }
        });
//        启动计时器
        timer.start();
    }

十二、画食物、吃食物

1.画食物代码:

/*定义食物的xy轴坐标*/
    int foodX,foodY;
 /*自定义一个初始化方法,初始化小蛇的坐标:*/
    public void init(){
    
    
        /*初始化蛇的长度:*/
        length=3;
        /*初始化蛇头坐标:*/
        snakeX[0]=175;
        snakeY[0]=275;
        /*初始化第一节身子坐标:*/
        snakeX[1]=150;
        snakeY[1]=275;
        /*初始化第二节身子坐标:*/
        snakeX[2]=125;
        snakeY[2]=275;

        /*初始化蛇头的方向为右:*/
        direction="R";//上U 下D 左L 右R

        /*初始化食物的坐标:*/
        foodX=300;
        foodY=200;
    }
 /*6.画食物:*/    Images.foodImg.paintIcon(this,g,foodX,foodY);

2.吃食物代码:

 /*对定时器做初始化:*/
//        延迟毫秒数、一个ActionListener类对象
        timer=new Timer(100, new ActionListener() {
    
    //创建ActionListener对象,一个匿名内部类
//            ActionListener是 事件监听
//            相当于每100ms监听你是否发生一个动作
//            具体动作放入actionPerformed中
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                 if(isStart){
    
    //游戏开始状态下才动
//                     由最后一节身子往前一节身子上动
                     for(int i=length-1;i>0;i--){
    
    
                         snakeX[i]=snakeX[i-1];
                         snakeY[i]=snakeY[i-1];
                     }
//                     动头的位置
                     if("R".equals(direction)){
    
    
                         snakeX[0]+=25;
                     }
                     if("L".equals(direction)){
    
    
                         snakeX[0]-=25;
                     }
                     if("U".equals(direction)){
    
    
                         snakeY[0]-=25;
                     }
                     if("D".equals(direction)){
    
    
                         snakeY[0]+=25;
                     }

//                     防止蛇超出边界:
                     if(snakeX[0]>725){
    
    
                        snakeX[0]=25;
                     }
                     if (snakeX[0]<25) {
    
    
                         snakeX[0]=725;
                     }
                     if(snakeY[0]<75){
    
    
                         snakeY[0]=700;
                     }
                     if(snakeY[0]>700){
    
    
                         snakeY[0]=75;
                     }

                     /*监测碰撞的动作,也就是吃食物的动作:*/
//                     如果蛇头坐标与食物坐标一样时,即吃到食物
                     if (snakeX[0]==foodX&&snakeY[0]==foodY) {
    
    
//                         蛇长度加一
                         length++;
//                         食物坐标改变,随机生成坐标,随机数必须是25的倍数,x轴【25,725】,y轴【75,700】
//                         方法一:
//                         math.random()-->[0.0,1.0)
//                         math.random()*29-->[0.0,29.0)
//                         (int)(math.random()*29)-->[0,28]
//                         (int)(math.random()*29)+1-->[1,29]
                         foodX=((int)(Math.random()*29)+1)*25;//[1,29]*25=[25,725]
//                         方法二:
//                         [75,700]-->[3,28]*25
//                         [3,28]-->[0,25]+3
//                         [0,25]
//                         new Random().nextInt(26)-->[0,26)-->[0,25]
                         foodY=((new Random().nextInt(26))+3)*25;//[3,28]*25=[75,700]
                     }

                     repaint();//重绘,根据新的蛇头蛇身的位置画
                 }
            }
        });
//        启动计时器
        timer.start();
    }

十三、画积分

代码如下:

 /*定义一个积分:*/
    int score;
     /*自定义一个初始化方法,初始化小蛇的坐标:*/
    public void init(){
    
    
        /*初始化蛇的长度:*/
        length=3;
        /*初始化蛇头坐标:*/
        snakeX[0]=175;
        snakeY[0]=275;
        /*初始化第一节身子坐标:*/
        snakeX[1]=150;
        snakeY[1]=275;
        /*初始化第二节身子坐标:*/
        snakeX[2]=125;
        snakeY[2]=275;

        /*初始化蛇头的方向为右:*/
        direction="R";//上U 下D 左L 右R

        /*初始化食物的坐标:*/
        foodX=300;
        foodY=200;

        /*初始化积分:*/
        score=0;

    }
 /*监测碰撞的动作,也就是吃食物的动作:*/
//                     如果蛇头坐标与食物坐标一样时,即吃到食物
                     if (snakeX[0]==foodX&&snakeY[0]==foodY) {
    
    
//                         蛇长度加一
                         length++;
//                         食物坐标改变,随机生成坐标,随机数必须是25的倍数,x轴【25,725】,y轴【75,700】
//                         方法一:
//                         math.random()-->[0.0,1.0)
//                         math.random()*29-->[0.0,29.0)
//                         (int)(math.random()*29)-->[0,28]
//                         (int)(math.random()*29)+1-->[1,29]
                         foodX=((int)(Math.random()*29)+1)*25;//[1,29]*25=[25,725]
//                         方法二:
//                         [75,700]-->[3,28]*25
//                         [3,28]-->[0,25]+3
//                         [0,25]
//                         new Random().nextInt(26)-->[0,26)-->[0,25]
                         foodY=((new Random().nextInt(26))+3)*25;//[3,28]*25=[75,700]

                         /*每吃到一次食物,积分加10*/
                         score+=10;

                     }

                     repaint();//重绘,根据新的蛇头蛇身的位置画
                 }
            }
        });
  /*7.画积分(画文字):*/
        g.setColor(new Color(246, 245, 245));
        g.setFont(new Font("微软雅黑",Font.BOLD,20));
        g.drawString("积分:"+score,280,40);

十四、死亡判定

        /*初始化积分:*/
        score=0;
 @Override
            public void keyPressed(KeyEvent e) {
    
    
                super.keyPressed(e);
                int keyCode = e.getKeyCode();
//                System.out.println(keyCode);//空格对应32
                /*监听空格:*/
                if (keyCode==KeyEvent.VK_SPACE){
    
    //将空格32封装起来
                   if(isDie){
    
    
//                    全部恢复到初始化状态
                       init();
                       isDie=false;
                   }else{
    
    
                       isStart=!isStart;
                       repaint();//重绘
                   }

                }
 @Override
            public void actionPerformed(ActionEvent e) {
    
    
                 if(isStart&&isDie==false){
    
    //游戏开始状态、小蛇活着的状态下才动
//                     由最后一节身子往前一节身子上动
                     for(int i=length-1;i>0;i--){
    
    
                         snakeX[i]=snakeX[i-1];
                         snakeY[i]=snakeY[i-1];
                     }
  /*死亡判定:*/
                     for(int i=1;i<length;i++){
    
    
//                         如果蛇头的坐标和身子的任意一节坐标一样
                         if (snakeX[0]==snakeX[i]&&snakeY[0]==snakeY[i]) {
    
    
//                             判定为死亡状态
                             isDie=true;
                         }
 /*8.画死亡状态:*/
        if(isDie){
    
    
            g.setColor(new Color(246, 11, 11));
            g.setFont(new Font("微软雅黑",Font.BOLD,35));
            g.drawString("小蛇死亡!游戏停止,按下空格重新开始游戏",50,330);

十五、游戏打包

在对方电脑有jre的情况下,可以将程序打包发给对方。
1.选择File菜单下的项目结构Project Structure
在这里插入图片描述
2.点击Artifacts,再点击左上角的“+”号,JAR包下选择from modules with dependencies从项目中将所有依赖选上
在这里插入图片描述
3.在Main Class中选择游戏进入的入口,即main方法所在的类。
在这里插入图片描述
4.此时已经生成了一个jar文件,点击Apply运行,再OK即可。
在这里插入图片描述
5.在菜单栏Build下选择Build Artifacts,再点击Build,就会生成jar包。
在这里插入图片描述
在这里插入图片描述
6.生成好的jar包,可以在out文件夹下,找到刚生成好的jar包,右击选择Show inExplorer,就可以打开jar包所在位置
在这里插入图片描述
7.直接双击jar包,即可运行。
在这里插入图片描述
在这里插入图片描述

源代码

Images类:

package com.game;

import javax.swing.*;
import java.net.URL;

/*用来获取游戏中所有涉及的图片*/
public class Images {
    
    
/*面向对象思想:将图片进行封装,封装为一个对象,在程序中通过操纵这个对象来操纵图片*/

    /*将图片的路径封装为一个对象:*/
//    class获取字节码
//    getResource获取图片路径
//    在菜单Build下选中第二个Build Module,重写构建一下,生成对应的字节码信息
//    在out文件夹下找图片路径
//    '/'指代相对路径,相对file:/C:/Users/ASUS/Desktop/贪吃蛇小游戏/out/production/TestSnake/而言
    public static URL bodyURL=Images.class.getResource("/images/body.png");

    /*将图片封装为程序中一个对象:*/
//    ImageIcon构造器,可以直接把url传进去
    public static ImageIcon bodyImg=new ImageIcon(bodyURL);

    /*同理,将剩下的图片都封装:*/
    public static URL downURL=Images.class.getResource("/images/down.png");
    public static ImageIcon downImg=new ImageIcon(downURL);

    public static URL foodURL=Images.class.getResource("/images/food.png");
    public static ImageIcon foodImg=new ImageIcon(foodURL);

    public static URL headerURL=Images.class.getResource("/images/header.png");
    public static ImageIcon headerImg=new ImageIcon(headerURL);

    public static URL leftURL=Images.class.getResource("/images/left.png");
    public static ImageIcon leftImg=new ImageIcon(leftURL);

    public static URL rightURL=Images.class.getResource("/images/right.png");
    public static ImageIcon rightImg=new ImageIcon(rightURL);

    public static URL upURL=Images.class.getResource("/images/up.png");
    public static ImageIcon upImg=new ImageIcon(upURL);



}

StartGame类:

package com.game;

import javax.swing.*;
import java.awt.*;

public class StartGame {
    
    
    public static void main(String[] args) {
    
    
        /*1.创建一个窗体:*/
        JFrame jf = new JFrame();
        /*2.给窗体设置一个标题:*/
        jf.setTitle("贪吃蛇小游戏 by:cym");
        /*3.设置窗体弹出的坐标,对应窗体的宽和高:*/
//        获取屏幕的宽、高
        int width = Toolkit.getDefaultToolkit().getScreenSize().width;
        int height = Toolkit.getDefaultToolkit().getScreenSize().height;
        jf.setBounds((width - 800) / 2, (height - 800) / 2, 800, 800);
        /*4.设置窗体大小不可调:*/
        jf.setResizable(false);
        /*5.关闭窗口的同时,程序随之关闭:*/
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        /*6.创建面板:*/
        GamePanel gp = new GamePanel();
//        将面板放入窗体中:
        jf.add(gp);

        /*默认情况下窗体隐藏,需要进行显现(最好放在最后):*/
        jf.setVisible(true);

    }
}

GamePanel类:

package com.game;

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

/*面板*/
//继承JPanel才具备面板的功能,才成为一个面板
public class GamePanel extends JPanel {
    
    
    /*定义两个数组:
     * 一个数组,专门存储蛇的x轴坐标
     * 一个数组,专门存储蛇的y轴坐标
     * */
    int[] snakeX = new int[200];
    int[] snakeY = new int[200];
    /*定义蛇的长度:*/
    int length;

    /*定义蛇的行走方向:*/
    String direction;

    /*游戏只有两个状态:开始、暂停*/
    boolean isStart = false;//默认游戏为暂停状态

    /*加入一个定时器:*/
    Timer timer;

    /*定义食物的xy轴坐标*/
    int foodX, foodY;

    /*定义一个积分:*/
    int score;

    /*加入一个变量,判断小蛇生死状态:*/
    boolean isDie = false;//默认小蛇为活着的状态

    /*自定义一个初始化方法,初始化小蛇的坐标:*/
    public void init() {
    
    
        /*初始化蛇的长度:*/
        length = 3;
        /*初始化蛇头坐标:*/
        snakeX[0] = 175;
        snakeY[0] = 275;
        /*初始化第一节身子坐标:*/
        snakeX[1] = 150;
        snakeY[1] = 275;
        /*初始化第二节身子坐标:*/
        snakeX[2] = 125;
        snakeY[2] = 275;

        /*初始化蛇头的方向为右:*/
        direction = "R";//上U 下D 左L 右R

        /*初始化食物的坐标:*/
        foodX = 300;
        foodY = 200;

        /*初始化积分:*/
        score = 0;

    }


    /*构造器,调用一下小蛇默认方法init(),进行面板初始化的时候就默认了蛇的位置:*/
    public GamePanel() {
    
    
        init();
//        将焦点定位在当前操作的面板上
        this.setFocusable(true);
//        加入监听:
//        addKeyListener表示加入键盘监听
        this.addKeyListener(new KeyAdapter() {
    
    //涉及到适配器模式
            //            监听键盘按下操作
            @Override
            public void keyPressed(KeyEvent e) {
    
    
                super.keyPressed(e);
                int keyCode = e.getKeyCode();
//                System.out.println(keyCode);//空格对应32
                /*监听空格:*/
                if (keyCode == KeyEvent.VK_SPACE) {
    
    //将空格32封装起来
                    if (isDie) {
    
    
//                    全部恢复到初始化状态
                        init();
                        isDie = false;
                    } else {
    
    
                        isStart = !isStart;
                        repaint();//重绘
                    }

                }
                /*监听向上箭头:*/
                if (keyCode == KeyEvent.VK_UP) {
    
    
                    direction = "U";

                }
                /*监听向下箭头:*/
                if (keyCode == KeyEvent.VK_DOWN) {
    
    
                    direction = "D";
                }
                /*监听向左箭头:*/
                if (keyCode == KeyEvent.VK_LEFT) {
    
    
                    direction = "L";
                }
                /*监听向右箭头:*/
                if (keyCode == KeyEvent.VK_RIGHT) {
    
    
                    direction = "R";
                }

            }
        });

        /*对定时器做初始化:*/
//        延迟毫秒数、一个ActionListener类对象
        timer = new Timer(100, new ActionListener() {
    
    //创建ActionListener对象,一个匿名内部类
            //            ActionListener是 事件监听
//            相当于每100ms监听你是否发生一个动作
//            具体动作放入actionPerformed中
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                if (isStart && isDie == false) {
    
    //游戏开始状态、小蛇活着的状态下才动
//                     由最后一节身子往前一节身子上动
                    for (int i = length - 1; i > 0; i--) {
    
    
                        snakeX[i] = snakeX[i - 1];
                        snakeY[i] = snakeY[i - 1];
                    }
//                     动头的位置
                    if ("R".equals(direction)) {
    
    
                        snakeX[0] += 25;
                    }
                    if ("L".equals(direction)) {
    
    
                        snakeX[0] -= 25;
                    }
                    if ("U".equals(direction)) {
    
    
                        snakeY[0] -= 25;
                    }
                    if ("D".equals(direction)) {
    
    
                        snakeY[0] += 25;
                    }

//                     防止蛇超出边界:
                    if (snakeX[0] > 725) {
    
    
                        snakeX[0] = 25;
                    }
                    if (snakeX[0] < 25) {
    
    
                        snakeX[0] = 725;
                    }
                    if (snakeY[0] < 75) {
    
    
                        snakeY[0] = 700;
                    }
                    if (snakeY[0] > 700) {
    
    
                        snakeY[0] = 75;
                    }

                    /*监测碰撞的动作,也就是吃食物的动作:*/
//                     如果蛇头坐标与食物坐标一样时,即吃到食物
                    if (snakeX[0] == foodX && snakeY[0] == foodY) {
    
    
//                         蛇长度加一
                        length++;
//                         食物坐标改变,随机生成坐标,随机数必须是25的倍数,x轴【25,725】,y轴【75,700】
//                         方法一:
//                         math.random()-->[0.0,1.0)
//                         math.random()*29-->[0.0,29.0)
//                         (int)(math.random()*29)-->[0,28]
//                         (int)(math.random()*29)+1-->[1,29]
                        foodX = ((int) (Math.random() * 29) + 1) * 25;//[1,29]*25=[25,725]
//                         方法二:
//                         [75,700]-->[3,28]*25
//                         [3,28]-->[0,25]+3
//                         [0,25]
//                         new Random().nextInt(26)-->[0,26)-->[0,25]
                        foodY = ((new Random().nextInt(26)) + 3) * 25;//[3,28]*25=[75,700]

                        /*每吃到一次食物,积分加10*/
                        score += 10;

                    }

                    /*死亡判定:*/
                    for (int i = 1; i < length; i++) {
    
    
//                         如果蛇头的坐标和身子的任意一节坐标一样
                        if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
    
    
//                             判定为死亡状态
                            isDie = true;
                        }


                    }

                    repaint();//重绘,根据新的蛇头蛇身的位置画
                }
            }
        });
//        启动计时器
        timer.start();
    }

    /*此方法特殊,就相当于图形版的main方法,自动调用*/
    @Override
    protected void paintComponent(Graphics g) {
    
    
        super.paintComponent(g);
        /*1.填充背景颜色:*/
//        颜色由rgb(red green blue)组成
//        可通过点击左边行号旁的色块进行颜色修改
        this.setBackground(new Color(243, 239, 190, 255));

        /*2.画头部的图片header.png:*/
//        Images.headerImg:调用图片类中的header.png图片
//        paintIcon()方法将图片在面板中绘制出来
//        this:当前面板
//        g:使用的画笔
//        x、y:对应坐标轴
        Images.headerImg.paintIcon(this, g, 15, 10);

        /*3.画一个矩形:*/
//        先调节画笔颜色:
        g.setColor(new Color(205, 228, 246));
//        fillRect填充矩形
        g.fillRect(15, 70, 750, 670);


        /*4.画小蛇:*/
//        画蛇头:
        if ("R".equals(direction)) {
    
    
            Images.rightImg.paintIcon(this, g, snakeX[0], snakeY[0]);
        }
        if ("L".equals(direction)) {
    
    
            Images.leftImg.paintIcon(this, g, snakeX[0], snakeY[0]);
        }
        if ("U".equals(direction)) {
    
    
            Images.upImg.paintIcon(this, g, snakeX[0], snakeY[0]);
        }
        if ("D".equals(direction)) {
    
    
            Images.downImg.paintIcon(this, g, snakeX[0], snakeY[0]);
        }

        画第一节身子:
//        Images.bodyImg.paintIcon(this,g,snakeX[1],snakeY[1]);
        画第二节身子:
//        Images.bodyImg.paintIcon(this,g,snakeX[2],snakeY[2]);

//        用循环来画蛇身子:
        for (int i = 1; i < length; i++) {
    
    
            Images.bodyImg.paintIcon(this, g, snakeX[i], snakeY[i]);

        }

        /*5.如果游戏是暂停的,界面中间有一个提示语*/
        if (isStart == false) {
    
    
            /*画一个文字:*/
//            画笔设置颜色
            g.setColor(new Color(222, 13, 245));
//            设置文字的字体、加粗、大小
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
//            画文字:
//            drawString()方法,画字符串
//            文字内容、xy轴坐标
            g.drawString("点击空格开始游戏", 250, 330);

        }

        /*6.画食物:*/
        Images.foodImg.paintIcon(this, g, foodX, foodY);

        /*7.画积分(画文字):*/
        g.setColor(new Color(246, 245, 245));
        g.setFont(new Font("微软雅黑", Font.BOLD, 20));
        g.drawString("积分:" + score, 280, 40);

        /*8.画死亡状态:*/
        if (isDie) {
    
    
            g.setColor(new Color(246, 11, 11));
            g.setFont(new Font("微软雅黑", Font.BOLD, 35));
            g.drawString("小蛇死亡!游戏停止,按下空格重新开始游戏", 50, 330);
        }

    }
}

贪吃蛇小游戏b站原视频:
https://www.bilibili.com/video/BV1w5411g73U?share_source=copy_web

贪吃蛇图片素材:
链接:https://pan.baidu.com/s/1B5aIR-luqF94Y-8Lhwck0g?pwd=5gj9
提取码:5gj9
解压缩密码:456654

我的jar包:
链接:https://pan.baidu.com/s/1Zk31sOPTr_WCidZUd2Jl9A?pwd=4h7d
提取码:4h7d

若双击jar包不能够直接运行的解决办法:
指路我的博客:http://t.csdn.cn/1QALL

猜你喜欢

转载自blog.csdn.net/weixin_47678894/article/details/125454495