Write a small snake game in java (the source code is at the end)

1. Introduction

Skills involved:

  1. cycle, branch
  2. method extraction
  3. Use of arrays
  4. object oriented
  5. Inheritance, overriding of subclass methods
  6. interface, implementation of the interface
  7. GUI (graphical interface programming)

Components in the GUI:
7.1 Window
7.2 Popup
7.3 Panel
7.4 Text Box
7.5 List Box
7.6 Button 7.7
Picture
7.8 Interactive Events: Listening Events (Mouse Events, Keyboard Events)

GUI technology is not popular anymore! ! ! The interface is too simple and rough! !

Has been eliminated, why still learn?

  1. raise interest
  2. layered thinking
  3. Exercise Listener Thoughts
  4. Infer other cases from one example (replace the picture of the snake with other pictures, and replace the picture of food with other pictures)
  5. used in work

Note:
Learning is thinking! ! !

2. Schematic diagram

The pixel size of the small snake material image is 25*25, so the value on the corresponding xy axis also takes 25 as the step size.
insert image description here

3. Enter the topic

1. Open the IDEA tool and create a new module module.
insert image description here
2. Select java and click next.
insert image description here
3. Take a name called TestSnake, and then click finish.
insert image description here
4. Open the newly created TestSnake, create a new folder under src, and name it images.
insert image description here
insert image description here
5. Copy all the material pictures to the images folder.
insert image description here
insert image description here

4. Load the picture into the program

1. Create a new folder com.game under src, and create a class Images under this folder to obtain pictures.
insert image description here
insert image description here
insert image description here
2. Open the Images class, write the following code to encapsulate the image, and only need the class name + image for subsequent use.

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. Regarding the operations of " select the second Build Module under the menu Build, rewrite and build it, and generate the corresponding bytecode information " and " find the image path under the out folder " in the code comment, the specific steps are as follows:
insert image description here

5. Create a form

1. Create a new class called StartGame under the com.game folder.
insert image description here
insert image description here
2. Write the following code into the StartGame class.

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. The method of calculating the centering of the form on the screen is as follows:
insert image description here

6. Create a panel

1. Create a new class GamePanel.
insert image description here
insert image description here
2. Write the following code into the GamePanel class.

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. Add Step 6 to create a panel in the StartGame class, and add the panel to the form.

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);


    }
}

7. Draw a static snake

1. Take this picture as an example, as a default snake, with one head and two sections of body.
insert image description here
2. Outside the paintComponent() method in the panel class, write the following code.

/*定义两个数组:
     * 一个数组,专门存储蛇的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. Inside the paintComponent() method in the panel class, write step 4 to draw the snake, see the following code for details.

 /*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]);

        }

8. Change the snake head according to the walking direction

1. Define the direction of the snake head, the code is as follows.

/*定义两个数组:
     * 一个数组,专门存储蛇的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. Draw snake heads according to different directions, the code is as follows.

 /*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]);
        }

9. Add monitoring and space to control whether the game starts

The specific code is as follows:

 /*游戏只有两个状态:开始、暂停*/
    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);
            
        }

10. The little snake moves to the right

The specific code is as follows:

/*加入一个定时器:*/
    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();
    }

Eleven, the little snake moves up and down, left and right

code show as below:

 /*构造器,调用一下小蛇默认方法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();
    }

12. Draw food, eat food

1. Draw the food code:

/*定义食物的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. Eat food code:

 /*对定时器做初始化:*/
//        延迟毫秒数、一个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();
    }

Thirteen, drawing points

code show as below:

 /*定义一个积分:*/
    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);

14. Determination of death

        /*初始化积分:*/
        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);

15. Game packaging

If the other party's computer has jre, the program can be packaged and sent to the other party.
1. Select the project structure Project Structure under the File menu
insert image description here
2. Click Artifacts, then click the "+" sign in the upper left corner, select from modules with dependencies under the JAR package and select all dependencies from the project
insert image description here
3. Select the game in the Main Class The entrance to enter, that is, the class where the main method is located.
insert image description here
4. At this point, a jar file has been generated, click Apply to run, and then OK.
insert image description here
5. Select Build Artifacts under Build in the menu bar, and then click Build to generate a jar package.
insert image description here
insert image description here
6. For the generated jar package, you can find the newly generated jar package in the out folder, right-click and select Show inExplorer to open the location of the jar package. 7. Double-
insert image description here
click the jar package directly to run it.
insert image description here
insert image description here

source code

Images class:

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 class:

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 class:

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);
        }

    }
}

The original video of Snake Game B:
https://www.bilibili.com/video/BV1w5411g73U?share_source=copy_web

Greedy snake image material:
Link: https://pan.baidu.com/s/1B5aIR-luqF94Y-8Lhwck0g?pwd=5gj9
Extraction code: 5gj9
Decompression password: 456654

My jar package:
Link: https://pan.baidu.com/s/1Zk31sOPTr_WCidZUd2Jl9A?pwd=4h7d
Extraction code: 4h7d

If you double-click the jar package and can’t run it directly, the solution:
point to my blog: http://t.csdn.cn/1QALL

Guess you like

Origin blog.csdn.net/weixin_47678894/article/details/125454495