用Qt实现一个迷宫

简介: 用Qt实现一个迷宫

用qt实现一个迷宫,玩家通过键盘方向键移动,从左上角走到右下角即为成功。

先来看下最终实现效果:

其实重点在于随机迷宫的生成,而迷宫的生成靠的是并查集算法。

思路参考自bigsai(感谢授权):

原文链接

游戏总结如下:

游戏规则为若两个小方块之间没有线,说明是互通的,可以移动,否则两个方块之间被“墙”阻隔不能移动。因此我们先画一个由多个小方块组成的初始地图,然后随机擦除其中的一些线,就可以生成一个迷宫了。就像这样:

而将两个方格之间的线擦除,是不是也就意味着这两个方格是一个集合了(他们是一家子了),所以擦除线的过程实际上就是两个原本不相交的集合合并的过程,而并查集解决的正是合并的问题。知道了使用并查集,那么对于地图而言,该如何实现这个合并过程呢?我们可以将地图中每个方格抽象成一个个点,比如对于5 x 5大小的地图,就有25个小方格,按顺序排个号,这时我们可以发现,其实数值和数值在地图中的坐标是存在关系的,比如:11 / 5 == 2;11 % 5 == 1,而(2, 1)正是11所在地图的位置,利用这种关系就可以把地图当做一个一维数组来看待。

这样一来就好办了,直接套用并查集的模板即可。

游戏的目的是从左上角走到右下角,也就意味着左上角的点和右下角的点一定是可以连通的。为了生成随机迷宫,我们在地图中随机找一些点,判断它和它上下左右四个方向的点是否连通,不连通则将两个集合连通,一直重复,直到左上角和右下角连通了,说明游戏是有解的了,这时就不再合并了。总结一下就是:我们为了让游戏是有解的,就在地图中随机找点合并一些集合,当发现有解时,就不再合并。当把两个点连通时,就将它们之间的线擦除,也就形成了视觉上的随机地图。

迷宫生成了,但是在玩家走的时候,如何判断当前所走方向有没有被阻隔(有没有线)呢?这很好办,通过一个二维数组来记录两个点之间有没有线,比如:在合并的过程中,合并1和2,则让arr[1][2] = 1,这样当1走向2或2走向1时判断arr[1][2]是否为1即可。

这样的话,随机地图产生了,并且左上角到右下角是连通的,判断两格之间是否能走的问题也解决了,迷宫也就没什么难的了。

下面看一下一些关键的代码片段:

初始化地图(画方格):

int x1 = BEGINX, y1 = 0, x2 = ENDX, y2 = 0;
    for (int i = 0; i < LINENUM; i++) {
        y1 += LINEHEIGHT;
        y2 = y1;
        //画横线
        line.setLine(x1, y1, x2, y2);
        m_pPainter->drawLine(line);
        //画竖线
        line.setLine(y1, x1, y2, x2);
        m_pPainter->drawLine(line);
    }

生成随机地图:

//当左上角的点和右下角的点不连通
while (find(0) != find(SQUARESNUM - 1)) {
        //在合法范围内随机生成一个点
        int randnum = rand() % SQUARESNUM;
        //随机找一个邻居(上下左右)
        int neighbor = getNeighbor(randnum);
        if (find(randnum) != find(neighbor)) {
            m_qvectorIsUnicom[randnum][neighbor] = m_qvectorIsUnicom[neighbor][randnum] = 1;
            //将两个点之间的线擦除
            myDrawLine(randnum, neighbor);
            merge(randnum, neighbor);
        }
    }

这里的画线只是为了满足视觉上的效果,因此,我只是把画笔的颜色改成了白色,覆盖了之前的黑色,也就形成了擦除的效果。

找邻居:

int Widget::getNeighbor(int n)
{
    //计算得到点n在地图中的坐标
    int x = n / ROWNUM;
    int y = n % ROWNUM;
    QVector<int> arr;
    //判断是否出界
    if (y > 0) arr.push_back(n - 1);//左邻居
    if (y < ROWNUM - 1) arr.push_back(n + 1);//右邻居
    if (x > 0) arr.push_back((x - 1) * ROWNUM + y);//上邻居
    if (x < ROWNUM - 1) arr.push_back((x + 1) * ROWNUM + y);//下邻居
    //取一个随机的邻居
    int tmp = rand() % arr.size();
    return arr[tmp];
}

擦除线:

void Widget::myDrawLine(int n, int m)
{
    int x1 = n / ROWNUM;
    int y1 = n % ROWNUM;
    int x2 = m / ROWNUM;
    int y2 = m % ROWNUM;
    //取两点中间线位置
    int x3 = (x1 + x2) / 2 + 1;
    int y3 = (y1 + y2) / 2 + 1;
    QPen pen;
    pen.setColor(QColor(Qt::white));
    pen.setWidth(3);
    QLineF line;
    m_pPainter->begin(m_pPixmap);
    m_pPainter->setPen(pen);
    //上下方向的点,画横线
    if (x1 - x2 == 1 || x1 - x2 == -1) {
        line.setLine(y1 * LINEWIDTH + BEGINX, x3 * LINEHEIGHT + BEGINX, (y1 + 1) * LINEWIDTH + BEGINX, (x3 + 1) * LINEHEIGHT);
    } else {
        line.setLine(y3 * LINEWIDTH + BEGINX, x1 * LINEHEIGHT + BEGINX, (y3 + 1) * LINEWIDTH, (x1 + 1) * LINEHEIGHT + BEGINX);
    }
    m_pPainter->drawLine(line);
    m_pPainter->end();
}

并查集的合并和查询:

int Widget::find(int n)
{
    if (m_qvectorArray[n] == n) return n;
    return find(m_qvectorArray[n]);
}
void Widget::merge(int n, int m)
{
    int fn = find(n), fm = find(m);
    if (fn == fm) return;
    if (m_qvectorSize[fn] > m_qvectorSize[fm]) std::swap(fn, fm);
    m_qvectorArray[fn] = fm;
    m_qvectorSize[fm] += m_qvectorSize[fn];
    return;
}

至此就完成了迷宫的核心部分,剩下的就是通过qt完成控制,画线以及对游戏体验的优化了,见源代码。最后放上操作指南:按下方向键开始游戏,方向键控制走向,超过一分钟游戏自动结束,按下重置按钮更换地图,游戏就绪。

效果图:

源代码(包含可执行程序):

https://gitee.com/gao-yuelong/qtdemo/tree/master/mazes

最后还是感谢bigsai提供的思路,非常感谢。

相关文章
|
7月前
|
Linux C语言
[学习][笔记] qt5 从入门到入坟:<一>hello Qt
[学习][笔记] qt5 从入门到入坟:<一>hello Qt
|
5天前
Qt+OpenGL 打砖块游戏
Qt+OpenGL 打砖块游戏
21 0
|
5天前
|
定位技术 C# 图形学
Unity和C#游戏编程入门:创建迷宫小球游戏示例
Unity和C#游戏编程入门:创建迷宫小球游戏示例
82 2
|
5天前
用Qt画圣诞树——要画就画最丑的圣诞树
用Qt画圣诞树——要画就画最丑的圣诞树
|
10月前
1252:走迷宫 2021-01-05
1252:走迷宫 2021-01-05
|
5天前
[Qt5] 实现CAD中的十字标辅助线效果
[Qt5] 实现CAD中的十字标辅助线效果
58 0
|
7月前
|
API
[学习][笔记] qt5 从入门到入坟:<八>Qt 绘制系统
[学习][笔记] qt5 从入门到入坟:<八>Qt 绘制系统
|
10月前
|
C语言
c语言通过easyx窗口实现小球打砖块的游戏
c语言通过easyx窗口实现小球打砖块的游戏
110 0
使用EasyX实现画图软件中画出矩形的效果
具体效果就是 可以复制代码自己实现一下
123 0
|
Python
考点:海龟画图turtle库的使用,绘制五角星、矩形等【Python习题09】
考点:海龟画图turtle库的使用,绘制五角星、矩形等【Python习题09】
376 0
考点:海龟画图turtle库的使用,绘制五角星、矩形等【Python习题09】