效果展示
自动寻路连线
工具
QT Creator 4.3.0
QGraphicsPathItem 画路径
QGraphicsRectItem 画方块
QGraphicsView QGraphicsScene 视图画板
概述
本文描述的连线方式与曼哈顿连线类似,仅包含水平和垂直方向的线,但不是最优路线。
尝试过用Astar算法去连接两个方块,但由于我的视图设置的无限大,而我又不想设置太大的网格,尽管我限制了寻路的范围,但是连接两个方块还是计算了三分钟,改善了Astar算法后寻路的计算时间还是不可接受,理清了自己的需求后,我需要的是不要最优但接近最优的路线,这样能节省很大的计算量。基本是感觉不到路径计算的时间。
寻路方式及原理很简单,傻瓜式寻路,基本思想是从起点出发,假设先水平走,水平遇障垂直走,垂直走时检测水平是否仍有障碍,无障后继续水平走直到接近终点的水平坐标附近再切换到垂直走,然后和水平走原理相同。
也可以这么理解,就是哪有障碍物往哪走。遇到障碍物就绕开,那么什么时候认为寻路成功呢,当然不需要傻乎乎的走到终点,而是每一步计算完毕后以起点坐标和终点坐标画一个矩形。
当红色路线无障碍物或黑色路线无障碍物时,就可以判断寻路成功了
当然这种寻路方式有一定的前提条件:方块与方块之间要有足够的间隔,不能完全堵死,要给线留出通过空间,所以方块与方块之间要有碰撞检测功能。
当然,方块堵死话任何寻路算法都没办法,方块之间留空隙,是为了让寻路更快捷。
方块
画方块(QGraphicsRectItem)就不在这里叙述了,这里主要展示方块间碰撞检测的距离:
void logic_block::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
setCursor(Qt::ArrowCursor); // 设置鼠标样式为箭头
QGraphicsRectItem::mouseReleaseEvent(event);
// 获取当前块的边界矩形
QRectF currentRect = sceneBoundingRect();
// 遍历所有块
QList<QGraphicsItem*> allBlocks = scene()->items();
foreach (QGraphicsItem* item, allBlocks) {
if (item == this || (child_group_list.contains(item)) == true)
continue;
if (item->type() >= QGraphicsItem::UserType + BLOCK_TYPE_LOGIC) {
// 获取其他块的边界矩形
QRectF otherRect = item->sceneBoundingRect();
// 判断是否与其他块及其周围一定距离范围内重叠
if (currentRect.intersects(otherRect.adjusted(-BLOCK_SPCING, -BLOCK_SPCING, BLOCK_SPCING, BLOCK_SPCING))) {
// 重叠处理,将块移回原位
setPos(originalPos);
break;
}
}
}
}
BLOCK_SPCING是方块本身再往外一段空间,相当于虚拟了一个大矩形,这个大矩形每条边都距离方块BLOCK_SPCING距离,这里我设置的BLOCK_SPCING大小为40
#define LOGIC_BLOCK_WIDTH 50 //逻辑块的宽度
#define LOGIC_BLOCK_HEIGHT 100 //逻辑块的高度
#define CONDITION_BLOCK_WIDTH 80 //条件块的宽度
#define CONDITION_BLOCK_HEIGHT 20 //条件块的高度
#define CONNECT_POINT_WIDTH 10 //块上连接点的大小
#define CONNECT_POINT_HEIGHT 10
#define BLOCK_SPCING (CONNECT_POINT_WIDTH * 4) //块之间的最小距离 防止块的碰撞与重叠
连接点
我在每个方块上都添加了输入连接点和输出连接点,输出只能与输入连接点连接。相同属性的连接点无法互连。当然这是我项目需求上的限制,本文只描述连接点的作用。
连线
我们用QGraphicsPathItem画线,用QPainterPath给线设置起点与各个节点
QPainterPath path;
path.moveTo(start_point);
path.lineTo(probe_point);
setPath(path);
有必要提醒的是,每次想要重新划线设置路径时,一定要重新新建QPainterPath 变量,否则无法消除原来的线段,目前没找到QPainterPath 类的线段清除手段。
那么现在开始寻路吧
当我们获取到起点和终点时,就有了目标:
1.起步选向:用起点和终点画一个矩形,矩形有两个横边和两个竖边,如果边与其他快有重叠,则记这个边为障碍边,那么横边和竖边的障碍边的情况都分辨有0/1/2三种情况,那我们那边障碍边比较多,我们往那个方向走。
path_info.longitudinal_intersect_num = 0;
path_info.transverse_intersect_num = 0;
QPointF rect_point1(probe_end_point.x(), probe_start_point.y()); //矩形的其他点,此点与起点组成横线
QPointF rect_point2(probe_start_point.x(), probe_end_point.y()); //矩形的其他点,此点与起点组成纵线
QLineF transversse_line1(probe_start_point, rect_point1); //与起点相连的横线
QLineF transversse_line2(rect_point2, probe_end_point); //与终点相连的横线
QLineF longitudinal_line1(probe_start_point, rect_point2); //与起点相连的纵线
QLineF longitudinal_line2(rect_point1, probe_end_point); //与终点相连的纵线
path_info.transversse_line1_is_none = true; //横向线与纵向线是否无遮挡
path_info.transversse_line2_is_none = true;
path_info.longitudinal_line1_is_none = true;
path_info.longitudinal_line2_is_none = true;
/* 判断四条线有几条线与其他块相交 */
if (check_line_intersects_rect(transversse_line1)) {
path_info.transverse_intersect_num