<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Java 掃雷 > 原文: [https://zetcode.com/tutorials/javagamestutorial/minesweeper/](https://zetcode.com/tutorials/javagamestutorial/minesweeper/) 在 Java 2D 游戲教程的這一部分中,我們創建一個掃雷游戲克隆。 源代碼和圖像可以在作者的 Github [Java-Minesweeper-Game](https://github.com/janbodnar/Java-Minesweeper-Game) 存儲庫中找到。 ## 掃雷 《掃雷》是一款流行的棋盤游戲,默認情況下附帶許多操作系統。 游戲的目標是掃雷場中的所有地雷。 如果玩家單擊包含地雷的單元格,則地雷會引爆,游戲結束。 單元格可以包含數字,也可以為空白。 該數字表示與該特定單元相鄰的地雷數量。 我們通過右鍵單擊在單元格上設置標記。 通過這種方式,我們表明我們相信,有一個地雷。 ## Java Minesweeper 游戲的開發 游戲包含兩個類別:`Board`和`Minesweeper`。 `src/resources`目錄中有 13 張圖片。 `com/zetcode/Board.java` ```java package com.zetcode; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Random; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; public class Board extends JPanel { private final int NUM_IMAGES = 13; private final int CELL_SIZE = 15; private final int COVER_FOR_CELL = 10; private final int MARK_FOR_CELL = 10; private final int EMPTY_CELL = 0; private final int MINE_CELL = 9; private final int COVERED_MINE_CELL = MINE_CELL + COVER_FOR_CELL; private final int MARKED_MINE_CELL = COVERED_MINE_CELL + MARK_FOR_CELL; private final int DRAW_MINE = 9; private final int DRAW_COVER = 10; private final int DRAW_MARK = 11; private final int DRAW_WRONG_MARK = 12; private final int N_MINES = 40; private final int N_ROWS = 16; private final int N_COLS = 16; private final int BOARD_WIDTH = N_COLS * CELL_SIZE + 1; private final int BOARD_HEIGHT = N_ROWS * CELL_SIZE + 1; private int[] field; private boolean inGame; private int minesLeft; private Image[] img; private int allCells; private final JLabel statusbar; public Board(JLabel statusbar) { this.statusbar = statusbar; initBoard(); } private void initBoard() { setPreferredSize(new Dimension(BOARD_WIDTH, BOARD_HEIGHT)); img = new Image[NUM_IMAGES]; for (int i = 0; i < NUM_IMAGES; i++) { var path = "src/resources/" + i + ".png"; img[i] = (new ImageIcon(path)).getImage(); } addMouseListener(new MinesAdapter()); newGame(); } private void newGame() { int cell; var random = new Random(); inGame = true; minesLeft = N_MINES; allCells = N_ROWS * N_COLS; field = new int[allCells]; for (int i = 0; i < allCells; i++) { field[i] = COVER_FOR_CELL; } statusbar.setText(Integer.toString(minesLeft)); int i = 0; while (i < N_MINES) { int position = (int) (allCells * random.nextDouble()); if ((position < allCells) && (field[position] != COVERED_MINE_CELL)) { int current_col = position % N_COLS; field[position] = COVERED_MINE_CELL; i++; if (current_col > 0) { cell = position - 1 - N_COLS; if (cell >= 0) { if (field[cell] != COVERED_MINE_CELL) { field[cell] += 1; } } cell = position - 1; if (cell >= 0) { if (field[cell] != COVERED_MINE_CELL) { field[cell] += 1; } } cell = position + N_COLS - 1; if (cell < allCells) { if (field[cell] != COVERED_MINE_CELL) { field[cell] += 1; } } } cell = position - N_COLS; if (cell >= 0) { if (field[cell] != COVERED_MINE_CELL) { field[cell] += 1; } } cell = position + N_COLS; if (cell < allCells) { if (field[cell] != COVERED_MINE_CELL) { field[cell] += 1; } } if (current_col < (N_COLS - 1)) { cell = position - N_COLS + 1; if (cell >= 0) { if (field[cell] != COVERED_MINE_CELL) { field[cell] += 1; } } cell = position + N_COLS + 1; if (cell < allCells) { if (field[cell] != COVERED_MINE_CELL) { field[cell] += 1; } } cell = position + 1; if (cell < allCells) { if (field[cell] != COVERED_MINE_CELL) { field[cell] += 1; } } } } } } private void find_empty_cells(int j) { int current_col = j % N_COLS; int cell; if (current_col > 0) { cell = j - N_COLS - 1; if (cell >= 0) { if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) { find_empty_cells(cell); } } } cell = j - 1; if (cell >= 0) { if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) { find_empty_cells(cell); } } } cell = j + N_COLS - 1; if (cell < allCells) { if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) { find_empty_cells(cell); } } } } cell = j - N_COLS; if (cell >= 0) { if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) { find_empty_cells(cell); } } } cell = j + N_COLS; if (cell < allCells) { if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) { find_empty_cells(cell); } } } if (current_col < (N_COLS - 1)) { cell = j - N_COLS + 1; if (cell >= 0) { if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) { find_empty_cells(cell); } } } cell = j + N_COLS + 1; if (cell < allCells) { if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) { find_empty_cells(cell); } } } cell = j + 1; if (cell < allCells) { if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) { find_empty_cells(cell); } } } } } @Override public void paintComponent(Graphics g) { int uncover = 0; for (int i = 0; i < N_ROWS; i++) { for (int j = 0; j < N_COLS; j++) { int cell = field[(i * N_COLS) + j]; if (inGame && cell == MINE_CELL) { inGame = false; } if (!inGame) { if (cell == COVERED_MINE_CELL) { cell = DRAW_MINE; } else if (cell == MARKED_MINE_CELL) { cell = DRAW_MARK; } else if (cell > COVERED_MINE_CELL) { cell = DRAW_WRONG_MARK; } else if (cell > MINE_CELL) { cell = DRAW_COVER; } } else { if (cell > COVERED_MINE_CELL) { cell = DRAW_MARK; } else if (cell > MINE_CELL) { cell = DRAW_COVER; uncover++; } } g.drawImage(img[cell], (j * CELL_SIZE), (i * CELL_SIZE), this); } } if (uncover == 0 && inGame) { inGame = false; statusbar.setText("Game won"); } else if (!inGame) { statusbar.setText("Game lost"); } } private class MinesAdapter extends MouseAdapter { @Override public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); int cCol = x / CELL_SIZE; int cRow = y / CELL_SIZE; boolean doRepaint = false; if (!inGame) { newGame(); repaint(); } if ((x < N_COLS * CELL_SIZE) && (y < N_ROWS * CELL_SIZE)) { if (e.getButton() == MouseEvent.BUTTON3) { if (field[(cRow * N_COLS) + cCol] > MINE_CELL) { doRepaint = true; if (field[(cRow * N_COLS) + cCol] <= COVERED_MINE_CELL) { if (minesLeft > 0) { field[(cRow * N_COLS) + cCol] += MARK_FOR_CELL; minesLeft--; String msg = Integer.toString(minesLeft); statusbar.setText(msg); } else { statusbar.setText("No marks left"); } } else { field[(cRow * N_COLS) + cCol] -= MARK_FOR_CELL; minesLeft++; String msg = Integer.toString(minesLeft); statusbar.setText(msg); } } } else { if (field[(cRow * N_COLS) + cCol] > COVERED_MINE_CELL) { return; } if ((field[(cRow * N_COLS) + cCol] > MINE_CELL) && (field[(cRow * N_COLS) + cCol] < MARKED_MINE_CELL)) { field[(cRow * N_COLS) + cCol] -= COVER_FOR_CELL; doRepaint = true; if (field[(cRow * N_COLS) + cCol] == MINE_CELL) { inGame = false; } if (field[(cRow * N_COLS) + cCol] == EMPTY_CELL) { find_empty_cells((cRow * N_COLS) + cCol); } } } if (doRepaint) { repaint(); } } } } } ``` 首先,我們定義游戲中使用的常量。 ```java private final int NUM_IMAGES = 13; private final int CELL_SIZE = 15; ``` 此游戲中使用了十三張圖像。 一個牢房最多可被八個地雷包圍,因此我們需要一號到八號。 我們需要一個空單元格,一個地雷,一個被遮蓋的單元格,一個標記的單元格,最后需要一個錯誤標記的單元格的圖像。 每個圖像的大小為`15x15`像素。 ```java private final int COVER_FOR_CELL = 10; private final int MARK_FOR_CELL = 10; private final int EMPTY_CELL = 0; ... ``` 地雷區是數字數組。 例如,0 表示一個空單元格。 數字 10 用于電池蓋和標記。 使用常量可以提高代碼的可讀性。 ```java private final int MINE_CELL = 9; ``` `MINE_CELL`表示包含地雷的單元。 ```java private final int COVERED_MINE_CELL = MINE_CELL + COVER_FOR_CELL; private final int MARKED_MINE_CELL = COVERED_MINE_CELL + MARK_FOR_CELL; ``` `COVERED_MINE_CELL`用于覆蓋并包含地雷的區域。 `MARKED_MINE_CELL`代碼是由用戶標記的隱蔽地雷單元。 ```java private final int DRAW_MINE = 9; private final int DRAW_COVER = 10; private final int DRAW_MARK = 11; private final int DRAW_WRONG_MARK = 12; ``` 這些競爭因素決定是否繪制地雷,地雷覆蓋物,標記和標記錯誤的單元。 ```java private final int N_MINES = 40; private final int N_ROWS = 16; private final int N_COLS = 16; ``` 我們游戲中的雷區有 40 個隱藏的地雷。 該字段中有 16 行和 16 列。 因此,雷場中共有 262 個牢房。 ```java private int[] field; ``` 該字段是數字數組。 字段中的每個單元格都有一個特定的編號。 例如,一個礦井的編號為 9。一個礦井的編號為 2 意味著它與兩個礦井相鄰。 數字已添加。 例如,一個被覆蓋的地雷的編號為 19,地雷的編號為 9,電池蓋的編號為 10,依此類推。 ```java private boolean inGame; ``` `inGame`變量確定我們是在游戲中還是游戲結束。 ```java private int minesLeft; ``` `minesLeft`變量要標記為左側的地雷數量。 ```java for (int i = 0; i < NUM_IMAGES; i++) { var path = "src/resources/" + i + ".png"; img[i] = (new ImageIcon(path)).getImage(); } ``` 我們將圖像加載到圖像數組中。 這些圖像分別命名為`0.png`,`1.png` ... `12.png`。 `newGame()`啟動掃雷游戲。 ```java allCells = N_ROWS * N_COLS; field = new int[allCells]; for (int i = 0; i < allCells; i++) { field[i] = COVER_FOR_CELL; } ``` 這些線設置了雷區。 默認情況下覆蓋每個單元格。 ```java int i = 0; while (i < N_MINES) { int position = (int) (allCells * random.nextDouble()); if ((position < allCells) && (field[position] != COVERED_MINE_CELL)) { int current_col = position % N_COLS; field[position] = COVERED_MINE_CELL; i++; ... ``` 在白色周期中,我們將所有地雷隨機放置在野外。 ```java cell = position - N_COLS; if (cell >= 0) { if (field[cell] != COVERED_MINE_CELL) { field[cell] += 1; } } ``` 每個單元最多可以包圍八個單元。 (這不適用于邊界單元。)我們為每個隨機放置的地雷增加相鄰單元的數量。 在我們的示例中,我們向相關單元格的頂部鄰居添加 1。 在`find_empty_cells()`方法中,我們找到了空單元格。 如果玩家單擊雷區,則游戲結束。 如果他單擊與地雷相鄰的單元,則會發現一個數字,該數字指示該單元與地雷相鄰的數量。 單擊一個空單元格會導致發現許多其他空單元格以及帶有數字的單元格,這些數字在空邊界的空間周圍形成邊界。 我們使用遞歸算法來查找空單元格。 ```java cell = j - 1; if (cell <= 0) { if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) { find_empty_cells(cell); } } } ``` 在此代碼中,我們檢查位于相關空單元格左側的單元格。 如果不為空,則將其覆蓋。 如果為空,則通過遞歸調用`find_empty_cells()`方法來重復整個過程。 `paintComponent()`方法將數字轉換為圖像。 ```java if (!inGame) { if (cell == COVERED_MINE_CELL) { cell = DRAW_MINE; } else if (cell == MARKED_MINE_CELL) { cell = DRAW_MARK; } else if (cell > COVERED_MINE_CELL) { cell = DRAW_WRONG_MARK; } else if (cell > MINE_CELL) { cell = DRAW_COVER; } } ... ``` 如果游戲結束并且我們輸了,我們將顯示所有未發現的地雷(如果有的話),并顯示所有錯誤標記的單元格(如果有)。 ```java g.drawImage(img[cell], (j * CELL_SIZE), (i * CELL_SIZE), this); ``` 此代碼行繪制了窗口上的每個單元格。 ```java if (uncover == 0 && inGame) { inGame = false; statusbar.setText("Game won"); } else if (!inGame) { statusbar.setText("Game lost"); } ``` 如果沒有什么可以發現的,我們就贏了。 如果`inGame`變量設置為`false`,我們將丟失。 在`mousePressed()`方法中,我們對鼠標單擊做出反應。 掃雷游戲完全由鼠標控制。 我們對鼠標左鍵和右鍵單擊做出反應。 ```java int x = e.getX(); int y = e.getY(); ``` 我們確定鼠標指針的 x 和 y 坐標。 ```java int cCol = x / CELL_SIZE; int cRow = y / CELL_SIZE; ``` 我們計算雷區的相應列和行。 ```java if ((x < N_COLS * CELL_SIZE) && (y < N_ROWS * CELL_SIZE)) { ``` 我們檢查我們是否位于雷區。 ```java if (e.getButton() == MouseEvent.BUTTON3) { ``` 地雷的發現是通過鼠標右鍵完成的。 ```java field[(cRow * N_COLS) + cCol] += MARK_FOR_CELL; minesLeft--; ``` 如果右鍵單擊未標記的單元格,則將`MARK_FOR_CELL`添加到表示該單元格的數字中。 這導致在`paintComponent()`方法中繪制帶有標記的覆蓋單元。 ```java field[(cRow * N_COLS) + cCol] -= MARK_FOR_CELL; minesLeft++; var msg = Integer.toString(minesLeft); statusbar.setText(msg); ``` 如果我們在已經標記的單元格上單擊鼠標左鍵,我們將刪除標記并增加要標記的單元格的數量。 ```java if (field[(cRow * N_COLS) + cCol] > COVERED_MINE_CELL) { return; } ``` 如果單擊覆蓋并標記的單元格,則不會發生任何事情。 必須首先通過另一次右鍵單擊來發現它,然后才可以在其上單擊鼠標左鍵。 ```java field[(cRow * N_COLS) + cCol] -= COVER_FOR_CELL; ``` 左鍵單擊可從單元中移除蓋子。 ```java if (field[(cRow * N_COLS) + cCol] == MINE_CELL) { inGame = false; } if (field[(cRow * N_COLS) + cCol] == EMPTY_CELL) { find_empty_cells((cRow * N_COLS) + cCol); } ``` 萬一我們左鍵單擊地雷,游戲就結束了。 如果我們在空白單元格上單擊鼠標左鍵,我們將調用`find_empty_cells()`方法,該方法遞歸地找到所有相鄰的空白單元格。 ```java if (doRepaint) { repaint(); } ``` 如果需要重新粉刷電路板(例如,設置或移除了標記),我們將調用`repaint()`方法。 `com/zetcode/Minesweeper.java` ```java package com.zetcode; import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.JFrame; import javax.swing.JLabel; /** * Java Minesweeper Game * * Author: Jan Bodnar * Website: http://zetcode.com */ public class Minesweeper extends JFrame { private JLabel statusbar; public Minesweeper() { initUI(); } private void initUI() { statusbar = new JLabel(""); add(statusbar, BorderLayout.SOUTH); add(new Board(statusbar)); setResizable(false); pack(); setTitle("Minesweeper"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new Minesweeper(); ex.setVisible(true); }); } } ``` 這是主要的類。 ```java setResizable(false); ``` 窗口大小固定。 為此,我們使用`setResizable()`方法。 ![Minesweeper](https://img.kancloud.cn/39/df/39dff9a7b6570ecd66b57cbb395cd9ce_250x290.jpg) 圖:掃雷 在 Java 2D 游戲教程的這一部分中,我們創建了掃雷游戲的 Java 復制版本。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看