您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Java如何實現經典游戲復雜迷宮”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“Java如何實現經典游戲復雜迷宮”這篇文章吧。
人類建造迷宮已有5000年的歷史。在世界的不同文化發展時期,這些奇特的建筑物始終吸引人們沿著彎彎曲曲、困難重重的小路吃力地行走,尋找真相。迷宮類小游戲應運而生。在游戲中,迷宮被表現為冒險舞臺里,藏有各式各樣奇妙與謎題或寶藏的危險區域。型態有洞窟、人工建筑物、怪物巢穴、密林或山路等。迷宮內有惡徒或兇猛的生物(真實存在或想像物體都有)徘徊,其中可能會有陷阱、不明設施、遺跡等。
《復雜迷宮》游戲是用java語言實現,采用了swing技術進行了界面化處理,設計思路用了面向對象思想。
主要需求
方向鍵控制移動,角色走出迷宮,游戲勝利。增加游戲難度和增加隨機地圖。
1、構建游戲地圖面板
2、設定迷宮地圖,包含可走的通道,不可走的墻體,還有出口位置
3、鍵盤的上下左右按鍵,來控制角色的移動
4、角色移動的算法,通道可走,遇到墻體不可走
5、走到終點,有成功通關的提示。
6、增加游戲的難度選擇,難度1,難度2和難度3
7、每次生成的地圖是隨機的
8、地圖大小可選擇,迷宮的長在10-45之間,寬在10-90之間
9、增加撞墻的音樂效果
游戲開始頁面
生成難度1,10*10的迷宮地圖
隨機地圖:生成難度1,10*10的迷宮地圖
生成難度2,30*30的迷宮地圖
生成難度3,90*45的迷宮地圖
成功過關-效果
窗口布局
public class StartView extends JFrame { public StartView() { this.setTitle("復雜迷宮"); 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("迷宮長度:"); 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("生成迷宮"); // 設定標簽位置 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); // 限制輸入框只接收數字 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); // 默認選擇難度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); // 生成迷宮監聽器 run.addActionListener(e -> { // 建議寬在10-90,長在10-45之間 if (widthText.getText().equals("")) { JOptionPane.showMessageDialog(null, "長度不能為空!", "提示", 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, "迷宮的長必須在10-45之間,寬必須在10-90之間,請檢查輸入是否有誤!", "錯誤輸入", JOptionPane.ERROR_MESSAGE); } } }); // 添加回車鍵入監聽器 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(); } }
迷宮的數學模型
public class MazeModel { private int width; private int height; private ArrayList<MazePoint> mazePoints; /** * 迷宮的構造方法 * * @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<>(); // 初始化所以單元格都被強包圍 for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { MazePoint point = new MazePoint(w, h, true); maze.add(point); } } // 建立一個存放操作單元格的棧 Stack<MazePoint> stack = new Stack<>(); // 選擇(0,0)點作為起始點,開始打通迷宮 stack.push(maze.get(0)); maze.get(0).visited = true; Random random = new Random(); int x; // 操作單元格的橫坐標 int y; // 操作單元格的縱坐標 int direction; // 方向 while (!stack.empty()) { // 選擇棧頂元素作為當前操作數 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); // 判斷左邊單元格是否被訪問過 if (!adjacency.visited) { operatingPoint.setLeft(0); // 打通操作單元格的左墻,和左邊單元格的右墻 adjacency.setRight(0); stack.push(adjacency); // 將左墻入棧,作為下次循環的操作單元格 adjacency.visited = true; // 將左邊的單元格設置為訪問過了 x--; // 改變操作單元格的坐標,方便后面判斷當前單元格四周是否都訪問過 } } 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; } // 若操作單元格四周都被訪問過,將該單元格出棧。 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; } /** * 分割迷宮區域 * * @param maze 單元格集合 * @param right 區域的寬 * @param top 區域的高 */ private void divide(ArrayList<MazePoint> maze, int left, int right, int top, int down) { if (right - left > 0 && top - down > 0) { // 在區域中心”十“字筑墻 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); } // 在“十”字墻中選其中三個方向拆一面墻 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<>(); //記錄已連通的"路"的坐標的集合 int[] coordinate = new int[2]; //記錄未訪問的點坐標 int x = 1, y = 1; //設置起點位置 coordinate[0] = coordinate[1] = 1; list.add(coordinate); //將起點加入已經連通的路集合 //x,y表示當前訪問坐標 while (list.size() < row1 * column1) //當所有點都已訪問完時結束 { boolean flag1; //標識坐標是否已經被訪問 int[] record = {-1, -1, -1, -1}; //用于記錄四周未被訪問的方位,0代表上,1代表下,2代表左,3代表右 if (x - 2 > 0) //判斷當前位置上方是否有路 { int[] a = new int[2]; a[0] = x - 2; a[1] = y; flag1 = judge(a, list); //判斷上方是否已經被訪問 if (flag1) { record[0] = 0; } } if (x + 2 < row1 * 2) //判斷當前位置下方是否有路 { int[] a = new int[2]; a[0] = x + 2; a[1] = y; flag1 = judge(a, list); //判斷下方是否已經被訪問 if (flag1) { record[1] = 1; } } if (y - 2 > 0) //判斷當前位置左方是否有路 { int[] a = new int[2]; a[0] = x; a[1] = y - 2; flag1 = judge(a, list); //判斷左方是否已經被訪問 if (flag1) { record[2] = 2; } } if (y + 2 < column1 * 2) //判斷當前位置右方是否有路 { int[] a = new int[2]; a[0] = x; a[1] = y + 2; flag1 = judge(a, list); //判斷右方是否已經被訪問 if (flag1) { record[3] = 3; } } boolean flag2 = false; //flag2標識四周是否有未訪問過的路 for (int i = 0; i < 4; i++) //判斷當前位置的四個方位是否有未訪問過的路 { if (record[i] == i) { flag2 = true; //如果有未訪問過的路,跳出循環 break; } } int r = new Random().nextInt(4); while (record[r] == r) { r = new Random().nextInt(4); } while (record[r] != r && flag2) //當方位標識錯誤且當前位置四周有未訪問過的點時繼續隨機獲取一個新的方位標識,直到標識正確 { r = new Random().nextInt(4); //隨機選取一個可以符合條件的墻并將其敲碎 if (record[r] == r) //當標識正確時,敲碎兩點之間的墻 { if (r == 0) { //當上方有未訪問過的點時,敲碎上方的墻 maze[x - 1][y] = 0; } if (r == 1) { //當下方有未訪問過的點時,敲碎下方的墻 maze[x + 1][y] = 0; } if (r == 2) { //當左方有未訪問過的點時,敲碎左方的墻 maze[x][y - 1] = 0; } if (r == 3) { //當右方有未訪問過的點時,敲碎右方的墻 maze[x][y + 1] = 0; } } } //將與當前坐標之間的墻被敲碎的路的坐標從未被訪問的集合中移出 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()); //隨機選取一個被連通的路坐標 x = list.get(i)[0]; //獲取路坐標 y = list.get(i)[1]; } for (int r = 0; r < maze.length; r++)//將方格墻轉為線條墻,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) //判斷路是否已經加入通路集合,已加入則返回false { boolean flag = true; for (int[] ints : list) { if (coordinate[0] == ints[0] && coordinate[1] == ints[1]) //若已訪問點集合中含有該位置的坐標,表示該位置已訪問過,不用重復加入該位置的坐標 { flag = false; break; } } return flag; } }
以上是“Java如何實現經典游戲復雜迷宮”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。