基于JAVA的GUI编程的的迷宫游戏 2020-12-15

本文档详细介绍了如何设计一个基于Java的迷宫游戏,其中包括使用二叉树深度优先遍历的逆推来替代A*寻路算法,创建子方格对象类,迷宫Maze类的设计,以及主函数的实现。游戏支持玩家操作和系统寻路,具备图形界面和随机生成迷宫的功能。玩家可以通过键盘控制移动,按回车键显示最优路径。此外,还涉及到了图形界面设计、按键事件监听和迷宫生成的算法细节。
摘要由CSDN通过智能技术生成

一级目录

二级目录

三级目录

实验要求:

1、迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;

2、要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于A*算法实现,输出走迷宫的最优路径并显示。

3、设计交互友好的游戏图形界面。

程序设计工具:

  1、语言:JAVA
  2、开发环境:IntelliJ IDEA Community Edition 2020

实验设计准备:

1、A*寻路算法:

一个比较好的参考链接如下:
A*寻路算法参考链接
但是有点整不明白这个算法到底怎么实现,有点细节感觉太麻烦了,既然不用考虑效率之类的问题,我就还是觉得用二叉树的深度优先遍历的逆推来做

2、二叉树的深度优先遍历的逆推:

二叉树的深度优先遍历的逆推主要分为:

  1. 生成树阶段:从起点向所有可能的方向生长出所有的树枝。起点作为根节点;
  2. 保存相关信息和标识位,将每个点的上一个父节点记忆住;
  3. 使用阶段:当我们查询根节点到某点路径,就是查询某点到根节点的路径,只要查出这个节点的父节点,由于父节点的唯一性,可以最后查到根节点位置,我们描绘出路径信息就找出了通路,

https://blog.csdn.net/weixin_43790276/article/details/105473527

2、基本图形界面设计:

图形界面设计主要包含awt包和swing包的内容;
参考我的另一篇博文:实验准备篇
https://blog.csdn.net/weixin_43894818/article/details/111059084

实验内容:

主要设计思想:

  1. 图形界面以及各类控件的设置;
  2. 随机地图的生成,避免生成连通图;
  3. 寻路算法:在当前随机地图中生成二叉树,由父节点唯一性的原理,进行逆二叉树查询根节点,对路径上的方格对象修改标志位。

一、子方格对象类的创建:

我们需要把大的窗口分割为n×n个子方块,对方块类创建的的方格对象进行操作,使得各子方块具有更多的方法和属性,就要用子方块类创建子方块对象,并设计相关的方法,获取和修改各子方块对象的属性。
1、网格类grid的创建以及其构造函数的,坐标变量,标志位,以及父节点对象的初始设置;

class grid {
    static final int InTree = 1;
    static final int NotIntree = 0;
    private int x = -1;
    private int y = -1;
    private int flag = NotIntree;                //是否有  的标志位;
    private grid father = null;
    public grid(int xx, int yy) {             //构造函数;
        x = xx;
        y = yy;
    }
 

2、比如基本的坐标获取方法:

public int getX() {                          //获取该方格的横坐标;(由于不可直接用对象访问私有变量);
        return x;
    }
    public int getY() {                          //获取纵坐标;
        return y;
    }

3、标志位的获取和修改方法;

 public int getFlag() {                       //获取标志位;
        return flag;
    }
 public void setFlag(int f) {
        flag = f;                                //修改标志位;
    }

4、父节点对象的获取和修改;

   public grid getFather() {                 //获取方格对象的父节点;
        return father;
    }
    public void setFather(grid f) {           //修改方格对象的父节点;
        father = f;
    }

二、迷宫Maze类的设计;

1、类的声明以及基本变量设置;

1、创建迷宫类继承 JPanel类;
2、关于迷宫窗口的长宽、方格数目,边距宽度。

public class Maze extends JPanel {
  private  static final long serialVersionUID = -8300339045454852626L;
    private int NUM, width, padding;            // NUM为界面总边长,width为每个格子的边长;padding为内边距;

3、将整个窗口定义位一个网格类二维矩阵;设置相关变量;

  private grid[][] maze;
    private int myX, myY;                       //定义了两个点;
    int sum = 0;                                //记录步数;
    int rand = 0;                               //记录关数;
    private boolean drawPath = false;           //路径标志位;

2、迷宫类的构造函数:

建立二重循环,对二维矩阵的迷宫内部按照方格分别定义n×n个网格对象;
后续调用创建迷宫和按键监视器得操作得方法。

 Maze(int n, int w, int p) {                //Maze的构造方法;
        NUM = n;                                //窗口边长;
        width = w;                             //子方格边长;
        padding = p;
        maze = new grid[NUM][NUM];           //调用Lattice的构造函数,对尾端的小方格构造;
        for (int i = 0; i <= NUM - 1; i++)      //对除最后一格外的每一个坐标认定为一个小方格对象并构造;
            for (int j = 0; j <= NUM - 1; j++)
                maze[i][j] = new grid(i, j); //每个在窗口内具有整数坐标的点被视为一个方格对象;
        createMaze();
        setKeyListener();
        this.setFocusable(true);
    }

3、迷宫得默认构造方法:

1、对每个子方格对象的构造,通过循环,设置每个子方格对象的父节点位null,标志位为非入树的空状态;
2、设置初始的位置坐标(myX,myY);
3、将路径标识位也置为false状态;
4、设置控件获取焦点;

 private void init() {                              //定义一个私有的默认构造函数;
      //  String name = JOptionPane.showInputDialog(null, "请输入:\n", "title", JOptionPane.PLAIN_MESSAGE);

        for (int i = 0; i <= NUM - 1; i++)
            for (int j = 0; j <= NUM - 1; j++) {
                maze[i][j].setFather(null);              //设置每个小方格的父节点为null;
                maze[i][j].setFlag(grid.NotIntree);   //设置每个方格的标志位;
            }
        myX = 0;
        myY = 0;
        drawPath = false;                                //路径绘制标识;
        createMaze();
        this.setFocusable(true);                         //使能控件获得焦点能力
        repaint();                                       //重新绘制;
    }


5、根据子网格对象的坐标获取和直接由对象获取坐标的信息的四个方法:
(由于私有变量只能通过对象调用函数访问)

    public int getCenterX(int x) {
        return padding + x * width + width / 2;           //内边距属性;第x+1位的小方格中心点横坐标位置;
    }
    public int getCenterY(int y) {
        return padding + y * width + width / 2;           //内边距属性;第y+1位的小方格中心点纵坐标位置;
    }
    public int getCenterX(grid p) {
        return padding + p.getY() * width + width / 2;     //得到方格对象P的第x+1位的小方格中心点横坐标位置;
    }
    public int getCenterY(grid p) {
        return padding + p.getX() * width + width / 2;      //得到方格对象P的第x+1位的小方格中心点纵坐标位置;
    }

4、通过检测以及反应操作;

1、检测:如果当前对象位置(myX,myY)对于最后方格对象位置,则通过;
2、反馈:弹出消息框显示通关并显示共走了多少步;
3、弹出选项框:询问是否继续游戏
(1)继续游戏,则再次调用构造函数(重新开始);
(2)退出游戏则弹出本次游戏一共完成了多少关;

 private void checkIsWin() {
        if (myX == NUM - 1 && myY == NUM - 1) {          //如果当前方格位置坐标对于末尾方格的坐标;
           // JOptionPane.showMessageDialog(null, "你成功的走出了迷宫 !"+"你一共用了"+sum+"步",
           //         "恭喜过关。",JOptionPane.WARNING_MESSAGE);
            JOptionPane.showMessageDialog(null, "你成功的走出了迷宫 !"+"你一共走了"+sum+"步");
            rand++;
            sum = 0;
          //  Object[] options ={ "继续", "退出" };  //自定义按钮上的文字
            int m = JOptionPane.showConfirmDialog(null, "你已经通过"+rand+"关,是否要继续闯关?", "是否继续",JOptionPane.YES_NO_OPTION); //返回值为0或1
           // int m = JOptionPane.showOptionDialog(null, "你是否要继续闯关?", "第"+rand+"关",JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
           if(m == 0) {
               init();
           }
           if(m == 1){
               JOptionPane.showMessageDialog(null, "本次你一共通过了 !"+rand+"关,下次继续挑战");
           }
        }
    }

5、按键的绑定以及对应操作:

对控件上、下、左、右、回车键对应操作的设置;
主要是上下左右对目标对象的坐标更改和回车键对应的路径显示开关(标志位)的设置;
越界检测,如果越界了不做操作;

synchronized private void move(int c ) {
        int tx = myX, ty = myY;
        switch (c) {
            case KeyEvent.VK_LEFT :                      //监控按键,对应按键有对应的当前位置坐标的修改;
                ty--;
                sum++;
              // g.drawLine(myX,myY,tx,ty);
                break;
            case KeyEvent.VK_RIGHT :
                ty++;
                sum++;
                break;
            case KeyEvent.VK_UP :
                tx--;
                sum++;
                break;
            case KeyEvent.VK_DOWN :
                tx++;
                sum++;
                break;
            case KeyEvent.VK_ENTER:                       //如果是空格键;
                if (drawPath == true) {                   //如果路径绘制标识符为1,就置0;
                    drawPath = false;
                } else {
                    drawPath = true;                       //否则就把路径标识符置1;
                }
                break;
            default :
        }
if (!isOutOfBorder(tx, ty) && (maze[tx][ty].getFather() == maze[myX][myY]
                || maze[myX][myY].getFather() == maze[tx][ty])) {             //是否越界判断;
            myX = tx;                                                         //越界则不动;
            myY = ty;
        }
    }

6、键盘事件监视器

1、创建键盘事件对象,通过对象调用move方法,和绘制方法,以及获胜检测方法;
2、分别创建用对象和坐标调用的越界检查方法;

 private void setKeyListener() {                              //设置按键监视器;
        this.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {                  //按键事件;
                int c = e.getKeyCode();                           //获取当前按键信息;
                move(c);                                          //根据按键信息继续坐标操作;
                repaint();                                                   //将操作后的信息修正展示;
                checkIsWin();                                                //检测是否赢了;
            }
        });
    }
    private boolean isOutOfBorder(grid p) {               //子方格对象调用的越界判断;
        return isOutOfBorder(p.getX(), p.getY());
    }
    private boolean isOutOfBorder(int x, int y) {            //坐标调用的越界判断;
        return (x > NUM - 1 || y > NUM - 1 || x < 0 || y < 0) ? true : false;
    }

7、对网格的按规则随机遍历

按照左右上下的遍历规则,随机生成【0,3】四个数中的任一个,代入规则中,对网格进行遍历操作;并只对坐标未越界的子网格对象进行操作;

private grid[] getNeis(grid p) {
        final int[] adds = {-1, 0, 1, 0, -1};                // 顺序为左上右下 一个数组;
        if (isOutOfBorder(p)) {                              //未越界不操作;
            return null;
        }
        grid[] ps = new grid[4];                       // 顺序为上右下左
        int xt;
        int yt;
        for (int i = 0; i <= 3; i++) {
            xt = p.getX() + adds[i];
            yt = p.getY() + adds[i + 1];
            if (isOutOfBorder(xt, yt))                       //遍历点的左上右下四个方向,如果越界则不操作;
                continue;
            ps[i] = maze[xt][yt];                            //将可能的方向存入对象数组中;
        }
        return ps;                                           //将得到的全部方向返回;
    }

8、地图的创建

地图的生成主要是随机性和有路可寻性;
可以由路推图,先随机生成一条从入口到出口的二叉树,入口为根节点,出口为其中的一个叶子节点;对树的节点进行入栈操作和标志位的修改;
细节的标注在完整代码部分都有;

9、寻路

经过一遍遍历,从末尾格局父节点唯一原则,生成了一条从末尾遍历至开头的通路,路上全标记为2;

  private void drawPath(Graphics g) {
        //完整路径寻路;逆二叉树搜索;
        if (drawPath == true)
            g.setColor(Color.blue);           //显示正确的路径;
        else
            g.setColor(this.getBackground()); //不需要答案提示情况下显示背景颜色(白色)
        grid p = maze[NUM - 1][NUM - 1];   //生成对象P为最后一个对象位置;
        while (p.getFather() != null) {       //当存在父节点时;
            p.setFlag(2);                     //将节点标志位置2;
            p = p.getFather();                //再将当前节点更迭至其父节点处
        }

三、主函数的设计

对窗口参数的设定

  • 窗口大小
  • 窗口的子方格数目
  • 窗口的位置
  • 窗口的可关闭设置
  • 窗口标题设置
  • 等;
 public static void main(String[] args) {
        final int n = 15, width = 400, padding = 20, LX = 400, LY = 200;        // n为边界的方格数目,width设置窗口边长;
        JPanel p = new Maze(n, (width - padding - padding) / n, padding);
        JFrame frame = new JFrame("迷宫游戏(按回车键提示获取提示)");
        frame.getContentPane().add(p);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                     //设置窗口1点×可关闭;
        frame.setSize(width + padding, width + padding + padding);   //设置窗口大小;
        frame.setLocation(LX, LY);                                   //设置窗口位置;
        frame.setVisible(true);                                      //通过 setVisible()并设置参数为true,把内存中的窗口显示在屏幕上;
    }

实验结果

1、迷宫得生成:
1、由随机生成的width个像素宽度(n×n)个子方格对象和padding边界宽度组成的一个窗口界面;
2、坐标为(0,0)和(n,n)的方格对象为迷宫的入口和出口。
3、生成一个一定大小的移动对象(注意方格对象的边框宽度决定)。
在这里插入图片描述

2、迷宫的界面大小由输入参数决定,大小可由输入参数决定;
点按enter键可以出现提示的路径(即根据迷宫自动生成的寻路算法的结果展现);
在这里插入图片描述

3、过关提示:
当到达了迷宫出口;
点击确定就可以加入新的关卡;设置提示说一共走了多少步;
在这里插入图片描述
(1)如果通过后问是否继续继续闯关,如果选择是
在这里插入图片描述
(2)产生新的关卡地图;
在这里插入图片描述
(3)点击否,退出游戏,提示一共闯过了多少关。
在这里插入图片描述

完整代码:

package First_App;
import java.awt.Color;                           //颜色相关操作的类;
import java.awt.Graphics;                        //用以基本计何图形的绘制;
import java.awt.event.KeyAdapter;                //提供按键事件相关操作的接口
import java.awt.event.KeyEvent;                  //按键事件
import java.util.Random;                         //随机类
import java.util.Stack;                          //栈
import javax.swing.JFrame;                       //控件,独立存在的顶级窗口;
import javax.swing.JOptionPane;                  //java的对话框类;
import javax.swing.JPanel;                       //Panel默认的布局管理器;

class grid {
    static final int InTree = 1;
    static final int NotIntree = 0;
    private int x = -1;
    private int y = -1;
    private int flag = NotIntree;                //是否有  的标志位;
    private grid father = null;
    public grid(int xx, int yy) {             //构造函数;
        x = xx;
        y = yy;
    }
    public int getX() {                          //获取该方格的横坐标;(由于不可直接用对象访问私有变量);
        return x;
    }
    public int getY() {                          //获取纵坐标;
        return y;
    }
    public int getFlag() {                       //获取标志位;
        return flag;
    }
    public grid getFather() {                 //获取方格对象的父节点;
        return father;
    }
    public void setFather(grid f) {           //修改方格对象的父节点;
        father = f;
    }
    public void setFlag(int f) {
        flag = f;                                //修改标志位;
    }
    public String toString() {                   //以字符串形式导出坐标;
        return new String("(" + x + "," + y + ")\n");
    }
}
public class Maze extends JPanel {
  private  static final long serialVersionUID = -8300339045454852626L;
    private int NUM, width, padding;            // NUM为界面总边长,width为每个格子的边长;padding为内边距;
    private grid[][] maze;
    private int myX, myY;                       //定义了两个点;
    int sum = 0;                                //记录步数;
    int rand = 0;                               //记录关数;
    private boolean drawPath = false;           //路径标志位;
    Maze(int n, int w, int p) {                //Maze的构造方法;
        NUM = n;                                //窗口边长;
        width = w;                             //子方格边长;
        padding = p;                            //
        maze = new grid[NUM][NUM];           //调用Lattice的构造函数,对尾端的小方格构造;
        for (int i = 0; i <= NUM - 1; i++)      //对除最后一格外的每一个坐标认定为一个小方格对象并构造;
            for (int j = 0; j <= NUM - 1; j++)
                maze[i][j] = new grid(i, j); //每个在窗口内具有整数坐标的点被视为一个方格对象;
        createMaze();
        setKeyListener();
        this.setFocusable(true);
    }
    private void init() {                              //定义一个私有的默认构造函数;
      //  String name = JOptionPane.showInputDialog(null, "请输入:\n", "title", JOptionPane.PLAIN_MESSAGE);

        for (int i = 0; i <= NUM - 1; i++)
            for (int j = 0; j <= NUM - 1; j++) {
                maze[i][j].setFather(null);              //设置每个小方格的父节点为null;
                maze[i][j].setFlag(grid.NotIntree);   //设置每个方格的标志位;
            }
        myX = 0;
        myY = 0;
        drawPath = false;                                //路径绘制标识;
        createMaze();
        this.setFocusable(true);                         //使能控件获得焦点能力
        repaint();                                       //重新绘制;
    }
    public int getCenterX(int x) {
        return padding + x * width + width / 2;           //内边距属性;第x+1位的小方格中心点横坐标位置;
    }
    public int getCenterY(int y) {
        return padding + y * width + width / 2;           //内边距属性;第y+1位的小方格中心点纵坐标位置;
    }
    public int getCenterX(grid p) {
        return padding + p.getY() * width + width / 2;     //得到方格对象P的第x+1位的小方格中心点横坐标位置;
    }
    public int getCenterY(grid p) {
        return padding + p.getX() * width + width / 2;      //得到方格对象P的第x+1位的小方格中心点纵坐标位置;
    }
    private void checkIsWin() {
        if (myX == NUM - 1 && myY == NUM - 1) {          //如果当前方格位置坐标对于末尾方格的坐标;
           // JOptionPane.showMessageDialog(null, "你成功的走出了迷宫 !"+"你一共用了"+sum+"步",
           //         "恭喜过关。",JOptionPane.WARNING_MESSAGE);
            JOptionPane.showMessageDialog(null, "你成功的走出了迷宫 !"+"你一共走了"+sum+"步");
            rand++;
            sum = 0;
          //  Object[] options ={ "继续", "退出" };  //自定义按钮上的文字
            int m = JOptionPane.showConfirmDialog(null, "你已经通过"+rand+"关,是否要继续闯关?", "是否继续",JOptionPane.YES_NO_OPTION); //返回值为0或1
           // int m = JOptionPane.showOptionDialog(null, "你是否要继续闯关?", "第"+rand+"关",JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
           if(m == 0) {
               init();
           }
           if(m == 1){
               JOptionPane.showMessageDialog(null, "本次你一共通过了 !"+rand+"关,下次继续挑战");
           }
        }
    }
    synchronized private void move(int c ) {
        int tx = myX, ty = myY;
        switch (c) {
            case KeyEvent.VK_LEFT :                      //监控按键,对应按键有对应的当前位置坐标的修改;
                ty--;
                sum++;
              // g.drawLine(myX,myY,tx,ty);
                break;
            case KeyEvent.VK_RIGHT :
                ty++;
                sum++;
                break;
            case KeyEvent.VK_UP :
                tx--;
                sum++;
                break;
            case KeyEvent.VK_DOWN :
                tx++;
                sum++;
                break;
            case KeyEvent.VK_ENTER:                       //如果是空格键;
                if (drawPath == true) {                   //如果路径绘制标识符为1,就置0;
                    drawPath = false;
                } else {
                    drawPath = true;                       //否则就把路径标识符置1;
                }
                break;
            default :
        }
        if (!isOutOfBorder(tx, ty) && (maze[tx][ty].getFather() == maze[myX][myY]
                || maze[myX][myY].getFather() == maze[tx][ty])) {             //是否越界判断;
            myX = tx;                                                         //越界则不动;
            myY = ty;
        }
    }
    private void setKeyListener() {                                          //设置按键监视器;
        this.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {                  //按键事件;
                int c = e.getKeyCode();                           //获取当前按键信息;
                move(c);                                          //根据按键信息继续坐标操作;
                repaint();                                                   //将操作后的信息修正展示;
                checkIsWin();                                                //检测是否赢了;
            }
        });
    }
    private boolean isOutOfBorder(grid p) {               //子方格对象调用的越界判断;
        return isOutOfBorder(p.getX(), p.getY());
    }
    private boolean isOutOfBorder(int x, int y) {            //坐标调用的越界判断;
        return (x > NUM - 1 || y > NUM - 1 || x < 0 || y < 0) ? true : false;
    }
    private grid[] getNeis(grid p) {
        final int[] adds = {-1, 0, 1, 0, -1};                // 顺序为左上右下 一个数组;
        if (isOutOfBorder(p)) {                              //未越界不操作;
            return null;
        }
        grid[] ps = new grid[4];                       // 顺序为上右下左
        int xt;
        int yt;
        for (int i = 0; i <= 3; i++) {
            xt = p.getX() + adds[i];
            yt = p.getY() + adds[i + 1];
            if (isOutOfBorder(xt, yt))                       //遍历点的左上右下四个方向,如果越界则不操作;
                continue;
            ps[i] = maze[xt][yt];                            //将可能的方向存入对象数组中;
        }
        return ps;                                           //将得到的全部方向返回;
    }

    private void createMaze() {                              //迷宫建立;
        Random random = new Random();                        //创建随机对象;
        int rx = Math.abs(random.nextInt()) % NUM;           //生成【0,num】间的随机数做横纵坐标;
        int ry = Math.abs(random.nextInt()) % NUM;
        Stack<grid> s = new Stack<grid>();              //生成栈;
        grid p = maze[rx][ry];                             //由随机坐标生成随机方格对象;
        grid neis[] = null;                                //生成方格对象数组neis;
        s.push(p);                                            //随机方格对象压栈;
        while (!s.isEmpty()) {                                //如果栈非空;
            p = s.pop();
            p.setFlag(grid.InTree);                        //将之前随机生成的点都修改标志位;
            neis = getNeis(p);                                //获取当前方格的全部可行方向,存入neis中;
            int ran = Math.abs(random.nextInt()) % 4;         //产生【0,3】的随机整数;
            for (int a = 0; a <= 3; a++) {
                ran++;
                ran %= 4;                                     //每次修改ran的值并保证其介于【0,4】;
                if (neis[ran] == null || neis[ran].getFlag() == grid.InTree)
                    continue;               //如果不存在或者已经标记,就不操作;
                s.push(neis[ran]);          //对于可用的点都入栈操作;
                neis[ran].setFather(p);     //如果存在,又未入树则标记其父节点
            }
        }
    }
    //清除掉书的枝干上的节点;;
    private void clearFence(int i, int j, int fx, int fy, Graphics g) {
        int sx = padding + ((j > fy ? j : fy) * width),    //取较大的设置
                sy = padding + ((i > fx ? i : fx) * width),
                dx = (i == fx ? sx : sx + width),
                dy = (i == fx ? sy + width : sy);
        if (sx != dx) {
            sx++;
            dx--;
        } else {
            sy++;
            dy--;
        }
        g.drawLine(sx, sy, dx, dy);                         //绘制栅栏边界;
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);                           //super 调用父类的方法;
        for (int i = 0; i <= NUM; i++) {
            g.drawLine(padding + i * width, padding,padding + i * width, padding + NUM * width);
        }                                                  //需要两个坐标(x1,y1)和(x2,y2)绘制全局格框(注意空出出入口);
        for (int j = 0; j <= NUM; j++) {
            g.drawLine(padding, padding + j * width, padding + NUM * width, padding + j * width);
        }
        g.setColor(this.getBackground());
        for (int i = NUM - 1; i >= 0; i--) {
            for (int j = NUM - 1; j >= 0; j--) {
                grid f = maze[i][j].getFather();         //取出个方格对象的父节点;
                if (f != null) {
                    int fx = f.getX(), fy = f.getY();       //如果父节点存在(父节点其实就是一个对象)获取父节点的坐标;
                    clearFence(i, j, fx, fy, g);            //清除掉树枝干上的边框;
                }
            }
        }
        g.drawLine(padding, padding + 1, padding, padding + width - 1);
        int last = padding + NUM * width;
        g.drawLine(last, last - 1, last, last - width + 1);           //补充绘制外边框;
        g.setColor(Color.blue);
        g.fillOval(getCenterX(myY) - width / 3, getCenterY(myX) - width / 3, width / 2, width / 2);
        //填充我们需要操作的小圆球的对象;Graphics中用当前颜色填充以指定矩形为边界的椭圆的方法;
        if (drawPath == true)
            drawPath(g);
    }
    //绘制迷宫路径;
    private void drawPath(Graphics g) {
        //完整路径寻路;逆二叉树搜索;
        if (drawPath == true)
            g.setColor(Color.blue);           //显示正确的路径;
        else
            g.setColor(this.getBackground()); //不需要答案提示情况下显示背景颜色(白色)
        grid p = maze[NUM - 1][NUM - 1];   //生成对象P为最后一个对象位置;
        while (p.getFather() != null) {       //当存在父节点时;
            p.setFlag(2);                     //将节点标志位置2;
            p = p.getFather();                //再将当前节点更迭至其父节点处
        }
        //经过一遍遍历,从末尾格局父节点唯一原则,生成了一条从末尾遍历至开头的通路,路上全标记为2;
        p = maze[0][0];                       //对p置首位;
        while (p.getFather() != null) {       //存在父节点时;
            if (p.getFlag() == 2) {           //如果对象标志位为2
                p.setFlag(3);                 //对象标志位置为3;
                g.setColor(Color.blue);
            }
           // g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getFather()), getCenterY(p.getFather()));
            p = p.getFather();                //将p点对象更迭为其父类对象位置;
        }
        g.setColor(Color.blue);               //绘制颜色(颜色覆盖);
        p = maze[NUM - 1][NUM - 1];           //
        while (p.getFather() != null) {       //该点对象存在父节点;
            if(p.getFlag() == 3)
                break;
            g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getFather()), getCenterY(p.getFather()));
            p = p.getFather();
        }
    }
    public static void main(String[] args) {
        final int n = 10, width = 400, padding = 20, LX = 400, LY = 200;        // n为边界的方格数目,width设置窗口边长;
        JPanel p = new Maze(n, (width - padding - padding) / n, padding);
        JFrame frame = new JFrame("迷宫游戏(按回车键提示获取提示)");
        frame.getContentPane().add(p);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                     //设置窗口1点×可关闭;
        frame.setSize(width + padding, width + padding + padding);   //设置窗口大小;
        frame.setLocation(LX, LY);                                   //设置窗口位置;
        frame.setVisible(true);                                      //通过 setVisible()并设置参数为true,把内存中的窗口显示在屏幕上;
    }
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值