六边形寻路

1 篇文章 0 订阅

表现

在这里插入图片描述
(红色文本表示的是移动到该点的难度)

原理

本质还是A*的 f=g+h 那一套,就不细说了

与正方形寻路的区别

一.取周围能去的点不同
取正方形周围 4个(或者带斜向8个),改成了取六边形周围6个
二。排布不同
在这里插入图片描述
可以看到奇数列(0开头)的位置相对于正方形摆放来说,会稍微高一点。
如果把六边形转90°(横摆变竖摆),那就是奇数行会偏右

实现

根据索引确定六边形的坐标

在这里插入图片描述
简单的几何学.mp3
然后根据完全贴合摆放的偏移量来确定每个六边形的位置

        public MyVector2 GetPos(int columnIndex, int rowIndex)
        {
            //横着摆,奇数行会错位
            MyVector2 offset = new MyVector2();
            if (columnIndex % 2 == 1)
                offset.y = SQUARE_ROOT_OF_3 / 2f;

            MyVector2 pos = new MyVector2()
            {
                x = columnIndex * 1.5f,
                y = rowIndex * SQUARE_ROOT_OF_3,
            };

            pos += offset;//加六边形组合偏移
            pos *= fNodeSize;//乘边长
            pos += centerOffset;//加上地图中心点偏移

            return pos;
        }

取周围的六边形

在这里插入图片描述
终结索引的规律(竖摆就是把 Y 的偏移,换成了 X 的偏移)

        public MyVector2Int[] GetRoundPos(MyVector2Int center)
        {
            List<MyVector2Int> tmp = new List<MyVector2Int>();
            int offset = center.x % 2 == 0 ? -1 : 1;//偶数行上下的 x 是i-1,i。奇数行上下的 x 是 i,i+1
            //左
            tmp.Add(new MyVector2Int() { x = center.x - 1, y = center.y });
            tmp.Add(new MyVector2Int() { x = center.x - 1, y = center.y + offset });
            //上下
            tmp.Add(new MyVector2Int() { x = center.x, y = center.y + 1 });
            tmp.Add(new MyVector2Int() { x = center.x, y = center.y - 1 });
            //右
            tmp.Add(new MyVector2Int() { x = center.x + 1, y = center.y });
            tmp.Add(new MyVector2Int() { x = center.x + 1, y = center.y + offset });

            //去除越界的坐标
            for (var i = 0; i < tmp.Count; i++)
            {
                if (tmp[i].x < 0 || tmp[i].x >= nColumnCount ||
                    tmp[i].y < 0 || tmp[i].y >= nRowCount)
                {
                    tmp.RemoveAt(i);
                    i--;
                }
            }
            return tmp.ToArray();
        }

寻路算法

Created with Raphaël 2.2.0 开始 添加起点进检测列表 找到还未检测点中的最优点(f 最小) 还能不能找到下一个点 计算该点周围点的 g 跟 h 检测终点是不是周围点 结束 到不了目标点 yes no yes no

查找未检测点中的最优解,取 f 最小,也就 h+g 最小

var tmpList = listAll.FindAll((pathPoint) => !pathPoint.bHaveChecked)
	.OrderBy((pathPoint) => pathPoint.g + pathPoint.h)
	.ThenBy((pathPoint) => pathPoint.h).ToList();

检测还能不能找到下一个点

int maxNum = mapData.nColumnCount * mapData.nRowCount + 1;//防死循环

while (!sussecc)
{
...
	if (tmpList == null || tmpList.Count == 0 || ++count > maxNum)
	{
	   	UnityEngine.Debug.Log($"{tmpList == null} - {tmpList.Count} - {count}");
		//找不到下一个点,或者到不了终点就找离终点最近的点
		lastPoint = listAll.OrderBy((pathPoint) => pathPoint.h).ToList()[0];
		break;
	}
...
}

计算当前最优点的周围点的 h 跟 g

h 直接取坐标距离,g 取两点距离 * 移动到该点的难度
如果周围点已经被计算过,但是本次计算的 g 要比之前的小,就更新该点的值(g 更小说明从起点经过当前点到达该点要比经过之前计算的点到达该点更近)
另外有点需要注意的是,如果刷新了 g 要把该点重新标记为未检测,这样可以用新的 g (更近的路径)来刷新该点周围的点(不然可能出现如图的后果)
不然这就是代价

完整代码

        public static bool FindPath_Hexagon(STHexagonMapData mapData, MyVector2Int startPoint, MyVector2Int endPoint, out List<MyVector2Int> path)
        {
            MyVector2 startPos = mapData.GetPos(startPoint.x, startPoint.y), endPos = mapData.GetPos(endPoint.x, endPoint.y);

            List<PathData> listAll = new List<PathData>();
            listAll.Add(new PathData() { point = startPoint, fromPath = null, g = 0, h = (endPos - startPos).Dis, bHaveChecked = false });//起点

            bool sussecc = false;
            PathData lastPoint = null;//检查到最后的点

            int count = 0;
            int maxNum = mapData.nColumnCount * mapData.nRowCount + 1;//防死循环

            while (!sussecc)
            {
                var tmpList = listAll.FindAll((pathPoint) => !pathPoint.bHaveChecked)
                    .OrderBy((pathPoint) => pathPoint.g + pathPoint.h)
                    .ThenBy((pathPoint) => pathPoint.h).ToList();

                if (tmpList == null || tmpList.Count == 0 || ++count > maxNum)
                {
                    UnityEngine.Debug.Log($"{tmpList == null} - {tmpList.Count} - {count}");
                    //找不到下一个点,或者到不了终点就找离终点最近的点
                    lastPoint = listAll.OrderBy((pathPoint) => pathPoint.h).ToList()[0];
                    break;
                }

                PathData curPath = tmpList[0];
                curPath.bHaveChecked = true;//用过就改标识符

                MyVector2Int[] roundPoint = mapData.GetRoundPos(curPath.point);

                for (var i = 0; i < roundPoint.Length; i++)
                {
                    //来的点不用管
                    if (roundPoint[i] == curPath.fromPath?.point) continue;
                    //跳过障碍物
                    if (mapData.GetNodeData(roundPoint[i]).bIsObs) continue;

                    int index = listAll.FindIndex((pathPoint) => pathPoint.point == roundPoint[i]);
                    PathData tmpPath;

                    float g = mapData.GetNodeData(roundPoint[i]).nDisWeight * mapData.fNodeSize + curPath.g;
                    //查找是不是旧点,如果是不是就新建
                    if (index < 0)
                    {
                        //新点
                        tmpPath = new PathData();
                        tmpPath.point = roundPoint[i];
                        tmpPath.fromPath = curPath;
                        tmpPath.g = g;
                        tmpPath.h = (endPos - mapData.GetPos(tmpPath.point.x, tmpPath.point.y)).Dis;
                        tmpPath.bHaveChecked = false;

                        listAll.Add(tmpPath);//添加新点
                    }
                    else
                    {
                        //旧点,如果路径更短就更新
                        tmpPath = listAll[index];
                        if (tmpPath.g > g)
                        {
                            tmpPath.g = g;
                            tmpPath.fromPath = curPath;
                            tmpPath.bHaveChecked = false;//该点路径路径刷新重置一下,让这个点再检测一次,保证该点周边的点也刷新

                            listAll[index] = tmpPath;//修改旧点
                        }
                    }

                    if (!sussecc && roundPoint[i] == endPoint)
                    {
                        //周围点是终点,结束
                        sussecc = true;
                        lastPoint = tmpPath;//指向终点的path
                        break;
                    }
                }
            }

            path = new List<MyVector2Int>();

            //链表取到来的点,一直到null,也就是起点
            while (lastPoint != null)
            {
                path.Add(lastPoint.point);

                lastPoint = lastPoint.fromPath;
            }

            path.Reverse();//翻转列表,从起点到终点

            return sussecc;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值