自學Java-AI結合GUI開發一個石頭迷陣的游戲
- 準備環節
- 1、創建石頭迷陣的界面
- 2、打亂順序
- 3、控制上下左右移動
- 4、判斷是否通關
- 5、統計移動步驟,重啟游戲
- 6、拓展問題
準備環節
技術:
1、GUI界面編程
2、二維數組
3、程序流程控制
4、面向對象編程
? \bullet ?創建一個模塊用于開發石頭迷陣游戲,模塊名稱取名為:stone-maze
? \bullet ?導入項目需要的資源包到src目錄下:主要是一些圖片文件,在image文件夾下
? \bullet ?創建項目包:com.itheima.
1、創建石頭迷陣的界面
? \bullet ?定義主界面類,MainFrame繼承JFrame
? \bullet ?初始化窗口大小
? \bullet ?初始化界面圖片
? \bullet ?初始化界面菜單:系統退出,重啟游戲
完成代碼如下:
package com.itheima;import javax.swing.*;
// 自定義窗口類,創建對象,展示一個主窗口。
public class MainFrame extends JFrame {// 設置圖片位置private static final String imagePath = "stone-maze/src/image/";// 準備一個數組,用戶存儲數字色塊的行列位置:4行4列private int[][] imageData = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12},{13, 14, 15, 0}};public MainFrame() {// 1、調用一個初始化方法:初始化窗口大小等信息。initFrame();// 2、初始化界面:展示數字色塊。initImage();// 3、初始化系統菜單:點擊彈出菜單信息是系統退出,重啟游戲。initMenu();// 設置窗口可見this.setVisible(true);}private void initMenu() {JMenuBar menuBar = new JMenuBar(); // 創建一個菜單欄JMenu menu = new JMenu("系統");JMenuItem exitJi = new JMenuItem("退出");menu.add(exitJi); // 添加子菜單exitJi.addActionListener(e -> { // 添加點擊事件// 退出游戲dispose();});JMenuItem restartJi = new JMenuItem("重啟");menu.add(restartJi);restartJi.addActionListener(e -> { // 添加點擊事件// 重啟游戲});menuBar.add(menu); // 添加到菜單欄中this.setJMenuBar(menuBar);}private void initImage() {// 1、展示一個行列矩陣的圖片色塊依次鋪滿窗口(4 * 4)for (int i = 0; i < imageData.length; i++) { // 遍歷行for (int j = 0; j < imageData[i].length; j++) { // 遍歷列// 拿到圖片的名稱String imageName = imageData[i][j] + ".png";// 2、創建一個JLabel對象,設置圖片給他展示。JLabel label = new JLabel();// 3、設置圖片到label對象中去。label.setIcon(new ImageIcon(imagePath + imageName));// 4、設置數字色塊的位置label.setBounds(25 + j * 100, 60 + i * 100, 100, 100);// 5、把這個圖片展示到窗口中this.add(label);}}// 設置窗口的背景圖片JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));background.setBounds(0, 0, 450, 484);this.add(background);}private void initFrame() {// 設置窗口標題this.setTitle("石子迷宮 V 1.0");// 設置窗口大小this.setSize(465, 575);// 設置窗口關閉方式this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 設置窗口的居中顯示this.setLocationRelativeTo(null);// 設置布局方式為絕對位置定位this.setLayout(null);}
}
2、打亂順序
? \bullet ?打亂界面的圖片順序,讓游戲具備可玩性:使用方法如下
打亂二維數組中的元素順序:initRandomArray();
完成代碼如下:
private void initRandomArray() {// 1、打亂數組for (int i = 0; i < imageData.length; i++) { // 遍歷行for (int j = 0; j < imageData[i].length; j++) { // 遍歷列// 隨機兩個行列位置,讓這兩個位置交換。int i1 = (int)(Math.random() * imageData.length);int j1 = (int)(Math.random() * imageData.length);int i2 = (int)(Math.random() * imageData.length);int j2 = (int)(Math.random() * imageData.length);int temp = imageData[i1][j1];imageData[i1][j1] = imageData[i2][j2];imageData[i2][j2] = temp;}}}
3、控制上下左右移動
? \bullet ?給窗口綁定上下左右按鍵事件
? \bullet ?控制位置的交換
–定位當前空白色塊的位置。
–根據用戶點擊的方位確定交換哪個數據,到數組中交換。
? \bullet ?重新繪制主界面的內容
–讓主界面按照二維數組的最新內容刷新界面
public enum Direction {UP, DOWN, LEFT, RIGHT;
}
完成代碼如下:
package com.itheima;import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;// 自定義窗口類,創建對象,展示一個主窗口。
public class MainFrame extends JFrame {// 設置圖片位置private static final String imagePath = "stone-maze/src/image/";// 準備一個數組,用戶存儲數字色塊的行列位置:4行4列private int[][] imageData = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12},{13, 14, 15, 0}};// 定義兩個整數變量記錄當前空白色塊的位置。private int row; // 當前空白色塊的行位置private int col; // 當前空白色塊的列位置public MainFrame() {// 1、調用一個初始化方法:初始化窗口大小等信息。initFrame();// 4、打亂數組色塊的順序,再提示圖片initRandomArray();// 2、初始化界面:展示數字色塊。initImage();// 3、初始化系統菜單:點擊彈出菜單信息是系統退出,重啟游戲。initMenu();// 5、給當前窗口綁定上下左右按鍵事件。initKeyPressEvent();// 設置窗口可見this.setVisible(true);}private void initKeyPressEvent() {// 給當前窗口綁定上下左右按鍵事件。this.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {// 判斷當前按鍵的編號int keyCode = e.getKeyCode();// 判斷按鍵編號,上下左右switch (keyCode) {case KeyEvent.VK_UP:switchAndMove(Direction.UP);// 用戶按下上鍵,把圖片上移。break;case KeyEvent.VK_DOWN:switchAndMove(Direction.DOWN);// 用戶按下下鍵,把圖片下移。break;case KeyEvent.VK_LEFT:switchAndMove(Direction.LEFT);// 用戶按下左鍵,把圖片左移。break;case KeyEvent.VK_RIGHT:switchAndMove(Direction.RIGHT);// 用戶按下右鍵,把圖片右移。break;}}});}// 控制數據交換和圖片移動private void switchAndMove(Direction r) {// 判斷圖片的方向,再控制圖片移動。switch (r) {case UP:// 上交換的條件是行必須 < 3,然后才開始交換。if (row < imageData.length - 1) {// 當前空白色塊位置:rol col// 需要被交換的位置:row + 1 colint temp = imageData[row][col];imageData[row][col] = imageData[row + 1][col];imageData[row + 1][col] = temp;// 更新當前空白色塊的位置。row++;}break;case DOWN:if (row > 0) {// 當前空白色塊位置:row col// 需要被交換的位置:row - 1 colint temp = imageData[row][col];imageData[row][col] = imageData[row - 1][col];imageData[row - 1][col] = temp;// 更新當前空白色塊的位置。row--;}break;case LEFT:// 左交換的條件是列必須 < 3,然后才開始交換。if (col < imageData.length - 1) {// 當前空白色塊位置:row col// 需要被交換的位置:row col + 1int temp = imageData[row][col];imageData[row][col] = imageData[row][col + 1];imageData[row][col + 1] = temp;// 更新當前空白色塊的位置。col++;}break;case RIGHT:if (col > 0) {int temp = imageData[row][col];imageData[row][col] = imageData[row][col - 1];imageData[row][col - 1] = temp;col--;}break;}// 重新刷新界面!!!initImage();}private void initRandomArray() {// 1、打亂數組for (int i = 0; i < imageData.length; i++) { // 遍歷行for (int j = 0; j < imageData[i].length; j++) { // 遍歷列// 隨機兩個行列位置,讓這兩個位置交換。int i1 = (int)(Math.random() * imageData.length);int j1 = (int)(Math.random() * imageData.length);int i2 = (int)(Math.random() * imageData.length);int j2 = (int)(Math.random() * imageData.length);int temp = imageData[i1][j1];imageData[i1][j1] = imageData[i2][j2];imageData[i2][j2] = temp;}}// 定義空白色塊的位置。// 去二維數組中遍歷每個數據,只要發現這個數據等于0,這個位置就是當前空白色塊的位置。OUT:for (int i = 0; i < imageData.length; i++) { // 遍歷行for (int j = 0; j < imageData[i].length; j++) { // 遍歷列if (imageData[i][j] == 0) {// 定位空白色塊的位置row = i;col = j;break OUT; // 跳出兩個for循環}}}}private void initMenu() {JMenuBar menuBar = new JMenuBar(); // 創建一個菜單欄JMenu menu = new JMenu("系統");JMenuItem exitJi = new JMenuItem("退出");menu.add(exitJi); // 添加子菜單exitJi.addActionListener(e -> { // 添加點擊事件// 退出游戲dispose();});JMenuItem restartJi = new JMenuItem("重啟");menu.add(restartJi);restartJi.addActionListener(e -> { // 添加點擊事件// 重啟游戲});menuBar.add(menu); // 添加到菜單欄中this.setJMenuBar(menuBar);}private void initImage() {// 先清空窗口上的全部圖層this.getContentPane().removeAll();// 1、展示一個行列矩陣的圖片色塊依次鋪滿窗口(4 * 4)for (int i = 0; i < imageData.length; i++) { // 遍歷行for (int j = 0; j < imageData[i].length; j++) { // 遍歷列// 拿到圖片的名稱String imageName = imageData[i][j] + ".png";// 2、創建一個JLabel對象,設置圖片給他展示。JLabel label = new JLabel();// 3、設置圖片到label對象中去。label.setIcon(new ImageIcon(imagePath + imageName));// 4、設置數字色塊的位置label.setBounds(20 + j * 100, 60 + i * 100, 100, 100);// 5、把這個圖片展示到窗口中this.add(label);}}// 設置窗口的背景圖片JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));background.setBounds(0, 0, 450, 484);this.add(background);// 刷新新圖層,重新繪制窗口this.repaint();}private void initFrame() {// 設置窗口標題this.setTitle("石子迷宮 V 1.0");// 設置窗口大小this.setSize(463, 543);// 設置窗口關閉方式this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 設置窗口的居中顯示this.setLocationRelativeTo(null);// 設置布局方式為絕對位置定位this.setLayout(null);}
}
4、判斷是否通關
? \bullet ?用戶每操作一步,需要立即判斷是否已經通關,如果通過,需要顯示勝利的標記。
完成代碼如下:
package com.itheima;import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;// 自定義窗口類,創建對象,展示一個主窗口。
public class MainFrame extends JFrame {// 設置圖片位置private static final String imagePath = "stone-maze/src/image/";// 準備一個數組,用戶存儲數字色塊的行列位置:4行4列private int[][] imageData = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12},{13, 14, 15, 0}};// 定義一個二維數組,用于存儲最終游戲成功的數據順序private int[][] winData = new int[][] {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12},{13, 14, 15, 0}};// 定義兩個整數變量記錄當前空白色塊的位置。private int row; // 當前空白色塊的行位置private int col; // 當前空白色塊的列位置public MainFrame() {// 1、調用一個初始化方法:初始化窗口大小等信息。initFrame();// 4、打亂數組色塊的順序,再提示圖片initRandomArray();// 2、初始化界面:展示數字色塊。initImage();// 3、初始化系統菜單:點擊彈出菜單信息是系統退出,重啟游戲。initMenu();// 5、給當前窗口綁定上下左右按鍵事件。initKeyPressEvent();// 設置窗口可見this.setVisible(true);}private void initKeyPressEvent() {// 給當前窗口綁定上下左右按鍵事件。this.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {// 判斷當前按鍵的編號int keyCode = e.getKeyCode();// 判斷按鍵編號,上下左右switch (keyCode) {case KeyEvent.VK_UP:switchAndMove(Direction.UP);// 用戶按下上鍵,把圖片上移。break;case KeyEvent.VK_DOWN:switchAndMove(Direction.DOWN);// 用戶按下下鍵,把圖片下移。break;case KeyEvent.VK_LEFT:switchAndMove(Direction.LEFT);// 用戶按下左鍵,把圖片左移。break;case KeyEvent.VK_RIGHT:switchAndMove(Direction.RIGHT);// 用戶按下右鍵,把圖片右移。break;}}});}// 控制數據交換和圖片移動private void switchAndMove(Direction r) {// 判斷圖片的方向,再控制圖片移動。switch (r) {case UP:// 上交換的條件是行必須 < 3,然后才開始交換。if (row < imageData.length - 1) {// 當前空白色塊位置:rol col// 需要被交換的位置:row + 1 colint temp = imageData[row][col];imageData[row][col] = imageData[row + 1][col];imageData[row + 1][col] = temp;// 更新當前空白色塊的位置。row++;}break;case DOWN:if (row > 0) {// 當前空白色塊位置:row col// 需要被交換的位置:row - 1 colint temp = imageData[row][col];imageData[row][col] = imageData[row - 1][col];imageData[row - 1][col] = temp;// 更新當前空白色塊的位置。row--;}break;case LEFT:// 左交換的條件是列必須 < 3,然后才開始交換。if (col < imageData.length - 1) {// 當前空白色塊位置:row col// 需要被交換的位置:row col + 1int temp = imageData[row][col];imageData[row][col] = imageData[row][col + 1];imageData[row][col + 1] = temp;// 更新當前空白色塊的位置。col++;}break;case RIGHT:if (col > 0) {int temp = imageData[row][col];imageData[row][col] = imageData[row][col - 1];imageData[row][col - 1] = temp;col--;}break;}// 重新刷新界面!!!initImage();}private void initRandomArray() {// 1、打亂數組for (int i = 0; i < imageData.length; i++) { // 遍歷行for (int j = 0; j < imageData[i].length; j++) { // 遍歷列// 隨機兩個行列位置,讓這兩個位置交換。int i1 = (int)(Math.random() * imageData.length);int j1 = (int)(Math.random() * imageData.length);int i2 = (int)(Math.random() * imageData.length);int j2 = (int)(Math.random() * imageData.length);int temp = imageData[i1][j1];imageData[i1][j1] = imageData[i2][j2];imageData[i2][j2] = temp;}}// 定義空白色塊的位置。// 去二維數組中遍歷每個數據,只要發現這個數據等于0,這個位置就是當前空白色塊的位置。OUT:for (int i = 0; i < imageData.length; i++) { // 遍歷行for (int j = 0; j < imageData[i].length; j++) { // 遍歷列if (imageData[i][j] == 0) {// 定位空白色塊的位置row = i;col = j;break OUT; // 跳出兩個for循環}}}}private void initMenu() {JMenuBar menuBar = new JMenuBar(); // 創建一個菜單欄JMenu menu = new JMenu("系統");JMenuItem exitJi = new JMenuItem("退出");menu.add(exitJi); // 添加子菜單exitJi.addActionListener(e -> { // 添加點擊事件// 退出游戲dispose();});JMenuItem restartJi = new JMenuItem("重啟");menu.add(restartJi);restartJi.addActionListener(e -> { // 添加點擊事件// 重啟游戲});menuBar.add(menu); // 添加到菜單欄中this.setJMenuBar(menuBar);}private void initImage() {// 先清空窗口上的全部圖層this.getContentPane().removeAll();// 判斷是否贏了if (isWin()) {// 展示勝利的圖片JLabel label = new JLabel(new ImageIcon(imagePath + "win.png"));label.setBounds(124, 230, 266, 88);this.add(label);}// 1、展示一個行列矩陣的圖片色塊依次鋪滿窗口(4 * 4)for (int i = 0; i < imageData.length; i++) { // 遍歷行for (int j = 0; j < imageData[i].length; j++) { // 遍歷列// 拿到圖片的名稱String imageName = imageData[i][j] + ".png";// 2、創建一個JLabel對象,設置圖片給他展示。JLabel label = new JLabel();// 3、設置圖片到label對象中去。label.setIcon(new ImageIcon(imagePath + imageName));// 4、設置數字色塊的位置label.setBounds(20 + j * 100, 60 + i * 100, 100, 100);// 5、把這個圖片展示到窗口中this.add(label);}}// 設置窗口的背景圖片JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));background.setBounds(0, 0, 450, 484);this.add(background);// 刷新新圖層,重新繪制窗口this.repaint();}private boolean isWin() {// 判斷是否贏了for (int i = 0; i < imageData.length; i++) { // 遍歷行for (int j = 0; j < imageData[i].length; j++) { // 遍歷列// 判斷當前遍歷到的數據是否和winData中的數據是否一致if (imageData[i][j] != winData[i][j]) {// 如果不一致,則返回falsereturn false;}}}// 如果一致,則返回true,則顯示勝利的圖片return true;}private void initFrame() {// 設置窗口標題this.setTitle("石子迷宮 V 1.0");// 設置窗口大小this.setSize(463, 543);// 設置窗口關閉方式this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 設置窗口的居中顯示this.setLocationRelativeTo(null);// 設置布局方式為絕對位置定位this.setLayout(null);}
}
5、統計移動步驟,重啟游戲
? \bullet ?每成功移動一步,都需要累加一次步數
? \bullet ?定義一個變量用于累加步數,并實時展示到界面上
6、拓展問題
? \bullet ?數字華容道的亂序操作,并不是可以隨意打亂的,必須滿足一定的算法去打亂順序,這樣才是有解的,才能讓玩家恢復到有序。有沒有簡單的算法???