Java games - greedy snake

Greedy snake in Java games

System goals

Greedy snake is a puzzle game. Through the design and implementation of this game, you can improve your Java technology ability, enhance your independent development ability and master the development process of the project.

development environment

System environment: Windows
Development tools: Eclipse Mars, JDK 1.8

requirement analysis

Operation process requirements:
Within a certain range, generate a snake and randomly generate a food. When the snake eats the food, the snake's body becomes longer. The direction of the snake can be controlled by the direction key of the keyboard. When the snake's head encounters an obstacle or the snake's body, the game ends. It can also control the pause, continue, restart and end of the game.
Drawing requirements:
Draw a simple and clear snake and food. Among them, small snakes include: head, body and tail
Snake: a circular figure with the head filled with yellow and the body and tail filled with blue
Food: draw a square shape, and the food shall be filled with red color
Display requirements:
When the game starts, the snake moves all the time. When the snake eats the food, the body of the snake grows and the food disappears. The food is generated randomly again. The body of the snake is constantly refreshed with the movement of the snake.
Key control requirements:
Use the direction key (WASD or up, down, left and right) of the keyboard to move the snake. When the snake's head touches the border or the snake's body, the game is over.
Through the button to achieve the game pause, continue, restart, end the game.

Technology demand

Object oriented technology
GUI programming technology
Event handling mechanism
Multithreading technology
GUI programming:
Java provides three packages for GUI programming
Java.awt provides graphics, fill colors, and font styles
Javax.swing provides various components (forms, panels, buttons, text boxes, and so on)
Java.awt.event provides event handling mechanism
Multithreading technology:
The greedy snake's movement is continuous. As long as the greedy snake does not die, it can continue to move. Multi threading technology is needed here
Main thread
Keyboard listening thread
Status listening thread
Draw animation thread
Event handling mechanism:
Three elements of the event driven model:
Event Object: an effect produced by user interaction (mouse event, keyboard event, window event)
Event Source: the source of triggering events. Different event sources will trigger different event types
Event listener: responsible for listening to various events sent by event source
Procedure of event programming:
1) Write an event processing class (event listener)
2) Event processing class should implement listening interface KeyListener
3) Override event handling methods
4) Specify the event listener, register to listen (the responder of the event)

Code

Class Config
This class defines the size of the container row = 22, cols = 35, span (pixels per rectangle) = 20
It defines the top, bottom, left and right, the symbol of judging the survival of greedy Snake: isLive, and the symbol of judging the suspension and continuation of the game: isPause

//constant configuration
public class Config {
	public static final int ROWS = 22;//That's ok
	public static final int COLS = 35;//column
	public static final int SPAN = 20;// Pixels per rectangle
	
	public static final String U = "u";// Direction - up
	public static final String D = "d";// Direction down
	public static final String L = "l";// Direction left
	public static final String R = "r";// Direction right
	
	public static boolean isLive = true;// A sign of the survival of greedy snake
	
	public static boolean isPause = true;// Continue or pause the game
}

Class Button
Add pause, continu e, restart and exit buttons, register button monitoring, and define event response processing methods

//Button
public class Button extends JPanel implements ActionListener{
	MyPanel myPanel;
	JButton pause;// Pause game
	JButton continu;// Continue the game
	JButton restart;// Replay
	JButton exit;// Quit game
	public Button(MyPanel myPanel) {
		this.myPanel = myPanel;
		this.setBounds(0, 440, 706, 60);
		pause = new JButton("Pause game");
		continu = new JButton("Continue the game");
		restart = new JButton("Restart");
		exit = new JButton("Quit game");
		this.add(pause);
		this.add(continu);
		this.add(restart);
		this.add(exit);
		// Register button listening
		pause.addActionListener(this);
		continu.addActionListener(this);
		restart.addActionListener(this);
		exit.addActionListener(this);
		
	}
	
	// ActionEvent: get object of event action
	@Override
	public void actionPerformed(ActionEvent e) {
		// The listening object is to pause the game
		if (e.getSource() == pause) {
			Config.isPause = false;
		}
		// Listening object is to continue the game
		if (e.getSource() == continu) {
			Config.isPause = true;
			// Set keyboard monitor focus
			myPanel.setFocusable(true);
			myPanel.requestFocus();
		}
		// The listener is to restart the game
		if (e.getSource() == restart) {
			// 1. Stop the current thread
			myPanel.snakeThread.stopThread();
			// 2. Regenerate snakes and food
			Food food = new Food();
			myPanel.food = food;
			myPanel.snake = new Snake(food);
			// Restore control condition to initial state
			Config.isPause = true;
			Config.isLive = true;
			// 3. Create a new thread object (inner class object)
			SnakeThread snakeThread = myPanel.new SnakeThread();
			// 4. Start thread
			snakeThread.start();
			myPanel.snakeThread = snakeThread;
			// Get keyboard focus
			myPanel.setFocusable(true);
			myPanel.requestFocus();
		}
		// Listening object is to quit the game
		if (e.getSource() == exit) {
			System.exit(0);
		}
		
	}

}

Class Food
This class defines the methods of drawing food, randomly generating food position and obtaining food coordinates

// food
public class Food {
	// Place of action
	private int row;
	// List
	private int col;
	
	// constructor
	public Food() {
		repair();
	}
	
	// Draw food
	public void draw(Graphics g) {
		// Set brush color
		g.setColor(Color.RED);
		// Filled rectangle (x,y,width,height)
		g.fillRect(col*Config.SPAN, row*Config.SPAN, Config.SPAN, Config.SPAN);
	}
	// Location of randomly generated food
	public void repair() {
		// Value range 0-Config.ROWS
		row = new Random().nextInt(Config.ROWS);
		col = new Random().nextInt(Config.COLS);
	}
	
	// Get food coordinates
	public Rectangle getFoodRec() {
		return new Rectangle(col*Config.SPAN, row*Config.SPAN, Config.SPAN, Config.SPAN);
	}
}

Class Snake
This class defines methods such as drawing snakes, eating food, controlling the movement of snakes, etc
The link snake adopts the method of two-way linked list

// Snake
public class Snake {
	Node head;// Snake head
	Node body;// Snake body
	Node tail;// Snake tail
	Food food;// food
	// Initialize the starting position and the direction of the snake
	public Snake(Food food) {
		// Create snake head, snake body and snake tail nodes 
		head = new Node(7, 13, Config.R);
		body = new Node(7, 12, Config.R);
		tail = new Node(7, 11, Config.R);
		// Binding the relationship among the head, body and tail of a snake
		head.next = body;
		body.pre = head;
		body.next = tail;
		tail.pre = body;
		// Initialize food object
		this.food = food;
		
	}
	
	// Plotting snake
	public void draw(Graphics g) {
		// The snake has multiple nodes. It needs to take out each node and draw out each node
		for (Node n = head;n!=null;n = n.next) {
			// Call node drawing method
			n.draw(g);
		}
	}
	
	// Greedy snake move
	public void move() {
		// 1. Add snake head 2. Remove snake tail 3. Eat food 4. Death detection
		addHead();// Add snake head
		removeTail();// Remove snake tail
		deadCheck();// Death detection
	}
	
	// Add snake head
	public void addHead() {
		// Judging according to the direction of snake head
		Node node = null;
		switch (head.dir) {
		case Config.R:
			node = new Node(head.row,head.col+1, head.dir);
			break;
		case Config.L:
			node = new Node(head.row, head.col-1, head.dir);
			break;
		case Config.U:
			node = new Node(head.row-1, head.col, head.dir);
			break;
		case Config.D:
			node = new Node(head.row+1, head.col, head.dir);
			break;
		default:
			break;
		}
		
		// The relationship between binding node and snake head
		node.next = head;
		head.pre = node;
		head = node;// Assign a new snakehead node to the original snakehead
		
	}
	
	// Remove snake tail
	public void removeTail() {
		// 1. Set the snake tail to null, and the next pointer of the previous node of the snake tail is null
		tail.pre.next = null;
		// 2. Assign the last node of snake tail to snake tail
		tail = tail.pre;
	}
	
	// Control the movement direction of greedy snake
	public void keyControl(KeyEvent e) {
		// The moving direction of the snake head is modified by the judgment of the keyboard, so as to control the moving direction of the greedy snake
		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			if (head.dir.equals(Config.D)) {
				break;
			}
			head.dir = Config.U;
			break;
		case KeyEvent.VK_DOWN:
			if (head.dir.equals(Config.U)) {
				break;
			}
			head.dir = Config.D;
			break;
		case KeyEvent.VK_LEFT:
			if (head.dir.equals(Config.R)) {
				break;
			}
			head.dir = Config.L;
			break;
		case KeyEvent.VK_RIGHT:
			if (head.dir.equals(Config.L)) {
				break;
			}
			head.dir = Config.R;
			break;
		
		}
	}
	
	/*
	 *  Eat food
	 *  1.Judge whether the coordinates of greedy snake's head and food coincide
	 *  2.Regenerate a new greedy snake
	 *  3.Regenerate food at random
	 */
	public void eat() {
		// Judge whether the two rectangles intersect (whether the snake head touches the food)
		Rectangle a = getHeadRec();
		Rectangle b = food.getFoodRec();
		if (a.intersects(b)) {
			addHead();// Add snake head
			food.repair();// Randomly generated food
		}
		
	}
	
	//Get snake head coordinates
	public Rectangle getHeadRec() {
		// Get rectangle coordinates of snake head
		return new Rectangle(head.col*Config.SPAN, head.row*Config.SPAN, Config.SPAN, Config.SPAN);
	}
	
	// Check if the snake is dead
	public void deadCheck() {
		// 1. Snakehead meets the boundary
		// Range of rows: 0-Config.ROWS-1
		// Column range: 0-Config.COLS-1
		if (head.row<0||head.col<0||head.row>Config.ROWS-1||head.col>Config.COLS-1) {
			// Change the state of greedy snake to death
			Config.isLive = false;
		}
		// 2. The head of the snake cannot touch the body
		// Traverse the snake body to determine whether each node of the snake body coincides with the snake head
		for (Node n = head.next; n!=null; n = n.next) {
			// Determine whether the position of the snake head is the same as that of the current snake body node
			if (head.row == n.row && head.col == n.col) {
				Config.isLive = false;
				break;
			}
		}
	}
}

Class Node
This class draws the segmentation node of greedy snake, judges the snake head and draws the snake head into yellow and the snake body into blue

// Greedy snake split nodes
public class Node {
	int row;// That's ok
	int col;// column
	Node next;// Next node pointer
	Node pre;// Previous node pointer
	String dir;// The direction of the snake
	
	// Constructor: initializing the position information of greedy snake and making the forward direction of greedy snake
	public Node(int row,int col,String dir) {
		this.row = row;
		this.col = col;
		this.dir = dir;
	}
	
	// Draw nodes
	public void draw(Graphics g) {
		// If the previous node of the current node is null, the current node is the snake head
		if (this.pre == null) {
			// Draw the color of the snake's head as yellow
			g.setColor(Color.YELLOW);
		}else {
			g.setColor(Color.BLUE);
		}
		g.fillOval(col*Config.SPAN, row*Config.SPAN, Config.SPAN, Config.SPAN);

	}
}

Class Mypanel
In the Mypanel class, we define the container size background and start multithreading. In each drawing container, we call snake.move, food.draw, snake.draw and snake.eat methods to judge the survival of the snake in the snake thread, judge whether the game continues or pause, and instantiate the method in KeyListener.

public class MyPanel extends JPanel implements KeyListener{
	
	// Create food object
	Food food = new Food();
	// Create a greedy snake object
	Snake snake = new Snake(food);
	// Create thread object
	SnakeThread snakeThread = new SnakeThread();
	
	public MyPanel() {
		// Set container coordinates and size
		this.setBounds(0, 0, 700, 440);
		// Set container background color
		this.setBackground(Color.PINK);
		// Startup thread
		snakeThread.start();
		// Register keyboard monitor
		this.addKeyListener(this);
	}
	
	// Drawing container
	@Override
	public void paint(Graphics g) {
		super.paint(g);
		// Set the color of the paint
		g.setColor(Color.GRAY);
		// Draw horizontal line
		for (int i = 0; i < Config.ROWS; i++) {
			// Draw a line between points (x1, y1) and (x2, y2) using the current color
			g.drawLine(0, Config.SPAN * i, Config.COLS * Config.SPAN, Config.SPAN * i);
		}
		// Draw vertical lines
		for (int i = 0; i < Config.COLS; i++) {
			g.drawLine(Config.SPAN * i, 0 , Config.SPAN * i , Config.ROWS * Config.SPAN);
		}
		// Greedy snake move
		snake.move();
		// Painting food
		food.draw(g);
		// Painting snake
		snake.draw(g);
		// Eat food
		snake.eat();
	}
	// Snake eating thread
	class SnakeThread extends Thread{
		boolean flag = true;// Restart
		@Override
		public void run() {
			// Config.isLive: judge whether the greedy snake survives
			while (Config.isLive && flag) {
				try {
					// When the snake is not dead, move on
					Thread.sleep(300);// Current thread sleep for 0.3 seconds
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// Config.isPause== true: to continue the game
				// Config.isPause== false: for game suspension
				if (Config.isLive && Config.isPause) {
					// Redraws the graph, with the effect of refreshing the page
					// Repaint() calls awt thread -- > update() method -- > paint()
					repaint();
				}
				if (!Config.isLive) {
					// Pop up a dialog box to end the game
					JOptionPane.showMessageDialog(MyPanel.this, "Game over");
				}
			}
		}
		// How to stop a thread
		public void stopThread() {
			flag = false;
		}
	}
	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// Call the control direction method of greedy snake
		snake.keyControl(e);
	}

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

}

Class MyFrame
The Myframe class defines the form information, and the main function calls the entire method

// JFrame graphical interface design container
public class MyFrame extends JFrame {
	MyPanel myPanel = new MyPanel();
	Button button = new Button(myPanel);
	
	public MyFrame() {
		// Set form title
		this.setTitle("Snake v1.0");
		// Set the initial position and size of the form
		this.setBounds(300, 50, 706, 500);
		// Set to ensure that the JVM exits when the window is closed
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// Set layout manager to null empty layout
		this.setLayout(null);
		// Set whether this form can be resized by the user
		this.setResizable(false);
		// add controls
		this.add(myPanel);
		// Set keyboard monitor focus
		// Set whether to allow focus
		myPanel.setFocusable(true);
		// Focus of acquisition
		myPanel.requestFocus();
		// add button
		this.add(button);
		// display
		this.setVisible(true);
	}
	
	public static void main(String[] args) {
		new MyFrame();
	}
}

The packages to be imported in the whole program include swing, awt, etc

Operation effect display

Published 19 original articles, won praise 8, visited 8887
Private letter follow

Tags: Java Programming Windows Eclipse

Posted on Mon, 27 Jan 2020 00:02:59 -0800 by phpian