黑马程序员Java基础9

------------java基础d9--1---项目实战2-游戏开发-数字华容道介绍---------------------------------------------

还是AI结合GUI;完成一个有趣点的项目。

准备环节:

  1. 创建一个模块用于开发石头迷阵游戏,模块名称为:stone-maze
  2. 导入项目需要的资源包到src目录下:主要是一些图片文件,
  3. 创建项目包:con.itheima.

------------java基础d9--2---项目实战2-游戏开发-游戏界面展示------------------------------------------------

1、创建石头迷阵的界面

  1. 定义主界面类,MainFrame继承JFrame。自定义窗口
  2. 初始化窗口大小
  3. 初始化界面图片
  4. 初始化界面菜单:系统退出,重启游戏。

PS:***JLabel label = new JLabel(icon);***可以把图片封装成一个对象;

然后***ImageIcon icon = new ImageIcon("图片地址" + imageData[i][j] + ".png");***就可以把图片给到jabel对象;;

***label.setBounds(j * 110, i * 110, 110, 110);***把这个label对象展示在某个位置。四个数据分别是X轴坐标、Y轴坐标、展示图片的宽度、展示图片的高度。

java 复制代码
package com.itheima;

import javax.swing.*;
//自定义窗口类,创建对象,展示一个主窗口
public class MainFrame extends JFrame {
    //2-3-1、设置图片位置
    private static final String imagePath = "stone-maze/src/image/";

    //2-1-1准备一个数组,用于存储数字色块的行列位置: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();
        //4、初始化系统的菜单,点击弹出菜单信息是系统退出、重启游戏。
        initMenu();

        //3、设置窗口的显示
        this.setVisible(true);
    }

    private void initMenu() {
        JMenuBar menuBar = new JMenuBar();          //创建一个菜单条
        JMenu menu = new JMenu("系统");           //创建一个菜单
//        menu.add(new JMenuItem("退出"));          //正常来说这是一个子菜单,但是要给子菜单加功能,所以要换个写法,给他搞个对象。
        //给子菜单添加一个选项,退出。
        JMenuItem exitJi = new JMenuItem("退出");
        menu.add(exitJi);                           //给子菜单添加功能
        exitJi.addActionListener(e -> {     //监听点击事件
            dispose();                              //对象exitJi的功能:销毁窗口
        });
        //给子菜单添加一个选项,重启
        JMenuItem restartJi = new JMenuItem("重启");
        menu.add(restartJi);
        restartJi.addActionListener(e -> {
            //功能:重启游戏
//            new MainFrame();
//            dispose();
        });
        //现在把子菜单添加到菜单条中
        menuBar.add(menu);
        //把菜单条添加到窗口中
        this.setJMenuBar(menuBar);
    }

    private void initImage() {
        //2-1、展示一个行列矩阵的图片,依次铺满窗口(4 * 4);
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                //2-2拿到图片名称
                String imageName = imageData[i][j] + ".png";
                //2-4、创建一个标签对象,把图片设置到标签上
                JLabel label = new JLabel();
                //2-3、创建一个数字色块对象
//                label.setIcon(new ImageIcon("stone-maze/src/image/" + imageName));      //因为这里的路径写死了,以后如果项目名更改之类的,不方便
                label.setIcon(new ImageIcon(imagePath + imageName));      //所以最上边写了常量路径。

                //2-5、设置标签的位置
                label.setBounds(22+j * 100, 60+i * 100, 100, 100);
                //2-6、把图片添加到窗口中
                this.add(label);
            }
        }
        //2-7、设置窗口的背景图片
        JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));
        background.setBounds(0, 0, 450, 484);
        this.add(background);
    }

    private void initFrame() {
        //设置窗口的标题
        this.setTitle("石头迷宫 V1.0 ybz");
        //设置窗口的宽高
        this.setSize(465, 555);
        //设置窗口的关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口居中显示
        this.setLocationRelativeTo(null);
        //设置窗口的绝对布局
        this.setLayout(null);
//        //设置窗口的显示
//        this.setVisible(true);  //因为发现图片不显示,所以要先布局好图片后,最后再显示窗口。这行显示代码搞到无参构造器了。
    }

    public static void main(String[] args) {
        new MainFrame();
    }
}

------------java基础d9--3---项目实战2-游戏开发-打乱游戏的界面顺序---------------------------------------

2、打乱顺序

打乱界面的图片顺序,让游戏具备可玩性:使用方法如下:

打乱二维数组中的元素顺序:initRandomArray()

java 复制代码
        //5、打乱二维数组中的数字
    private void initRandomArray() {
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                int random = (int)(Math.random() * 16);
                int temp = imageData[i][j];
                imageData[i][j] = imageData[random / 4][random % 4];
                imageData[random / 4][random % 4] = temp;
            }
        }
    }

先打乱然后再显示图片:

java 复制代码
    public MainFrame(){
        //1、调用一个初始化方法,初始化窗口大小等信息。
        initFrame();
        //5、打乱数组色块的顺序,再展示图片
        initRandomArray();
        //2、初始化界面,展示数字色块、背景图片
        initImage();
        //4、初始化系统的菜单,点击弹出菜单信息是系统退出、重启游戏。
        initMenu();

        //3、设置窗口的显示
        this.setVisible(true);
    }

------------java基础d9--4---项目实战2-游戏开发-上下左右的事件监听---------------------------------------

3、控制上下左右移动

  1. 给窗口绑定上下左右按键事件
  2. 控制位置的交换
  3. 重新绘制主界面的内容。
java 复制代码
    public MainFrame(){
        //1、调用一个初始化方法,初始化窗口大小等信息。
        initFrame();
        //5、打乱数组色块的顺序,再展示图片
        initRandomArray();
        //2、初始化界面,展示数字色块、背景图片
        initImage();
        //4、初始化系统的菜单,点击弹出菜单信息是系统退出、重启游戏。
        initMenu();
        //6、给当前窗口绑定上下左右按键事件
        initKeyPressEvent();
        //3、设置窗口的显示
        this.setVisible(true);
    }
java 复制代码
    //6、给当前窗口绑定上下左右按键事件
    private void initKeyPressEvent() {
        this.addKeyListener(new KeyAdapter() {
            @Override
            public 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;
                    default:
                        System.out.println("ct运势");
                        break;
                }
            }
        });
    }
    //6-2、控制图片移动、、、业务和功能分开。上变只负责监听,这里只负责做动作。
    private void switchAndMove(Direction r) {
        //控制图片移动
        //想用枚举来获取按键的编号
        //判断图片的方向,再控制图片移动
        switch (r) {
            case UP:
                System.out.println("上");
                break;
            case DOWN:
                System.out.println("下");
                break;
                case Direction.LEFT:
                System.out.println("左");
                break;
                case Direction.RIGHT:
                System.out.println("右");
                break;
        }
    }
java 复制代码
package com.itheima;

public enum Direction {
    UP, DOWN, LEFT, RIGHT;
}

------------java基础d9--5---项目实战2-游戏开发-界面移动控制------------------------------------------------

3、控制上下左右移动

  1. 给窗口绑定上下左右按键事件
  2. 控制位置的交换
    1. 定位当前恐怖色块的位置
    2. 根据用户点击的方位确定交换哪个数据,到数组中去交换
  3. 重新绘制主界面的内容。
    1. 让主界面按照二维数组的最新内容刷新界面。
java 复制代码
    private void switchAndMove(Direction r) {
        //控制图片移动
        //想用枚举来获取按键的编号
        //判断图片的方向,再控制图片移动
        switch (r) {
            case UP:
            //上交换的条件是:行>0,才开始交换
            if (rou > 0) {
                //当前空白色块位置:rou, col
                int temp = imageData[rou - 1][col];
                imageData[rou - 1][col] = 0;
                imageData[rou][col] = temp;
                rou--;
            }
            System.out.println("上");
            break;
            case DOWN:
                //下交换的条件是:行<3,才开始交换
                if (rou < imageData.length - 1) {
                    //当前空白色块位置:rou, col
                    int temp = imageData[rou + 1][col];
                    imageData[rou + 1][col] = 0;
                    imageData[rou][col] = temp;
                    rou++;
                }
                System.out.println("下");
                break;
            case Direction.LEFT:
                //左交换的条件是:列>0,才开始交换
                if (col > 0) {
                    int temp = imageData[rou][col - 1];
                    imageData[rou][col - 1] = 0;
                    imageData[rou][col] = temp;
                    col--;
                }
                System.out.println("左");
                break;
            case Direction.RIGHT:
                //右交换的条件是:列<3,才开始交换
                if (col < imageData[0].length - 1) {
                    int temp = imageData[rou][col + 1];
                    imageData[rou][col + 1] = 0;
                    imageData[rou][col] = temp;
                    col++;
                }
                System.out.println("右");
                break;
        }
        initImage();        //直接这样有bug;因为图片生成在了界面的底下,我们看不到。所以要更新一下initImage的代码。
    }
java 复制代码
//更新
private void initImage() {
        //先清空窗口上的全部图层,再重新生成图片
        this.getContentPane().removeAll();      //this就是指这个窗口;getContentPance是指窗口这个容器,默认有这个桌布;removeAll()是清空;合起来就是情况图层的图片。
        //2-1、展示一个行列矩阵的图片,依次铺满窗口(4 * 4);
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                //2-2拿到图片名称
                String imageName = imageData[i][j] + ".png";
                //2-4、创建一个标签对象,把图片设置到标签上
                JLabel label = new JLabel();
                //2-3、创建一个数字色块对象
//                label.setIcon(new ImageIcon("stone-maze/src/image/" + imageName));      //因为这里的路径写死了,以后如果项目名更改之类的,不方便
                label.setIcon(new ImageIcon(imagePath + imageName));      //所以最上边写了常量路径。

                //2-5、设置标签的位置
                label.setBounds(22+j * 100, 60+i * 100, 100, 100);
                //2-6、把图片添加到窗口中
                this.add(label);
            }
        }
        //2-7、设置窗口的背景图片
        JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));
        background.setBounds(0, 0, 450, 484);
        this.add(background);
        //刷新图层重新绘制;
        this.repaint();
    }

------------java基础d9--6---项目实战2-游戏开发-胜利判断、游戏演示---------------------------------------

4、判断是否通过

  1. 用户每操作一次,需要立即判断是否已经通关,如果通过,需要显示胜利的标记。
java 复制代码
        //只要刷新界面,就判断一下是否赢了。
        if (isWin()) {
            JLabel label = new JLabel(new ImageIcon(imagePath + "win.png"));
            label.setBounds(124, 230, 266, 88);
            this.add(label);
        }

//加在了这个代码后边。
    private void initImage() {
        //先清空窗口上的全部图层,再重新生成图片
        this.getContentPane().removeAll();      //this就是指这个窗口;getContentPance是指窗口这个容器,默认有这个桌布;removeAll()是清空;合起来就是情况图层的图片。
        //只要刷新界面,就判断一下是否赢了。
//
java 复制代码
    private boolean isWin() {
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                if(imageData[i][j] != imageData1[i][j]){
                    return false;
                }
            }
        }
        return true;
    }

------------java基础d9--7---项目实战2-游戏开发-重启-步数-乱序问题------------------------------------------

5、统计移动步骤、重启游戏

  1. 每成功移动一步,都需要累加一次步数。
  2. 定义一个遍历用于累加步数,并实时展示到界面上。

重启游戏:

就是重新打乱一下二维数组,然后再刷新一下界面就好了。

java 复制代码
package com.itheima;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

//自定义窗口类,创建对象,展示一个主窗口
public class MainFrame extends JFrame {
    //2-3-1、设置图片位置
    private static final String imagePath = "stone-maze/src/image/";

    //2-1-1准备一个数组,用于存储数字色块的行列位置:4行4列;
    private int[][] imageData = {
            {1,2,3,4},
            {5,6,7,8},
            {9,10,11,12},
            {13,14,15,0}
    };
    private final int[][] imageData1 = {
            {1,2,3,4},
            {5,6,7,8},
            {9,10,11,12},
            {13,14,15,0}
    };

    //6-3 定义两个整数记录0所在的位置;
    private int rou, col;       //记录数字0所在的行列。
    private int count;          //统计移动的步数

    public MainFrame(){
        //1、调用一个初始化方法,初始化窗口大小等信息。
        initFrame();
        //5、打乱数组色块的顺序,再展示图片
        initRandomArray();
        //2、初始化界面,展示数字色块、背景图片
        initImage();
        //4、初始化系统的菜单,点击弹出菜单信息是系统退出、重启游戏。
        initMenu();
        //6、给当前窗口绑定上下左右按键事件
        initKeyPressEvent();
        //3、设置窗口的显示
        this.setVisible(true);
    }

    //6、给当前窗口绑定上下左右按键事件
    private void initKeyPressEvent() {
        this.addKeyListener(new KeyAdapter() {
            @Override
            public 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;
                    case KeyEvent.VK_SPACE:
                        switchAndMove(Direction.SPACE);
                        break;
                    default:
                        System.out.println("ct运势");
                        break;
                }
            }
        });
    }
    //6-2、控制图片移动、、、业务和功能分开。上变只负责监听,这里只负责做动作。
    private void switchAndMove(Direction r) {
        //控制图片移动
        //想用枚举来获取按键的编号
        //判断图片的方向,再控制图片移动
        switch (r) {
            case UP:
                //上交换的条件是:行>0,才开始交换
                if (rou > 0) {
                    //当前空白色块位置:rou, col
                    int temp = imageData[rou - 1][col];
                    imageData[rou - 1][col] = 0;
                    imageData[rou][col] = temp;
                    rou--;
                    count++;
                }
                System.out.println("上");
                break;
            case DOWN:
                //下交换的条件是:行<3,才开始交换
                if (rou < imageData.length - 1) {
                    //当前空白色块位置:rou, col
                    int temp = imageData[rou + 1][col];
                    imageData[rou + 1][col] = 0;
                    imageData[rou][col] = temp;
                    rou++;
                    count++;
                }
                System.out.println("下");
                break;
            case Direction.LEFT:
                //左交换的条件是:列>0,才开始交换
                if (col > 0) {
                    int temp = imageData[rou][col - 1];
                    imageData[rou][col - 1] = 0;
                    imageData[rou][col] = temp;
                    col--;
                    count++;
                }
                System.out.println("左");
                break;
            case Direction.RIGHT:
                //右交换的条件是:列<3,才开始交换
                if (col < imageData[0].length - 1) {
                    int temp = imageData[rou][col + 1];
                    imageData[rou][col + 1] = 0;
                    imageData[rou][col] = temp;
                    col++;
                    count++;
                }
                System.out.println("右");
                break;
            case Direction.SPACE:
                imageData = imageData1;
                initZhao0();
                initImage();
                break;
        }
        initImage();        //直接这样有bug;因为图片生成在了界面的底下,我们看不到。所以要更新一下initImage的代码。
    }

    //5、打乱二维数组中的数字
    private void initRandomArray() {
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                int random = (int)(Math.random() * 16);
                int temp = imageData[i][j];
                imageData[i][j] = imageData[random / 4][random % 4];
                imageData[random / 4][random % 4] = temp;
            }
        }
        //找到数字零,直接把最后一个数字和0换位置。
        initZhao0();
    }

    private void initZhao0() {
        //找到0的行和列
        OUT:        //随便定义什么名字,一般叫 OUT。
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                if (imageData[i][j] == 0) {
                    //定位到空白色块的位置
                    rou = i;
                    col = j;
//                    break;      //只是结束了里面的for循环
                    break OUT;  //结束OUT循环(即这两个for循环)。
                }
            }
        }
    }

    //4、初始化系统的菜单,点击弹出菜单信息是系统退出、重启游戏。
    private void initMenu() {
        JMenuBar menuBar = new JMenuBar();          //创建一个菜单条
        JMenu menu = new JMenu("系统");           //创建一个菜单
//        menu.add(new JMenuItem("退出"));          //正常来说这是一个子菜单,但是要给子菜单加功能,所以要换个写法,给他搞个对象。
        //给子菜单添加一个选项,退出。
        JMenuItem exitJi = new JMenuItem("退出");
        menu.add(exitJi);                           //给子菜单添加功能
        exitJi.addActionListener(e -> {     //监听点击事件
            dispose();                              //对象exitJi的功能:销毁窗口
        });
        //给子菜单添加一个选项,重启
        JMenuItem restartJi = new JMenuItem("重启");
        menu.add(restartJi);
        restartJi.addActionListener(e -> {
            //功能:重启游戏
            initRandomArray();      //重新打乱数组
            initImage();            //重新生成图片
        });
        //现在把子菜单添加到菜单条中
        menuBar.add(menu);
        //把菜单条添加到窗口中
        this.setJMenuBar(menuBar);
    }

    //2、初始化界面,展示数字色块、背景图片
    private void initImage() {
        //先清空窗口上的全部图层,再重新生成图片
        this.getContentPane().removeAll();      //this就是指这个窗口;getContentPance是指窗口这个容器,默认有这个桌布;removeAll()是清空;合起来就是情况图层的图片。
        //刷新界面的时候,显示步数:
        //给窗口添加一个展示文字的组件:
        JLabel countTxt = new JLabel("当前移动" + count + "步");
        countTxt.setBounds(20, 20, 150, 20);
        //设置文字颜色为红色
        countTxt.setForeground(Color.red);
        countTxt.setFont(new Font("楷体", Font.BOLD, 16));
        this.add(countTxt);
        //只要刷新界面,就判断一下是否赢了。
        if (isWin()) {
            JLabel label = new JLabel(new ImageIcon(imagePath + "win.png"));
            label.setBounds(124, 230, 266, 88);
            this.add(label);
        }
        //2-1、展示一个行列矩阵的图片,依次铺满窗口(4 * 4);
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                //2-2拿到图片名称
                String imageName = imageData[i][j] + ".png";
                //2-4、创建一个标签对象,把图片设置到标签上
                JLabel label = new JLabel();
                //2-3、创建一个数字色块对象
//                label.setIcon(new ImageIcon("stone-maze/src/image/" + imageName));      //因为这里的路径写死了,以后如果项目名更改之类的,不方便
                label.setIcon(new ImageIcon(imagePath + imageName));      //所以最上边写了常量路径。

                //2-5、设置标签的位置
                label.setBounds(22+j * 100, 60+i * 100, 100, 100);
                //2-6、把图片添加到窗口中
                this.add(label);
            }
        }
        //2-7、设置窗口的背景图片
        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++) {
                if(imageData[i][j] != imageData1[i][j]){
                    return false;
                }
            }
        }
        return true;
    }


    //1、初始化窗口大小等信息。
    private void initFrame() {
        //设置窗口的标题
        this.setTitle("石头迷宫 V1.0 ybz");
        //设置窗口的宽高
        this.setSize(465, 555);
        //设置窗口的关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口居中显示
        this.setLocationRelativeTo(null);
        //设置窗口的绝对布局
        this.setLayout(null);
//        //设置窗口的显示
//        this.setVisible(true);  //因为发现图片不显示,所以要先布局好图片后,最后再显示窗口。这行显示代码搞到无参构造器了。
    }

    public static void main(String[] args) {
        new MainFrame();
    }
}

自己加了一个按空格就直接恢复正确顺序。

16分钟,还有一个算法问题,随机打乱顺序可能有无法复原的情况。

大概原理就是让电脑循环一定次数,电脑来直接模拟人的操作来进行打乱,把原先的打乱模式去掉。

总代码:

java 复制代码
package com.itheima;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

//自定义窗口类,创建对象,展示一个主窗口
public class MainFrame extends JFrame {
    //2-3-1、设置图片位置
    private static final String imagePath = "stone-maze/src/image/";

    //2-1-1准备一个数组,用于存储数字色块的行列位置:4行4列;
    private int[][] imageData = {
            {1,2,3,4},
            {5,6,7,8},
            {9,10,11,12},
            {13,14,15,0}
    };
    private final int[][] imageData1 = {
            {1,2,3,4},
            {5,6,7,8},
            {9,10,11,12},
            {13,14,15,0}
    };

    //6-3 定义两个整数记录0所在的位置;
    private int rou, col;       //记录数字0所在的行列。
    private int count;          //统计移动的步数

    public MainFrame(){
        //1、调用一个初始化方法,初始化窗口大小等信息。
        initFrame();
        //5、打乱数组色块的顺序,再展示图片
        initRandomArray();
        //2、初始化界面,展示数字色块、背景图片
        initImage();
        //4、初始化系统的菜单,点击弹出菜单信息是系统退出、重启游戏。
        initMenu();
        //6、给当前窗口绑定上下左右按键事件
        initKeyPressEvent();
        //3、设置窗口的显示
        this.setVisible(true);
    }

    //6、给当前窗口绑定上下左右按键事件
    private void initKeyPressEvent() {
        this.addKeyListener(new KeyAdapter() {
            @Override
            public 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;
                    case KeyEvent.VK_SPACE:
                        switchAndMove(Direction.SPACE);
                        break;
                    default:
                        System.out.println("ct运势");
                        break;
                }
            }
        });
    }
    //6-2、控制图片移动、、、业务和功能分开。上变只负责监听,这里只负责做动作。
    private void switchAndMove(Direction r) {
        //控制图片移动
        //想用枚举来获取按键的编号
        //判断图片的方向,再控制图片移动
        switch (r) {
            case UP:
                //上交换的条件是:行>0,才开始交换
                if (rou > 0) {
                    //当前空白色块位置:rou, col
                    int temp = imageData[rou - 1][col];
                    imageData[rou - 1][col] = 0;
                    imageData[rou][col] = temp;
                    rou--;
                    count++;
                }
                System.out.println("上");
                break;
            case DOWN:
                //下交换的条件是:行<3,才开始交换
                if (rou < imageData.length - 1) {
                    //当前空白色块位置:rou, col
                    int temp = imageData[rou + 1][col];
                    imageData[rou + 1][col] = 0;
                    imageData[rou][col] = temp;
                    rou++;
                    count++;
                }
                System.out.println("下");
                break;
            case Direction.LEFT:
                //左交换的条件是:列>0,才开始交换
                if (col > 0) {
                    int temp = imageData[rou][col - 1];
                    imageData[rou][col - 1] = 0;
                    imageData[rou][col] = temp;
                    col--;
                    count++;
                }
                System.out.println("左");
                break;
            case Direction.RIGHT:
                //右交换的条件是:列<3,才开始交换
                if (col < imageData[0].length - 1) {
                    int temp = imageData[rou][col + 1];
                    imageData[rou][col + 1] = 0;
                    imageData[rou][col] = temp;
                    col++;
                    count++;
                }
                System.out.println("右");
                break;
            case Direction.SPACE:
                for (int i = 0; i < imageData.length; i++) {
                    for (int j = 0; j < imageData[i].length; j++) {
                        imageData[i][j] = imageData1[i][j];
                    }
                }
                initZhao0();
                initImage();
                break;
        }
        initImage();        //直接这样有bug;因为图片生成在了界面的底下,我们看不到。所以要更新一下initImage的代码。
    }

    //5、打乱二维数组中的数字
    private void initRandomArray() {
        //这种方式可能会导致无法复原,所以搞另一种打乱方式:
//        for (int i = 0; i < imageData.length; i++) {
//            for (int j = 0; j < imageData[i].length; j++) {
//                int random = (int)(Math.random() * 16);
//                int temp = imageData[i][j];
//                imageData[i][j] = imageData[random / 4][random % 4];
//                imageData[random / 4][random % 4] = temp;
//            }
//        }
        //找到数字零,直接把最后一个数字和0换位置。
        initZhao0();
        //生成一个10的随机数
        int random = (int)(Math.random() * 100);
        random = random + 200;
        for (int i = 0; i < random; i++) {
            //生成一个4的随机数
            int random1 = (int)(Math.random() * 4);
            switch (random1) {
                case 0:
                    switchAndMove(Direction.UP);
                    break;
                case 1:
                    switchAndMove(Direction.DOWN);
                    break;
                case 2:
                    switchAndMove(Direction.LEFT);
                    break;
                case 3:
                    switchAndMove(Direction.RIGHT);
                    break;
            }
        }
        count = 0;
    }

    private void initZhao0() {
        //找到0的行和列
        OUT:        //随便定义什么名字,一般叫 OUT。
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                if (imageData[i][j] == 0) {
                    //定位到空白色块的位置
                    rou = i;
                    col = j;
//                    break;      //只是结束了里面的for循环
                    break OUT;  //结束OUT循环(即这两个for循环)。
                }
            }
        }
    }

    //4、初始化系统的菜单,点击弹出菜单信息是系统退出、重启游戏。
    private void initMenu() {
        JMenuBar menuBar = new JMenuBar();          //创建一个菜单条
        JMenu menu = new JMenu("系统");           //创建一个菜单
//        menu.add(new JMenuItem("退出"));          //正常来说这是一个子菜单,但是要给子菜单加功能,所以要换个写法,给他搞个对象。
        //给子菜单添加一个选项,退出。
        JMenuItem exitJi = new JMenuItem("退出");
        menu.add(exitJi);                           //给子菜单添加功能
        exitJi.addActionListener(e -> {     //监听点击事件
            dispose();                              //对象exitJi的功能:销毁窗口
        });
        //给子菜单添加一个选项,重启
        JMenuItem restartJi = new JMenuItem("重启");
        menu.add(restartJi);
        restartJi.addActionListener(e -> {
            //功能:重启游戏
            initRandomArray();      //重新打乱数组
            initImage();            //重新生成图片
        });
        //现在把子菜单添加到菜单条中
        menuBar.add(menu);
        //把菜单条添加到窗口中
        this.setJMenuBar(menuBar);
    }

    //2、初始化界面,展示数字色块、背景图片
    private void initImage() {
        //先清空窗口上的全部图层,再重新生成图片
        this.getContentPane().removeAll();      //this就是指这个窗口;getContentPance是指窗口这个容器,默认有这个桌布;removeAll()是清空;合起来就是情况图层的图片。
        //刷新界面的时候,显示步数:
        //给窗口添加一个展示文字的组件:
        JLabel countTxt = new JLabel("当前移动" + count + "步");
        countTxt.setBounds(20, 20, 150, 20);
        //设置文字颜色为红色
        countTxt.setForeground(Color.red);
        countTxt.setFont(new Font("楷体", Font.BOLD, 16));
        this.add(countTxt);
        //只要刷新界面,就判断一下是否赢了。
        if (isWin()) {
            JLabel label = new JLabel(new ImageIcon(imagePath + "win.png"));
            label.setBounds(124, 230, 266, 88);
            this.add(label);
        }
        //2-1、展示一个行列矩阵的图片,依次铺满窗口(4 * 4);
        for (int i = 0; i < imageData.length; i++) {
            for (int j = 0; j < imageData[i].length; j++) {
                //2-2拿到图片名称
                String imageName = imageData[i][j] + ".png";
                //2-4、创建一个标签对象,把图片设置到标签上
                JLabel label = new JLabel();
                //2-3、创建一个数字色块对象
//                label.setIcon(new ImageIcon("stone-maze/src/image/" + imageName));      //因为这里的路径写死了,以后如果项目名更改之类的,不方便
                label.setIcon(new ImageIcon(imagePath + imageName));      //所以最上边写了常量路径。

                //2-5、设置标签的位置
                label.setBounds(22+j * 100, 60+i * 100, 100, 100);
                //2-6、把图片添加到窗口中
                this.add(label);
            }
        }
        //2-7、设置窗口的背景图片
        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++) {
                if(imageData[i][j] != imageData1[i][j]){
                    return false;
                }
            }
        }
        return true;
    }


    //1、初始化窗口大小等信息。
    private void initFrame() {
        //设置窗口的标题
        this.setTitle("石头迷宫 V1.0 ybz");
        //设置窗口的宽高
        this.setSize(465, 555);
        //设置窗口的关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口居中显示
        this.setLocationRelativeTo(null);
        //设置窗口的绝对布局
        this.setLayout(null);
//        //设置窗口的显示
//        this.setVisible(true);  //因为发现图片不显示,所以要先布局好图片后,最后再显示窗口。这行显示代码搞到无参构造器了。
    }

    public static void main(String[] args) {
        new MainFrame();
    }
}
相关推荐
山沐与山2 小时前
【Redis】Redis集群模式架构详解
java·redis·架构
ss2732 小时前
Java并发编程:DelayQueue延迟订单系统
java·python·算法
wcy_10112 小时前
七大软件设计原则
java·设计规范
invicinble2 小时前
jar包在执行的时候需要关注的细节(提供一个解构jvm问题的视角)
java·jvm·jar
@游子2 小时前
Python类属性与魔术方法全解析
开发语言·python
麦芽糖02192 小时前
SSE介绍及使用(Server-Send Events)
java
alan07212 小时前
【Java + Elasticsearch全量 & 增量同步实战】
java·elasticsearch·jenkins
hashiqimiya2 小时前
后端springboot的接收前端发来的数据反序列化原理
java
眠りたいです3 小时前
现代C++:C++11并发支持库
开发语言·c++·多线程·c++11·c++并发支持库