JavaSE project | Realize the Snake game in pure Java

Table of contents

1: Implementation steps of the Snake game

1. Draw the window

2. Add a canvas to the window

3. Add a black game area to the canvas

4. Put static snake

5. Define the snake data structure

6. Control the direction of the snake head

7. Put the start prompt message

8. Press the space bar to start the game

9. Make the snake move

10. Implement Pause

11. Implement steering function

12. Add food

13. Eat food

Two: core source code

1. MySnake class

2. MyPanel class

3. Direction class

book recommendation

One: "Java Core Volume II"

Two: "Core Principles of Distributed Middleware and Best Practices of RocketMQ"


Show results:

1: Implementation steps of the Snake game

design game drawings

Achieve 700*800

①The width value is 700 pixels, each grid is 25 pixels, and there are 28 grids in total.

②The height value is 800 pixels, each grid is 25 pixels, and there are 32 grids in total.

1. Draw the window

Write the specific code as follows:

package demo;

import javax.swing.*;


public class MySnake {
    public static void main(String[] args) {
        // 创建一个窗口
        JFrame frame = new JFrame();
        // 指定窗口x和y的相对位置及窗口的宽度和高度值
        frame.setBounds(500,25,700,800);
        // 不允许拖拽改变大小
        frame.setResizable(false);
        // 当点击窗口关闭按钮,执行操作是退出
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 当前窗口显示出来
        frame.setVisible(true);

    }
}

The running effect is as follows:

2. Add a canvas to the window

①Create a new class MyPanel canvas and inherit JPanel at the same time. Write two methods: a no-argument construction method and an overridden drawing component, where the parameter is regarded as a brush.

② Write code in the method body: first call the parent class method to do some basic work, then set the background color, and finally add the canvas to the window of the main method.

Write the specific code as follows:

package demo;

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

public class MyPanel extends JPanel {
    public MyPanel() {
    }
    // 重写画组件的方法
    @Override
    protected void paintComponent(Graphics g) {
        // 调用父类值的方法做一些基本工作
        super.paintComponent(g);
        // 设置背景颜色
        this.setBackground(Color.red);
    }
}

Add canvas in main method

The running effect is as follows:

Execution idea: When adding a canvas, execute the no-argument construction method, and then automatically execute the method of rewriting the drawing component.

3. Add a black game area to the canvas

Use the brush to fill the entire area. The four parameters are: the x coordinate in the canvas, the y coordinate in the canvas, and the width and height values.

Write the specific code as follows:

package demo;

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

public class MyPanel extends JPanel {
    public MyPanel() {
    }
    // 重写画组件的方法
    @Override
    protected void paintComponent(Graphics g) {
        // 调用父类值的方法做一些基本工作
        super.paintComponent(g);
        // 设置背景颜色
        this.setBackground(Color.red);
        // 在画布中添加游戏区域(这里就和框一样大小)
        g.fillRect(0,0,700,800);
    }
}

The running effect is as follows:

4. Put static snake

①First of all, a static snake is composed of pictures. By default, the head points to the right; a static snake is composed of a snake head picture and two snake body pictures!

②It is mainly divided into two steps: the first step is to declare the pictures of the snake head and body; the second step is to add the declared pictures to the canvas!

Write the specific code as follows:

package demo;

import javax.swing.*;
import java.awt.*;
import java.lang.invoke.VarHandle;

public class MyPanel extends JPanel {
    // 声明右侧蛇头和身体
    ImageIcon right = new ImageIcon("images/right.png");
    ImageIcon body = new ImageIcon("images/body.png");

    public MyPanel() {
    }

    // 重写画组件的方法
    @Override
    protected void paintComponent(Graphics g) {
        // 调用父类值的方法做一些基本工作
        super.paintComponent(g);
        // 设置背景颜色
        this.setBackground(Color.red);
        // 在画布中添加游戏区域
        g.fillRect(0,0,700,800);

        // 在画布中添加右侧蛇头和身体
        right.paintIcon(this,g,100,100);
        body.paintIcon(this,g,75,100);
        body.paintIcon(this,g,50,100);
    }
}

The running effect is as follows:

5. Define the snake data structure

When the game is running, the body of the snake will continue to grow longer, and the position of the snake will continue to change. Therefore, it is necessary to store the length and position of the snake, which is currently done using an array. The specific operation steps are as follows:

① Declare an initial value, indicating that the initial length of the snake is 3

② Declare the x-coordinate and y-coordinate of the snake. When the object is created and executes the no-argument construction method, the initialization of the snake’s right head and body position is completed. At this time, there is no need to write the code for the static snake body before, and the array is traversed by writing a loop That's it.

Write the specific code as follows:

 

Note: The effect of this step is the same as that of placing a static snake, but this code method is more general! For example: the initialization length is not necessarily 3, and the specific position is not necessarily at the position of 100 pixels; this encoding method is easier to maintain!

6. Control the direction of the snake head

The snake head can move up, down, left, and right. Operation steps:

① Define an enumeration direction, which has four values: up, down, left, and right, and declare three snake head pictures up, down, and left respectively.

② Declare an enumeration type variable to identify the direction of the snake head, and change the direction of the snake head by changing the value of the enumeration direction.

Define an enumeration variable to represent the direction of the snake head

package demo;
public enum Direction { // 上、下、左、右
    top,bottom,left,right;
}

Declare the top, bottom, and left snake head images in the canvas

 To determine the direction of the tongue by enumeration

The running effect is as follows:

7. Put the start prompt message

In the method of rewriting the drawing component, use the brush to complete it!

Write the specific code as follows:

The running effect is as follows:

8. Press the space bar to start the game

① Declare a boolean type variable isStart as false to mark the state of the game.

② Judgment, when the value of isStart is false, display the start prompt text.

③In the no-argument construction method, set the acquisition focus to true, that is, you can acquire keyboard events.

④Who will listen after getting the keyboard event, add this.addKeyListener(this); where this represents itself, but the listening event has not been processed yet; you need to implement the KeyListener interface in the MyPanel class, and rewrite three methods, namely: keyTyped(), keyPressed(), and keyReleased() can be implemented in the keyPressed() method or keyReleased() method. The parameter keyEvent indicates which key is pressed, and different numbers can be obtained by pressing different keys. Then pass e.getKeyCode() Get the number corresponding to the current key, and then judge, if you press the space bar or the number 32, the value of the current mark isStart will be reversed, and there is no prompt message to start the game, you need to call the repaint() method, which means redrawing the component.

Declare variables to mark the state of the game

Determine whether to display prompt information according to the state 

In the no-argument constructor, get the focus and add a listener (implement the KeyListener class and override three methods)

Write the logic in the overridden press or pop method

When the space bar is indeed pressed, the game state is reversed and the component is redrawn!

Note: You must not write true directly, so no matter how many times you press the space bar, it will always be true, then the prompt text at the beginning will not be displayed. What we need is to click the prompt text to disappear, and press the second prompt text to display, and the cycle repeats!

9. Make the snake move

①Create a timer Timer, the first parameter is how long, for example: 100 milliseconds, the second parameter is who to look for when the time is up-this, this needs to implement the ActionListener interface, rewrite the actionPerformed() method, that is, when the time To call the actionPerformed () method.

② Start the timer in the construction method, and call the rewritten actionPerformed() method when it reaches 100 milliseconds.

③In the body of rewriting the actionPerformed() method, realize the movement of the snake; the idea of ​​moving the snake:

If the snake moves horizontally to the right, the last body moves to the position of the previous body, that is, the x coordinate changes and the y coordinate does not change; if the snake's head also moves horizontally to the right, the x coordinate of the snake's head should be at the current position +25.

Create a timer object Timer

This implements the ActionListener interface to override the actionPerformed() method

Start the timer in the no-argument constructor

In the rewritten actionPerformed() method body, implement the snake movement logic

The running effect is as follows: 

10. Implement Pause

Judging in the rewriting actionPerformed() method, when the value of the flag is true, the snake will move

When the tooltip is still there and the game has not started, it is static

When the space bar is pressed, the prompt information is not displayed and the snake starts to move horizontally to the right. The operation effect is as follows

When pressing the space bar again, the game pauses and a prompt message is displayed, and the running effect is as follows

11. Implement steering function

①Change the value of the variable direction by keyboard keys.

②In the actionPerformed() method, the snake head moves up, down, left, and right by judging the direction of the variable direction.

Specifies the direction of the snake's head according to the keyboard key pressed

Move the snake head according to the direction of the snake head

After running, press the space bar, and then press the arrow keys, the snake will move, and the running effect is as follows:

12. Add food

① Randomly generate food, declare two variables foodX and foodY to indicate the location of the food, declare a random variable random, and declare the food picture food.

②In the no-parameter construction method, generate the coordinates of food foodX and foodY

foodX = 25 + 25 * random.nextInt(20);

foodY = 25 + 25 * random.nextInt(20);

③ Add food in the paintComponect() method

Declare two variables to indicate the location of the food and declare the image of the food

Introduce the coordinates of the generated things in the no-argument construction method 

add food

Results of the:

13. Eat food

When the coordinates of the snake's head and the food completely overlap, it means that the food is eaten, and the length of the snake is increased by 1, and a new food is generated; the specific implementation ideas are as follows:

①In the actionPerformed() method, if it is judged that the x coordinate of the snake head is the same as the food x coordinate foodX, and the y coordinate of the snake head is the same as the food y coordinate foodY, then add 1 to the length.

② Regenerate the x and y coordinates of the food.

Determine whether the coordinates of the snake's head and tail are consistent, which means that the food has been eaten, and the length is increased by 1, and a food is randomly generated again

The running effect is as follows: 

Two: core source code

1. MySnake class

package demo;

import javax.swing.*;

public class MySnake {

    public static void main(String[] args) {
        // 创建一个窗口
        JFrame frame = new JFrame();
        // 指定窗口x和y的相对位置及窗口的宽度和高度值
        frame.setBounds(500, 25, 700, 800);
        // 不允许拖拽改变大小
        frame.setResizable(false);
        // 当点击窗口关闭按钮,执行操作是退出
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 添加画布
        frame.add(new MyPanel());

        // 当前窗口显示出来
        frame.setVisible(true);

    }
}

2. MyPanel class

package demo;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.lang.invoke.VarHandle;
import java.util.Random;


public class MyPanel extends JPanel implements KeyListener, ActionListener {
    // 声明右侧蛇头和身体
    ImageIcon right = new ImageIcon("images/right.png");
    ImageIcon body = new ImageIcon("images/body.png");

    // 声明上、下、左侧的蛇头图片
    ImageIcon top = new ImageIcon("images/top.png");
    ImageIcon bottom = new ImageIcon("images/bottom.png");
    ImageIcon left = new ImageIcon("images/left.png");

    // 声明两个变量表示食物的位置
    int foodX;
    int foodY;
    // 声明一个随机数
    Random random = new Random();
    // 声明食物的图片
    ImageIcon food = new ImageIcon("images/food.png");

    // 声明一个初始值,表示蛇的长度为3
    int len = 3;
    // 声明两个数组分别存放蛇的x和y的坐标
    int[] snakeX = new int[28 * 32]; // 最大值=宽度格子*高度格子,是蛇的最大理论长度
    int[] snakeY = new int[28 * 32];
    // 声明枚举变量,标识当前的蛇头方向
    Direction direction = Direction.right; // 假设默认是向下

    // 声明一个变量,标记游戏的状态,当为false时,表示没有开始游戏,true表示开始游戏
    boolean isStart = false;

    // 创建一个定时器
    Timer timer = new Timer(100, this); // this必须实现ActionListener接口

    public MyPanel() {
        // 初始化蛇的头部和身体的初始值
        snakeX[0] = 100;
        snakeY[0] = 100;

        snakeX[1] = 75;
        snakeY[1] = 100;

        snakeX[2] = 50;
        snakeY[2] = 100;

        // 设置获取焦点为true
        this.setFocusable(true);
        // 添加监听
        this.addKeyListener(this); // this代表的是MyPanel,这个类要实现KeyListener类,并重写三个方法
        // 启动定时器
        timer.start(); // 去调用actionPerformed方法

        // 生成食物foodX和foodY坐标位置
        foodX = 25 + 25*random.nextInt(20);
        foodY = 25 + 25*random.nextInt(20);
    }

    // 重写画组件的方法
    @Override
    protected void paintComponent(Graphics g) { // 是一个画笔
        // 调用父类值的方法做一些基本工作
        super.paintComponent(g);
        // 设置背景颜色
        this.setBackground(Color.red);
        // 在画布中添加游戏区域
        g.fillRect(0, 0, 700, 800);
        // 在画布中添加右侧蛇头和身体
        /*right.paintIcon(this,g,100,100);
        body.paintIcon(this,g,75,100);
        body.paintIcon(this,g,50,100);*/

        // right.paintIcon(this,g,snakeX[0],snakeY[0]);
        // 通过枚举变量的值来判断现在蛇头的方向
        switch (direction) {
            case top:
                top.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            case bottom:
                bottom.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            case left:
                left.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            case right:
                right.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
        }
        for (int i = 1; i < len; i++) { // 上面已经定义了蛇头,此时就要从1开始定义蛇体
            body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }


        // 放上开始提示的信息,并设置字体和颜色
        if (!isStart) {
            g.setColor(Color.white);
            g.setFont(new Font("宋体", Font.BOLD, 50));
            g.drawString("请按空格键表示游戏开始", 50, 500);
        }

        // 添加食物
        food.paintIcon(this,g,foodX,foodY);
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    // 在此方法中编写逻辑
    @Override
    public void keyPressed(KeyEvent e) {// KeyEvent键盘事件
        int keyCode = e.getKeyCode();
        if (keyCode == 32) { // 空格键的值是32
            // 游戏状态取反(一定不能直接写true)
            // 写true,无论按下几次一直都是true(我们要的是按一下消失,按两下显示)
            isStart = !isStart;
            // 并重新画组件
            repaint();
        } else if (keyCode == KeyEvent.VK_UP) { // 改变蛇头的方向
            direction = Direction.top;
        } else if (keyCode == KeyEvent.VK_DOWN) {
            direction = Direction.bottom;
        } else if (keyCode == KeyEvent.VK_LEFT) {
            direction = Direction.left;
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            direction = Direction.right;
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    // 实现ActionListener重写的方法,在里面编写移动的逻辑
    @Override
    public void actionPerformed(ActionEvent e) {
        if (isStart) {
            // 移动身体(后一个往前移)
            for (int i = len - 1; i > 0; i--) {
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }
           /* // 假如蛇头水平向右移动,则当前蛇头向前+25
            snakeX[0] += 25;
            // 判断,当前蛇头的值超出700,则x值从0开始
            if (snakeX[0]>=700){
                snakeX[0]=0;
            }*/

            // 根据蛇头的方向进行移动
            switch (direction) {
                // 向上,x轴不变,y-25
                case top:
                    snakeY[0] -= 25;
                    if (snakeY[0] <= 0) {
                        snakeY[0] = 800;
                    }
                    break;
                // 向下移,x轴不变,y+25
                case bottom:
                    snakeY[0] += 25;
                    if (snakeY[0] >= 800) {
                        snakeY[0] = 0;
                    }
                    break;
                // 向左移,y轴不变,x-25
                case left:
                    snakeX[0] -= 25;
                    if (snakeX[0] <= 0) {
                        snakeX[0] = 700;
                    }
                    break;
                // 向右移,y轴不变,x+25
                case right:
                    snakeX[0] += 25;
                    if (snakeX[0] >= 700) {
                        snakeX[0] = 0;
                    }
                    break;
            }

            // 判断蛇头x和食物x的坐标是否一致,并且蛇头y和食物y坐标一致,表示吃到食物
            if (snakeX[0] == foodX && snakeY[0] == foodY){
                // 蛇的长度加1
                len++;
                // 在重新生成一个新的食物
                foodX = 25 + 25*random.nextInt(20);
                foodY = 25 + 25*random.nextInt(20);
            }

            // 重新画组件
            repaint();
            // 重新启动定时器
            timer.start(); // 100毫秒就调用一次方法
        }
    }
}

3. Direction class

package demo;
public enum Direction { // 上、下、左、右
    top,bottom,left,right;
}

book recommendation

Books in this issue: "Java Core Volume II", "Core Principles of Distributed Middleware and Best Practices of RocketMQ"

way of participation:

This time, 2 books will be given away (choose one of the two)! 
Activity time: until 2023-05-03 00:00:00.

Lottery draw method: Use the program to draw a lottery.

Participation method: follow the blogger (only for fan benefits), like, bookmark, random selection in the comment area, up to three comments!

One: "Java Core Volume II"

        Since the birth of Java 28 years ago, this world-renowned Java classic book "Core Java" has been accompanied by the growth of Java all the way, and has been favored by millions of Java developers. It has become a best-selling Java classic book and has influenced several generations of technology. people.

        The latest Chinese version of "Java Core Technology (12th Edition of the original book) has been fully revised to cover the new features of Java 17. The new version continues the fine tradition of the previous version, using hundreds of actual engineering cases to comprehensively and systematically explain the core concepts, syntax, important features, and development methods of the Java language.

        Focus on enabling readers to flexibly apply the advanced features provided by Java on the basis of fully understanding the Java language and Java class library, including object-oriented programming, reflection and proxy, interface and inner class, exception handling, generic programming, collection framework , the event listener model, GUI design, and concurrency.

The latest version of Core Java, Volume II, is now available

        The father of Java also said earlier that developers should abandon JDK 8 as soon as possible, and can choose the long-term support version of JDK 17. The 12th edition of the latest edition of "Core Java", which is comprehensively updated for the new features of Java 17, has caused a sensation since it was released in May last year. It has received high attention from tens of thousands of readers, and everyone left messages looking forward to the release of Volume II!

        For experienced programmers, if you want to write robust code for practical applications, "Java Core Technology" is definitely an industry-leading, concise book. Now, it's finally here! "Java Core Technology Volume II Advanced Features (12th Edition of the original book)" is now on the market, and all major channels are in stock.

        Volume II has been revised for new features and improvements in Java 17. As before, all chapters have been fully updated to remove obsolete content and discuss various new APIs in detail.

 How to choose version? 

Learn more: The latest version of Core Java Volume II, which has been looking forward to for a year, is finally on the market!

Jingdong self-operated purchase link : "Official website spot java core technology original book 12th edition volume 2 advanced features Kay Horstmann computer program development programming basics introductory tutorial book" [Abstract book review trial reading] - Jingdong Books

Two: "Core Principles of Distributed Middleware and Best Practices of RocketMQ"

        The core principle of distributed middleware and RocketMQ actual combat technology in one book: actual combat case + operation steps + execution effect diagram, teach you to understand the distributed middleware technology step by step, and easily realize the career transition from novice to expert!

        The core principle of distributed middleware and RocketMQ practical technology compulsory book!

brief introduction:

        This book starts from the basic concepts of distributed systems, gradually deepens the advanced practice of middleware in distributed systems, and finally explains with a large-scale project case, focusing on the process of using Spring Cloud framework to integrate various distributed components. It allows readers not only to systematically learn the relevant knowledge of distributed middleware, but also to have a deeper understanding of business logic analysis ideas and practical application development.

        The book is divided into 12 chapters, the first three chapters are the preparation stage for learning distributed system architecture. The opening part of Chapter 1 explains how distributed systems emerge during the evolution process; Chapter 2 Spring explains how to build the currently popular Spring Boot and Spring Cloud frameworks; Chapter 3 Containers explains the most popular Docker containers at present Technology and Kubernetes container orchestration tools; Chapters 4~8 explain in depth the relevant knowledge of message middleware RocketMQ, where theory and practice coexist; Chapter 9 will go deep into the bottom layer of RocketMQ, explore the fun of reading source code, and learn to read source code while mastering RocketMQ Methods; Chapters 10 and 11 explain the issues that must be considered in distributed systems: distributed transactions and distributed locks; Chapter 12 takes an e-commerce system business as an example, allowing readers to experience the process of a project from scratch , and apply what you have learned.

        The content of this book is from shallow to deep, with a clear structure, rich examples, easy to understand, and strong practicability. It is suitable for those who need to learn all-round technologies related to distributed middleware, and it is also suitable for training schools as training materials. Teaching reference books for relevant majors in colleges and universities.

Jingdong self-operated purchase link : "Core Principles of Distributed Middleware and Best Practices of RocketMQ" (Liu Meng) [Abstract Book Review Trial Reading]- Jingdong Books

Guess you like

Origin blog.csdn.net/m0_61933976/article/details/130372026