您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Java如何實(shí)現(xiàn)經(jīng)典游戲復(fù)雜迷宮”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Java如何實(shí)現(xiàn)經(jīng)典游戲復(fù)雜迷宮”這篇文章吧。
人類建造迷宮已有5000年的歷史。在世界的不同文化發(fā)展時(shí)期,這些奇特的建筑物始終吸引人們沿著彎彎曲曲、困難重重的小路吃力地行走,尋找真相。迷宮類小游戲應(yīng)運(yùn)而生。在游戲中,迷宮被表現(xiàn)為冒險(xiǎn)舞臺(tái)里,藏有各式各樣奇妙與謎題或?qū)毑氐奈kU(xiǎn)區(qū)域。型態(tài)有洞窟、人工建筑物、怪物巢穴、密林或山路等。迷宮內(nèi)有惡徒或兇猛的生物(真實(shí)存在或想像物體都有)徘徊,其中可能會(huì)有陷阱、不明設(shè)施、遺跡等。
《復(fù)雜迷宮》游戲是用java語(yǔ)言實(shí)現(xiàn),采用了swing技術(shù)進(jìn)行了界面化處理,設(shè)計(jì)思路用了面向?qū)ο笏枷搿?/p>
主要需求
方向鍵控制移動(dòng),角色走出迷宮,游戲勝利。增加游戲難度和增加隨機(jī)地圖。
1、構(gòu)建游戲地圖面板
2、設(shè)定迷宮地圖,包含可走的通道,不可走的墻體,還有出口位置
3、鍵盤的上下左右按鍵,來(lái)控制角色的移動(dòng)
4、角色移動(dòng)的算法,通道可走,遇到墻體不可走
5、走到終點(diǎn),有成功通關(guān)的提示。
6、增加游戲的難度選擇,難度1,難度2和難度3
7、每次生成的地圖是隨機(jī)的
8、地圖大小可選擇,迷宮的長(zhǎng)在10-45之間,寬在10-90之間
9、增加撞墻的音樂(lè)效果
游戲開始頁(yè)面
生成難度1,10*10的迷宮地圖
隨機(jī)地圖:生成難度1,10*10的迷宮地圖
生成難度2,30*30的迷宮地圖
生成難度3,90*45的迷宮地圖
成功過(guò)關(guān)-效果
窗口布局
public class StartView extends JFrame { public StartView() { this.setTitle("復(fù)雜迷宮"); this.setSize(240, 265); this.setLocationRelativeTo(null); this.setDefaultCloseOperation(EXIT_ON_CLOSE); this.setResizable(false); initialize(); this.setVisible(true); } private void initialize() { JPanel contentPane = new JPanel(); this.setContentPane(contentPane); contentPane.setLayout(null); JLabel widthLabel = new JLabel("迷宮長(zhǎng)度:"); JLabel heightLabel = new JLabel("迷宮高度:"); JLabel levelLabel = new JLabel("難度:"); JTextField widthText = new JTextField(); JTextField heightText = new JTextField(); JRadioButton level1 = new JRadioButton("1"); JRadioButton level2 = new JRadioButton("2"); JRadioButton level3 = new JRadioButton("3"); ButtonGroup levelGroup = new ButtonGroup(); levelGroup.add(level1); levelGroup.add(level2); levelGroup.add(level3); JButton run = new JButton("生成迷宮"); // 設(shè)定標(biāo)簽位置 widthLabel.setBounds(20, 20, 100, 30); heightLabel.setBounds(20, 70, 110, 30); widthText.setBounds(120, 20, 70, 30); heightText.setBounds(120, 70, 70, 30); levelLabel.setBounds(20, 120, 60, 30); level1.setBounds(80, 120, 50, 30); level2.setBounds(130, 120, 50, 30); level3.setBounds(180, 120, 50, 30); run.setBounds(55, 170, 120, 30); // 限制輸入框只接收數(shù)字 widthText.setDocument(new NumberTextField()); heightText.setDocument(new NumberTextField()); // 改變字體 Font font = new Font("楷體", Font.PLAIN, 17); widthLabel.setFont(font); heightLabel.setFont(font); widthText.setFont(font); heightText.setFont(font); levelLabel.setFont(font); level1.setFont(font); level2.setFont(font); level3.setFont(font); run.setFont(font); // 取消按鈕選中邊框 level1.setFocusPainted(false); level2.setFocusPainted(false); level3.setFocusPainted(false); // 默認(rèn)選擇難度3 level3.setSelected(true); contentPane.add(widthLabel); contentPane.add(heightLabel); contentPane.add(widthText); contentPane.add(heightText); contentPane.add(levelLabel); contentPane.add(level1); contentPane.add(level2); contentPane.add(level3); contentPane.add(run); // 生成迷宮監(jiān)聽器 run.addActionListener(e -> { // 建議寬在10-90,長(zhǎng)在10-45之間 if (widthText.getText().equals("")) { JOptionPane.showMessageDialog(null, "長(zhǎng)度不能為空!", "提示", JOptionPane.INFORMATION_MESSAGE); } else if (heightText.getText().equals("")) { JOptionPane.showMessageDialog(null, "高度不能為空!", "提示", JOptionPane.INFORMATION_MESSAGE); } else { int width = Integer.parseInt(widthText.getText()); int height = Integer.parseInt(heightText.getText()); if (width >= 10 && width <= 90 && height >= 10 && height <= 45) { int level = level1.isSelected() ? 1 : level2.isSelected() ? 2 : 3; MazeModel maze = new MazeModel(width, height, level); this.dispose(); maze.draw(); } else { JOptionPane.showMessageDialog(null, "迷宮的長(zhǎng)必須在10-45之間,寬必須在10-90之間,請(qǐng)檢查輸入是否有誤!", "錯(cuò)誤輸入", JOptionPane.ERROR_MESSAGE); } } }); // 添加回車鍵入監(jiān)聽器 KeyAdapter enterAdapter = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { run.doClick(); // 回車即生成迷宮 } } }; widthText.addKeyListener(enterAdapter); heightText.addKeyListener(enterAdapter); } public static void main(String[] args) { new StartView(); } }
迷宮的數(shù)學(xué)模型
public class MazeModel { private int width; private int height; private ArrayList<MazePoint> mazePoints; /** * 迷宮的構(gòu)造方法 * * @param width 迷宮的寬度 * @param height 迷宮的 * @param level 1 -> 遞歸分割算法生成迷宮,2 -> 遞歸回溯算法生成迷宮,3 -> 普里姆算法生成迷宮 */ public MazeModel(int width, int height, int level) { super(); this.width = width; this.height = height; switch (level) { case 1 : this.mazePoints = recursiveDivision(); case 2 : this.mazePoints = recursiveBacktracker(); case 3 : this.mazePoints = prim(); } } /** * 遞歸回溯生成迷宮 * * @return 生成的迷宮的單元格集合 */ private ArrayList<MazePoint> recursiveBacktracker() { ArrayList<MazePoint> maze = new ArrayList<>(); // 初始化所以單元格都被強(qiáng)包圍 for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { MazePoint point = new MazePoint(w, h, true); maze.add(point); } } // 建立一個(gè)存放操作單元格的棧 Stack<MazePoint> stack = new Stack<>(); // 選擇(0,0)點(diǎn)作為起始點(diǎn),開始打通迷宮 stack.push(maze.get(0)); maze.get(0).visited = true; Random random = new Random(); int x; // 操作單元格的橫坐標(biāo) int y; // 操作單元格的縱坐標(biāo) int direction; // 方向 while (!stack.empty()) { // 選擇棧頂元素作為當(dāng)前操作數(shù) MazePoint operatingPoint = stack.peek(); x = operatingPoint.getX(); y = operatingPoint.getY(); direction = random.nextInt(4); MazePoint adjacency; switch (direction) { case 0: // 左邊 if ((x - 1) >= 0) { // 判斷左邊是否為邊緣 adjacency = maze.get(x - 1 + y * width); // 判斷左邊單元格是否被訪問(wèn)過(guò) if (!adjacency.visited) { operatingPoint.setLeft(0); // 打通操作單元格的左墻,和左邊單元格的右墻 adjacency.setRight(0); stack.push(adjacency); // 將左墻入棧,作為下次循環(huán)的操作單元格 adjacency.visited = true; // 將左邊的單元格設(shè)置為訪問(wèn)過(guò)了 x--; // 改變操作單元格的坐標(biāo),方便后面判斷當(dāng)前單元格四周是否都訪問(wèn)過(guò) } } break; case 1: // 右邊 // 注釋參照case0 if ((x + 1) < width) { adjacency = maze.get(x + 1 + y * width); if (!adjacency.visited) { operatingPoint.setRight(0); adjacency.setLeft(0); stack.push(adjacency); adjacency.visited = true; x++; } } break; case 2: // 上邊 // 注釋參照case0 if ((y - 1) >= 0) { adjacency = maze.get(x + (y - 1) * width); if (!adjacency.visited) { operatingPoint.setUp(0); adjacency.setDown(0); stack.push(adjacency); adjacency.visited = true; y--; } } break; case 3: // 下邊 // 注釋參照case0 if ((y + 1) < height) { adjacency = maze.get(x + (y + 1) * width); if (!adjacency.visited) { operatingPoint.setDown(0); adjacency.setUp(0); stack.push(adjacency); adjacency.visited = true; y++; } } break; } // 若操作單元格四周都被訪問(wèn)過(guò),將該單元格出棧。 if ((x - 1 < 0 || maze.get(x - 1 + y * width).visited) && (x + 1 >= width || maze.get(x + 1 + y * width).visited) && (y - 1 < 0 || maze.get(x + (y - 1) * width).visited) && (y + 1 >= height || maze.get(x + (y + 1) * width).visited)) { stack.pop(); } } maze.get(0).setLeft(0); // 左上角開墻作為入口 maze.get(width * height - 1).setRight(0); // 右下角開墻作為出口 return maze; } /** * 分割迷宮區(qū)域 * * @param maze 單元格集合 * @param right 區(qū)域的寬 * @param top 區(qū)域的高 */ private void divide(ArrayList<MazePoint> maze, int left, int right, int top, int down) { if (right - left > 0 && top - down > 0) { // 在區(qū)域中心”十“字筑墻 for (int x = left, y = (top - down) / 2 + down; x <= right; x++) { maze.get(x + y * this.width).setDown(1); maze.get(x + (y + 1) * this.width).setUp(1); } for (int x = (right - left) / 2 + left, y = down; y <= top; y++) { maze.get(x + y * this.width).setRight(1); maze.get(x + 1 + y * this.width).setLeft(1); } // 在“十”字墻中選其中三個(gè)方向拆一面墻 Random random = new Random(); int direction = random.nextInt(4); int x = (right - left) / 2 + left; int y = (top - down) / 2 + down; int tempX; int tempY; if (direction != 0) { // 打通一面左邊的墻 if (x - left > left) { tempX = random.nextInt(x - left + 1) + left; } else { tempX = left; } tempY = y; maze.get(tempX + tempY * this.width).setDown(0); maze.get(tempX + (tempY + 1) * this.width).setUp(0); } if (direction != 1) { // 打通一面右邊的墻 if (right - (x + 1) > x + 1) { tempX = random.nextInt(right - (x + 1) + 1) + x + 1; } else { tempX = x + 1; } tempY = y; maze.get(tempX + tempY * this.width).setDown(0); maze.get(tempX + (tempY + 1) * this.width).setUp(0); } if (direction != 2) { // 打通一面上面的墻 tempX = x; if (y - down > down) { tempY = random.nextInt(y - down + 1) + down; } else { tempY = down; } maze.get(tempX + tempY * this.width).setRight(0); maze.get(tempX + 1 + tempY * this.width).setLeft(0); } if (direction != 3) { // 打通一面下面的墻 tempX = x; if (top - (y + 1) > y + 1) { tempY = random.nextInt(top - (y + 1) + 1) + y + 1; } else { tempY = y + 1; } maze.get(tempX + tempY * this.width).setRight(0); maze.get(tempX + 1 + tempY * this.width).setLeft(0); } maze.stream().limit(this.width).forEach(m -> m.setUp(1)); maze.stream().skip((this.height - 1) * this.width).forEach(m -> m.setDown(1)); maze.stream().filter(m -> m.getX() == 0).forEach(m -> m.setLeft(1)); maze.stream().filter(m -> m.getX() == width - 1).forEach(m -> m.setRight(1)); divide(maze, left, (right - left) / 2 + left, (top - down) / 2 + down, down); divide(maze, left, (right - left) / 2 + left, top, (top - down) / 2 + down + 1); divide(maze, (right - left) / 2 + left + 1, right, (top - down) / 2 + down, down); divide(maze, (right - left) / 2 + left + 1, right, top, (top - down) / 2 + down + 1); } } /** * 遞歸分割生成迷宮 * * @return 生成的迷宮的單元格集合 */ private ArrayList<MazePoint> recursiveDivision() { // 初始化迷宮的所有單元格 ArrayList<MazePoint> maze = new ArrayList<>(); for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { MazePoint point = new MazePoint(w, h); maze.add(point); } } divide(maze, 0, width - 1, height - 1, 0); // 遞歸分割迷宮 maze.get(0).setLeft(0); // 左上角開墻作為入口 maze.get(width * height - 1).setRight(0); // 右下角開墻作為出口 return maze; } private ArrayList<MazePoint> prim() { ArrayList<MazePoint> mazePoints = new ArrayList<>(); PrimMaze primMaze = new PrimMaze(width * 2 + 1, height * 2 + 1); int[][] tempMaze = primMaze.getMaze(); for (int i = 0; i < tempMaze.length; i++) { for (int j = 0; j < tempMaze[i].length; j++) { if (i % 2 != 0 && j % 2 != 0) { MazePoint mazePoint = new MazePoint(i / 2, j / 2); if (tempMaze[i - 1][j] == 10) { mazePoint.setLeft(1); } if (tempMaze[i + 1][j] == 10) { mazePoint.setRight(1); } if (tempMaze[i][j - 1] == 11) { mazePoint.setUp(1); } if (tempMaze[i][j + 1] == 11) { mazePoint.setDown(1); } mazePoints.add(mazePoint); } } } mazePoints.get(0).setLeft(0); // 左上角開墻作為入口 mazePoints.get(width * height - 1).setRight(0); // 右下角開墻作為出口 return mazePoints; } public void draw() { new PlayView(mazePoints); } }
普里姆算法
class PrimMaze { private int[][] maze; public int[][] getMaze() { return maze; } PrimMaze(int row, int column) { int row1 = row / 2; int column1 = column / 2; maze = new int[row1 * 2 + 1][column1 * 2 + 1]; for (int x = 0; x < row1 * 2 + 1; x++) //初始化迷宮 { for (int y = 0; y < column1 * 2 + 1; y++) { if (x == 0 || x == row1 * 2) { maze[x][y] = -1; } if (y == 0 || y == column1 * 2) { maze[x][y] = -1; } } } for (int x = 1; x < row1 * 2; x++) { for (int y = 1; y < column1 * 2; y++) { if (x % 2 == 1 || y % 2 == 1) { maze[x][y] = 0; } if (x % 2 == 0 || y % 2 == 0) { maze[x][y] = 1; } } } ArrayList<int[]> list = new ArrayList<>(); //記錄已連通的"路"的坐標(biāo)的集合 int[] coordinate = new int[2]; //記錄未訪問(wèn)的點(diǎn)坐標(biāo) int x = 1, y = 1; //設(shè)置起點(diǎn)位置 coordinate[0] = coordinate[1] = 1; list.add(coordinate); //將起點(diǎn)加入已經(jīng)連通的路集合 //x,y表示當(dāng)前訪問(wèn)坐標(biāo) while (list.size() < row1 * column1) //當(dāng)所有點(diǎn)都已訪問(wèn)完時(shí)結(jié)束 { boolean flag1; //標(biāo)識(shí)坐標(biāo)是否已經(jīng)被訪問(wèn) int[] record = {-1, -1, -1, -1}; //用于記錄四周未被訪問(wèn)的方位,0代表上,1代表下,2代表左,3代表右 if (x - 2 > 0) //判斷當(dāng)前位置上方是否有路 { int[] a = new int[2]; a[0] = x - 2; a[1] = y; flag1 = judge(a, list); //判斷上方是否已經(jīng)被訪問(wèn) if (flag1) { record[0] = 0; } } if (x + 2 < row1 * 2) //判斷當(dāng)前位置下方是否有路 { int[] a = new int[2]; a[0] = x + 2; a[1] = y; flag1 = judge(a, list); //判斷下方是否已經(jīng)被訪問(wèn) if (flag1) { record[1] = 1; } } if (y - 2 > 0) //判斷當(dāng)前位置左方是否有路 { int[] a = new int[2]; a[0] = x; a[1] = y - 2; flag1 = judge(a, list); //判斷左方是否已經(jīng)被訪問(wèn) if (flag1) { record[2] = 2; } } if (y + 2 < column1 * 2) //判斷當(dāng)前位置右方是否有路 { int[] a = new int[2]; a[0] = x; a[1] = y + 2; flag1 = judge(a, list); //判斷右方是否已經(jīng)被訪問(wèn) if (flag1) { record[3] = 3; } } boolean flag2 = false; //flag2標(biāo)識(shí)四周是否有未訪問(wèn)過(guò)的路 for (int i = 0; i < 4; i++) //判斷當(dāng)前位置的四個(gè)方位是否有未訪問(wèn)過(guò)的路 { if (record[i] == i) { flag2 = true; //如果有未訪問(wèn)過(guò)的路,跳出循環(huán) break; } } int r = new Random().nextInt(4); while (record[r] == r) { r = new Random().nextInt(4); } while (record[r] != r && flag2) //當(dāng)方位標(biāo)識(shí)錯(cuò)誤且當(dāng)前位置四周有未訪問(wèn)過(guò)的點(diǎn)時(shí)繼續(xù)隨機(jī)獲取一個(gè)新的方位標(biāo)識(shí),直到標(biāo)識(shí)正確 { r = new Random().nextInt(4); //隨機(jī)選取一個(gè)可以符合條件的墻并將其敲碎 if (record[r] == r) //當(dāng)標(biāo)識(shí)正確時(shí),敲碎兩點(diǎn)之間的墻 { if (r == 0) { //當(dāng)上方有未訪問(wèn)過(guò)的點(diǎn)時(shí),敲碎上方的墻 maze[x - 1][y] = 0; } if (r == 1) { //當(dāng)下方有未訪問(wèn)過(guò)的點(diǎn)時(shí),敲碎下方的墻 maze[x + 1][y] = 0; } if (r == 2) { //當(dāng)左方有未訪問(wèn)過(guò)的點(diǎn)時(shí),敲碎左方的墻 maze[x][y - 1] = 0; } if (r == 3) { //當(dāng)右方有未訪問(wèn)過(guò)的點(diǎn)時(shí),敲碎右方的墻 maze[x][y + 1] = 0; } } } //將與當(dāng)前坐標(biāo)之間的墻被敲碎的路的坐標(biāo)從未被訪問(wèn)的集合中移出 if (r == 0 && flag2) //如果敲碎的是上方的墻,則將上方的路加入到已連通的路集合 { int[] b = new int[2]; b[0] = x - 2; b[1] = y; if (judge(b, list)) { list.add(b); } } if (r == 1 && flag2) //如果敲碎的是下方的墻,則將下方的路加入到已連通的路集合 { int[] b = new int[2]; b[0] = x + 2; b[1] = y; if (judge(b, list)) { list.add(b); } } if (r == 2 && flag2) //如果敲碎的是左方的墻,則將左方的路加入到已連通的路集合 { int[] b = new int[2]; b[0] = x; b[1] = y - 2; if (judge(b, list)) { list.add(b); } } if (r == 3 && flag2) //如果敲碎的是右方的墻,則將右方的路加入到已連通的路集合 { int[] b = new int[2]; b[0] = x; b[1] = y + 2; if (judge(b, list)) { list.add(b); } } int i = new Random().nextInt(list.size()); //隨機(jī)選取一個(gè)被連通的路坐標(biāo) x = list.get(i)[0]; //獲取路坐標(biāo) y = list.get(i)[1]; } for (int r = 0; r < maze.length; r++)//將方格墻轉(zhuǎn)為線條墻,10表示橫,11表示豎 { for (int c = 0; c < maze[r].length; c++) { if (r % 2 == 0 && c % 2 == 1) { if (maze[r][c] != 0) { maze[r][c] = 10; } } if (r % 2 == 1 && c % 2 == 0) { if (maze[r][c] != 0) { maze[r][c] = 11; } } } } } boolean judge(int[] coordinate, ArrayList<int[]> list) //判斷路是否已經(jīng)加入通路集合,已加入則返回false { boolean flag = true; for (int[] ints : list) { if (coordinate[0] == ints[0] && coordinate[1] == ints[1]) //若已訪問(wèn)點(diǎn)集合中含有該位置的坐標(biāo),表示該位置已訪問(wèn)過(guò),不用重復(fù)加入該位置的坐標(biāo) { flag = false; break; } } return flag; } }
以上是“Java如何實(shí)現(xiàn)經(jīng)典游戲復(fù)雜迷宮”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。