Author’s brief introduction
CSDN blog expert, engaged in software development for many years, proficient in Java, JavaScript, the blogger is also from scratch to learn and grow step by step, know the importance of learning and accumulation, like to fight with the majority of ADC upgrade, welcome your attention, look forward to learning, growth and take off with you!
Series directory
1. Java Tetris 2. Java backgammon 3. Old Java programmer spent a day writing a plane war 4. The old Java programmer spent 2 days to write a continuous look 6. Java elimination fun (love elimination every day) 7. Java snake small game 8
rendering
Implementation approach
1. Create a run window.
2. Create a menu. 3. Draw each unit of the maze. 4. Calculate the maze path through the algorithm and get through the path to form the maze. 5. Draw the start and end points. 6. Add keyboard events to control the starting block movement. 7. End.
Maze algorithm (online reference)
- Mark the starting point as the current maze unit and as visited
- Loop when there are still unmarked maze units
1). If the current maze unit has adjacent maze units that have not been visited (1). Randomly select an adjacent unvisited maze unit (2). Stack the current maze unit (3). Remove the wall of the current and adjacent maze cells (4). Mark the adjacent maze cell and use it as the current maze cell 2). If the current maze unit has no adjacent unvisited maze units and the stack is not empty (1). The maze element at the top of the stack is removed from stack (2). Make it the current maze element
This algorithm is called “depth-first”. In simple terms, it is to start from the starting point, look for its four neighbors, and then randomly walk one of them. When it is unable to walk, it will go back to the previous step and continue to walk until all the units are gone.
Related illustration
- The wall of each unit is divided into the upper wall, the right wall, the lower wall and the left wall. The wall is represented by an array of length 4. If the value of the element is true, the wall exists; otherwise, the wall does not exist.
2. The cell is created according to the rows and columns, which will use a double loop, similar to the table. For example, the second row is 1 if expressed by I, and the third column is 2 if expressed by j, then the elements of the second row and the third column together will be (1,2).
3. Similarly, its upper neighbor is (0,2), its right neighbor is (1,3), its lower neighbor is (2,2), and its left neighbor is (1,1). That is, its upper neighbor is I minus plus 1, and its left neighbor is j minus plus 1.
4. The coordinates of the 4 points of the square are (x1,y1)(x2,y2)(x3,y3)(x4,y4) respectively, and the formula for calculating the coordinates is (where start is the relative offset, in order to make some gaps on both sides of the maze) :
// I for row j for column h for cell height
// Top left coordinates
this.x1=start+j*h;
this.y1=start+i*h;
// Upper right corner coordinates
this.x2=start+(j+1)*h;
this.y2=start+i*h;
// Lower right corner coordinates
this.x3=start+(j+1)*h;
this.y3=start+(i+1)*h;
// Lower left corner coordinates
this.x4=start+j*h;
this.y4=start+(i+1)*h;
Copy the code
Calculate the coordinates. If the width and height of each square are 40, then the coordinates of the unit (1,2) are shown as follows:
[true,true,true,true] = [true,true,true] = [true,true,true]
If the array is [false,true,true,true], then the graph is:
6. What should I do if I want to connect my neighbor on the right? The current cell removes the right wall, and the right cell removes the left wall, thus connecting.
That’s what happens when you remove it, and so on
Code implementation
Create a window
First create a game form class GameFrame, inherit to JFrame, used to display on the screen (window object), each game has a window, set the window title, size, layout, etc.
import javax.swing.JFrame;
/** * form class */
public class GameFrame extends JFrame {
// constructor
public GameFrame(a){
setTitle("Maze");// Set the title
setSize(420.470);// Set the window size
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// The process exits after the shutdown
setLocationRelativeTo(null);/ / in the middle
setResizable(false);// Not allowed to be larger
//setVisible(true); // Sets the display form}}Copy the code
Create the panel container GamePanel to inherit from JPanel
import javax.swing.JMenuBar;
import javax.swing.JPanel;
/* * Canvas class */
public class GamePanel extends JPanel{
private JMenuBar jmb = null;
private GameFrame mainFrame = null;
private GamePanel panel = null;
private String gameFlag="start";// Game state
// constructor
public GamePanel(GameFrame mainFrame){
this.setLayout(null);
this.setOpaque(false);
this.mainFrame=mainFrame;
this.panel =this; }}Copy the code
Create a Main class to launch the window.
/ / the Main class
public class Main {
public static void main(String[] args) {
GameFrame frame = new GameFrame();
GamePanel panel = new GamePanel(frame);
frame.add(panel);
frame.setVisible(true); }}Copy the code
Right click to execute the Main class and the window is created
Create menus and menu options
Create a menu
private Font createFont(a){
return new Font("Song Of song origin",Font.BOLD,18);
}
// Create a menu
private void createMenu(a) {
/ / create a JMenuBar
jmb = new JMenuBar();
// get the font
Font tFont = createFont();
// Create game options
JMenu jMenu1 = new JMenu("Game");
jMenu1.setFont(tFont);
// Create help options
JMenu jMenu2 = new JMenu("Help");
jMenu2.setFont(tFont);
JMenuItem jmi1 = new JMenuItem("New Game");
jmi1.setFont(tFont);
JMenuItem jmi2 = new JMenuItem("Quit");
jmi2.setFont(tFont);
//jmi1 jmi2 added to the menu item "Games"
jMenu1.add(jmi1);
jMenu1.add(jmi2);
JMenuItem jmi3 = new JMenuItem("Operation Help");
jmi3.setFont(tFont);
JMenuItem jmi4 = new JMenuItem("Victory condition");
jmi4.setFont(tFont);
//jmi13 jmi4 added to the menu item "Games"
jMenu2.add(jmi3);
jMenu2.add(jmi4);
jmb.add(jMenu1);
jmb.add(jMenu2);
mainFrame.setJMenuBar(jmb);
// Add a listener
jmi1.addActionListener(this);
jmi2.addActionListener(this);
jmi3.addActionListener(this);
jmi4.addActionListener(this);
// Set directive
jmi1.setActionCommand("restart");
jmi2.setActionCommand("exit");
jmi3.setActionCommand("help");
jmi4.setActionCommand("win");
}
Copy the code
Implement ActionListener override method actionPerformed GamePanel is reporting an error, so you need to override the actionPerformed method.== Implementation of actionPerformed method ==
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
System.out.println(command);
UIManager.put("OptionPane.buttonFont".new FontUIResource(new Font("Song Of song origin", Font.ITALIC, 18)));
UIManager.put("OptionPane.messageFont".new FontUIResource(new Font("Song Of song origin", Font.ITALIC, 18)));
if ("exit".equals(command)) {
Object[] options = { "Sure"."Cancel" };
int response = JOptionPane.showOptionDialog(this."Are you sure you want to quit?"."",
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[0]);
if (response == 0) {
System.exit(0); }}else if("restart".equals(command)){
restart();
}else if("help".equals(command)){
JOptionPane.showMessageDialog(null."Move by keyboard up, down, left and right."."Hint!, JOptionPane.INFORMATION_MESSAGE);
}else if("win".equals(command)){
JOptionPane.showMessageDialog(null."Move to the finish line to win."."Hint!, JOptionPane.INFORMATION_MESSAGE); }}Copy the code
The GamePanel code is as follows:
package main;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.plaf.FontUIResource;
/* * Canvas class */
public class GamePanel extends JPanel implements ActionListener{
private JMenuBar jmb = null;
private GameFrame mainFrame = null;
private GamePanel panel = null;
private String gameFlag="start";// Game state
// constructor
public GamePanel(GameFrame mainFrame){
this.setLayout(null);
this.setOpaque(false);
this.mainFrame=mainFrame;
this.panel =this;
// Create a menu
createMenu();
}
private Font createFont(a){
return new Font("Song Of song origin",Font.BOLD,18);
}
// Create a menu
private void createMenu(a) {
/ / create a JMenuBar
jmb = new JMenuBar();
// get the font
Font tFont = createFont();
// Create game options
JMenu jMenu1 = new JMenu("Game");
jMenu1.setFont(tFont);
// Create help options
JMenu jMenu2 = new JMenu("Help");
jMenu2.setFont(tFont);
JMenuItem jmi1 = new JMenuItem("New Game");
jmi1.setFont(tFont);
JMenuItem jmi2 = new JMenuItem("Quit");
jmi2.setFont(tFont);
//jmi1 jmi2 added to the menu item "Games"
jMenu1.add(jmi1);
jMenu1.add(jmi2);
JMenuItem jmi3 = new JMenuItem("Operation Help");
jmi3.setFont(tFont);
JMenuItem jmi4 = new JMenuItem("Victory condition");
jmi4.setFont(tFont);
//jmi13 jmi4 added to the menu item "Games"
jMenu2.add(jmi3);
jMenu2.add(jmi4);
jmb.add(jMenu1);
jmb.add(jMenu2);
mainFrame.setJMenuBar(jmb);
// Add a listener
jmi1.addActionListener(this);
jmi2.addActionListener(this);
jmi3.addActionListener(this);
jmi4.addActionListener(this);
// Set directive
jmi1.setActionCommand("restart");
jmi2.setActionCommand("exit");
jmi3.setActionCommand("help");
jmi4.setActionCommand("win");
}
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
System.out.println(command);
UIManager.put("OptionPane.buttonFont".new FontUIResource(new Font("Song Of song origin", Font.ITALIC, 18)));
UIManager.put("OptionPane.messageFont".new FontUIResource(new Font("Song Of song origin", Font.ITALIC, 18)));
if ("exit".equals(command)) {
Object[] options = { "Sure"."Cancel" };
int response = JOptionPane.showOptionDialog(this."Are you sure you want to quit?"."",
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[0]);
if (response == 0) {
System.exit(0); }}else if("restart".equals(command)){
restart();
}else if("help".equals(command)){
JOptionPane.showMessageDialog(null."Move by keyboard up, down, left and right."."Hint!, JOptionPane.INFORMATION_MESSAGE);
}else if("win".equals(command)){
JOptionPane.showMessageDialog(null."Move to the finish line to win."."Hint!, JOptionPane.INFORMATION_MESSAGE); }}void restart(a){}}Copy the code
Run it
Draw each unit of the maze
- Example Initialize related parameters
public final int ROWS=20;/ / line
public final int COLS=20;/ / column
public final int H=20;// The width and height of each piece
Block[][] blocks = null;// Store a two-dimensional array for each cell
Copy the code
- Create a maze unit class.
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
/* * Maze unit class */
public class Block {
private GamePanel panel = null;
private int i=0;// The subscript I of the two-dimensional array
private int j=0;// The subscript j of the two-dimensional array
private int h=0;/ wide/high
private int start=6;// Offset pixels
//4 vertex coordinates
private int x1=0;/ / the x1 coordinates
private int y1=0;/ / y1 coordinates
private int x2=0;/ / x2 coordinates
private int y2=0;/ / y2 coordinates
private int x3=0;/ / x3 coordinates
private int y3=0;/ / y3 coordinates
private int x4=0;/ / x4 coordinates
private int y4=0;/ / y4 coordinates
// Whether to display the top, bottom, and left walls. True: display, false: hide
boolean[] walls=new boolean[4];
private boolean visited=false;// Whether to access
/ / structure
public Block(int i,int j,int h,GamePanel panel){
this.i=i;
this.j=j;
this.h=h;
this.panel=panel;
// Compute the coordinates of the four vertices
init();
}
// Compute the coordinates of the four vertices
private void init(a) {
// I stands for row and j stands for column
// Top left coordinates
this.x1=start+j*h;
this.y1=start+i*h;
// Upper right corner coordinates
this.x2=start+(j+1)*h;
this.y2=start+i*h;
// Lower right corner coordinates
this.x3=start+(j+1)*h;
this.y3=start+(i+1)*h;
// Lower left corner coordinates
this.x4=start+j*h;
this.y4=start+(i+1)*h;
// By default, all four walls are displayed
walls[0] =true;
walls[1] =true;
walls[2] =true;
walls[3] =true;
}
// The method to draw the indicator
public void draw(Graphics g) {
// Draw the maze block
drawBlock(g);
}
// Draw the maze block
private void drawBlock(Graphics g) {
// Check the top, right, bottom and left walls, if true the wall will have one, otherwise the wall will not have one
boolean top = walls[0];
boolean right = walls[1];
boolean bottom = walls[2];
boolean left = walls[3];
if(top){// Draw on the wall
g.drawLine(x1, y1, x2, y2);
}
if(right){// Draw the right wall
g.drawLine(x2, y2, x3, y3);
}
if(bottom){// Draw the next wall
g.drawLine(x3, y3, x4, y4);
}
if(left){// Draw the left wallg.drawLine(x4, y4, x1, y1); }}}Copy the code
- Create method createBlocks in the GamePanel class
// Create the array contents
private void createBlocks(a) {
blocks = new Block[ROWS][COLS];
Block block ;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
block = new Block(i, j,H,this); blocks[i][j]=block; }}}Copy the code
- Call this method in the constructor
// constructor
public GamePanel(GameFrame mainFrame){
this.setLayout(null);
this.setOpaque(false);
this.mainFrame=mainFrame;
this.panel =this;
// Create a menu
createMenu();
// Create the array contents
createBlocks();
}
Copy the code
- Re-paint the method in GamePanel to draw these squares
public void paint(Graphics g) {
super.paint(g);
// Draw the grid
drawBlock(g);
}
// Draw the maze block
private void drawBlock(Graphics g) {
Block block ;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
block = blocks[i][j];
if(block! =null){ block.draw(g); }}}}Copy the code
Run and you can see the squares drawn
Calculate and solve mazes
- Add neighbor lookup methods to each cell (Block class)
// Find if the current unit has any unaccessed neighbor units
public List<Block> findNeighbors(a) {
// Neighbors are divided into upper, lower, and left
List<Block> res= new ArrayList<Block>();// The array returned
Block top = this.getNeighbor(0.false);
Block right = this.getNeighbor(1.false);
Block bottom = this.getNeighbor(2.false);
Block left = this.getNeighbor(3.false);
if(top! =null){
res.add(top);
}
if(right! =null){
res.add(right);
}
if(bottom! =null){
res.add(bottom);
}
if(left! =null){
res.add(left);
}
return res;// Return an array of neighbors
}
// Get the neighbor according to the direction
public Block getNeighbor(int type,boolean lose_visited) {
Block neighbor;
int ti=0,tj=0;
if(type==0){
ti = this.i-1;
tj = this.j;
}else if(type==1){
ti = this.i;
tj = this.j+1;
}else if(type==2){
ti = this.i+1;
tj = this.j;
}else if(type==3){
ti = this.i;
tj = this.j-1;
}
Block[][] blocks = panel.blocks;
if(ti<0 || tj<0 || ti>=panel.ROWS || tj>=panel.COLS){// Out of bounds
neighbor = null;
}else{
// Find the neighbor first
neighbor = blocks[ti][tj];
// Check whether it is accessed, and return null if it is
if(neighbor.visited && ! lose_visited){//lose_visited equals true to ignore the visit
neighbor = null; }}return neighbor;
}
Copy the code
- To calculate
The only thing to notice about the code that follows the algorithm is that I set a value, unVisitedCount, which starts with the number of all cells. Each time a cell is marked as visited, this value decreases by 1, and when it reaches 0, the loop terminates, ending the algorithm.
// Line calculation processing
private void computed(a){
/* 1. Take the starting point as the current maze unit and mark it as visited. 2. If the current maze unit has adjacent maze units (1) that have not been visited, randomly select an adjacent maze unit (2) that has not been visited. Stack the current maze unit (3). Remove the wall between the current maze unit and the adjacent maze unit (4). Mark a neighboring maze unit and use it as the current maze unit 2). If the current maze unit has no adjacent maze units that are not accessed and the stack is not empty (1). The maze unit at the top of the stack is removed from the stack (2). Make it the current maze unit */
Random random = new Random();
Stack<Block> stack = new Stack<Block>();/ / stack
Block current = blocks[0] [0];// take the first unit as the current unit
current.setVisited(true);// Mark as visited
int unVisitedCount=ROWS*COLS-1;// Because the first one is already set to access
List<Block> neighbors ;// Define a neighbor
Block next;
while(unVisitedCount>0){
neighbors = current.findNeighbors();// Find the neighbor set (not accessed)
if(neighbors.size()>0) {// If the current maze unit has adjacent maze units that have not been accessed
// Randomly select an adjacent maze unit that is not accessed
int index = random.nextInt(neighbors.size());
next = neighbors.get(index);
// Push the current maze unit onto the stack
stack.push(current);
// Remove the walls of the current and adjacent maze cells
this.removeWall(current,next);
// Mark an adjacent maze unit and use it as the current maze unit
next.setVisited(true);
// mark one as access, then decrement the counter by 1
unVisitedCount--;/ / decline
current = next;
}else if(! stack.isEmpty()){// If the current maze unit has no adjacent unaccessed maze units and the stack is not empty
/* 1. The maze unit at the top of the stack is removed from the stackBlock cell = stack.pop(); current = cell; }}}Copy the code
- Remove the wall
// Remove the walls of the current and adjacent maze cells
private void removeWall(Block current, Block next) {
if(current.getI()==next.getI()){// Horizontal neighbor
if(current.getJ()>next.getJ()){// The left neighbor is matched
// Remove your own left wall and your neighbor's right wall
current.walls[3] =false;
next.walls[1] =false;
}else{// The neighbor on the right is matched
// Remove your own right wall and your neighbor's left wall
current.walls[1] =false;
next.walls[3] =false; }}else if(current.getJ()==next.getJ()){// Vertical neighbor
if(current.getI()>next.getI()){// The upper neighbor is matched
// For upper neighbors, remove your own upper wall and your neighbor's lower wall
current.walls[0] =false;
next.walls[2] =false;
}else{// The lower neighbor is matched
// Remove your own lower wall and your neighbor's upper wall
current.walls[2] =false;
next.walls[0] =false; }}}Copy the code
- Call the computed method in the constructor
// constructor
public GamePanel(GameFrame mainFrame){
this.setLayout(null);
this.setOpaque(false);
this.mainFrame=mainFrame;
this.panel =this;
// Create a menu
createMenu();
// Create the array contents
createBlocks();
// Calculate the processing line
computed();
}
Copy the code
- Running effect
Draw the starting point and ending point
- Create the Rect class
package main;
import java.awt.Color;
import java.awt.Graphics;
// Start end block
public class Rect {
private int i=0;// The subscript I of the two-dimensional array
private int j=0;// The subscript j of the two-dimensional array
private int x=0;/ / x coordinate
private int y=0;/ / y
private int h=0;/ wide/high
private int start=6;// Offset pixels
private String type="";//start end
public Rect(int i,int j,int h,String type){
this.i=i;
this.j=j;
this.h=h;
this.type=type;
}
/ / initialization
private void init(a) {
this.x=start+j*h+2;
this.y=start+i*h+2;
}
/ / to draw
void draw(Graphics g){
// Calculate x and y coordinates
init();
Color oColor = g.getColor();
if("start".equals(type)){/ / red
g.setColor(Color.red);
}else{
g.setColor(Color.blue);
}
g.fillRect(x, y, h-3, h-3);
g.setColor(oColor);
}
/ / move
public void move(int type, Block[][] blocks,GamePanel panel) {
// Get the corresponding maze unit based on the current start square
Block cur = blocks[this.i][this.j];
boolean wall = cur.walls[type];// Get the corresponding wall
if(! wall){// Get the cell corresponding to the moving block
Block next = cur.getNeighbor(type,true);
if(next! =null) {this.i = next.getI();
this.j = next.getJ();
panel.repaint();
// If I,j is equal to the end point, then success is achieved
if(this.i==panel.end.i && this.j==panel.end.j){ panel.gameWin(); }}}}public int getI(a) {
return i;
}
public void setI(int i) {
this.i = i;
}
public int getJ(a) {
return j;
}
public void setJ(int j) {
this.j = j; }}Copy the code
- Create a method in the GamePanel class and call it in the construct.
// Create a square that starts and ends
private void createRects(a) {
start = new Rect(0.0, H, "start"); end =new Rect(ROWS-1, COLS-1, H, "end"); }Copy the code
// constructor
public GamePanel(GameFrame mainFrame){
this.setLayout(null);
this.setOpaque(false);
this.mainFrame=mainFrame;
this.panel =this;
// Create a menu
createMenu();
// Create the array contents
createBlocks();
// Calculate the processing line
computed();
// Create a square that starts and ends
createRects();
}
Copy the code
- Draw in the paint method
@Override
public void paint(Graphics g) {
super.paint(g);
// Draw the grid
drawBlock(g);
// Draw the start and end directions
drawRect(g);
}
// Draw the start and end squares
private void drawRect(Graphics g) {
end.draw(g);
start.draw(g);
}
Copy the code
- Run the
Add keyboard movement monitor
- Create a listener method
// Add keyboard listener
private void createKeyListener(a) {
KeyAdapter l = new KeyAdapter() {
/ / press
@Override
public void keyPressed(KeyEvent e) {
if(!"start".equals(gameFlag)) return ;
int key = e.getKeyCode();
switch (key) {
/ / up
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
if(start! =null) start.move(0,blocks,panel);
break;
/ / to the right
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
if(start! =null) start.move(1,blocks,panel);
break;
/ / down
case KeyEvent.VK_DOWN:
case KeyEvent.VK_S:
if(start! =null) start.move(2,blocks,panel);
break;
/ / to the left
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
if(start! =null) start.move(3,blocks,panel);
break; }}/ / to loosen
@Override
public void keyReleased(KeyEvent e) {}};// Add keyboard listener to main frame
mainFrame.addKeyListener(l);
}
Copy the code
- Called in a construct
// constructor
public GamePanel(GameFrame mainFrame){
this.setLayout(null);
this.setOpaque(false);
this.mainFrame=mainFrame;
this.panel =this;
// Create a menu
createMenu();
// Create the array contents
createBlocks();
// Calculate the processing line
computed();
// Create a square that starts and ends
createRects();
// Add keyboard event listener
createKeyListener();
}
Copy the code
- run
finishing
At this point the code is almost complete, add the game victory, restart, etc
// Start over
void restart(a) {
/* Parameter reset 1. Game state 2. Maze unit reset 3
//1. Game state
gameFlag="start";
//2. Maze unit reset
Block block ;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
block = blocks[i][j];
if(block! =null){
block.setVisited(false);
block.walls[0] =true;
block.walls[1] =true;
block.walls[2] =true;
block.walls[3] =true; }}}//3
computed();
// Start the box to zero
start.setI(0);
start.setJ(0);
/ / redraw
repaint();
}
Copy the code
To see the big guy here, move the rich little hands to praise + reply + collection, can [concern] a wave of better.
Code acquisition method:
Help article [praise] + [collection] + [concern] + [comment], plus V: QQ283582761, I send you!
More wonderful
1. Java Tetris 2. Java backgammon 3. Old Java programmer spent a day writing a plane war 4. The old Java programmer spent 2 days to write a continuous look 6. Java elimination fun (love elimination every day) 7. Java snake small game 8
reading
1. JavaWeb library management system 2. JavaWeb Student dormitory management system 3