用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提供的思路,非常感谢。

相关文章
|
算法 C++
【Qt编程】3D迷宫游戏
       说起迷宫想必大家都很熟悉,个人感觉迷宫对人的方向感是很大的考验,至少我的方向感是不好的,尤其是在三维空间中。由于这段时间帮导师做项目用到了三维作图,便心血来潮想做个三维迷宫玩玩。
1722 0
|
4月前
|
数据安全/隐私保护 C++ 计算机视觉
Qt(C++)开发一款图片防盗用水印制作小工具
文本水印是一种常用的防盗用手段,可以将文本信息嵌入到图片、视频等文件中,用于识别和证明文件的版权归属。在数字化和网络化的时代,大量的原创作品容易被不法分子盗用或侵犯版权,因此加入文本水印成为了保护原创作品和维护知识产权的必要手段。 通常情况下,文本水印可以包含版权声明、制作者姓名、日期、网址等信息,以帮助识别文件的来源和版权归属。同时,为了增强防盗用效果,文本水印通常会采用字体、颜色、角度等多种组合方式,使得水印难以被删除或篡改,有效地降低了盗用意愿和风险。 开发人员可以使用图像处理技术和编程语言实现文本水印的功能,例如使用Qt的QPainter类进行文本绘制操作,将文本信息嵌入到图片中,
173 1
Qt(C++)开发一款图片防盗用水印制作小工具
|
3月前
|
监控 C++ 容器
【qt】MDI多文档界面开发
【qt】MDI多文档界面开发
75 0
|
2月前
|
开发工具 C++
qt开发技巧与三个问题点
本文介绍了三个Qt开发中的常见问题及其解决方法,并提供了一些实用的开发技巧。
|
2月前
|
3月前
|
C++
C++ Qt开发:QUdpSocket网络通信组件
QUdpSocket是Qt网络编程中一个非常有用的组件,它提供了在UDP协议下进行数据发送和接收的能力。通过简单的方法和信号,可以轻松实现基于UDP的网络通信。不过,需要注意的是,UDP协议本身不保证数据的可靠传输,因此在使用QUdpSocket时,可能需要在应用层实现一些机制来保证数据的完整性和顺序,或者选择在适用的场景下使用UDP协议。
132 2
Qt开发网络嗅探器02
Qt开发网络嗅探器02
|
3月前
|
存储 运维 监控
Qt开发网络嗅探器01
Qt开发网络嗅探器01
|
3月前
|
网络协议 容器
Qt开发网络嗅探器03
Qt开发网络嗅探器03
|
3月前
【qt】多窗口开发
【qt】多窗口开发
54 0