寻路算法1:A星寻路和navmesh寻路的技巧和优化

一:客户端navmesh 和 服务端A*算法的转换

这是以前项目用到的C#版的navmesh。从那个开源的C++版本navmesh改过来的

timotimosky/-Navmesh_Csharp

public void Sync()
    {
 
        //判断是否处于寻路//
        if (LocusPoints.Count < 2)
        {
            return;
        }
             
        //获取下一个路径点//
        Vector3 nextPosition = LocusPoints[0];
        Vector3 nextPosition2 = LocusPoints[1];
 
        //如果两者大于一个格子,则插点//
        if (Vector3.Distance(nextPosition ,nextPosition2) > 0.5f)
        {
            // * 方向//
            Vector3 directionVector3 = nextPosition2 - nextPosition;
 
            //预测下一个路径点//
            Vector3 targetVector3 = directionVector3.normalized * 0.5f + nextPosition;
 
            if (targetVector3.x == 0 && targetVector3.z == 0)
LocusPoints.Insert(1, targetVector3);
        }
      
 
        //TODo:和服务端同步一次位置//
 
 
        //记录上一个出发点,用于平滑路径
        NewLocusPointRotation = SelectedLightProjector.transform.rotation;
        NewLocusPointPosition = SelectedLightProjector.transform.position;
 
        NewLocusPointStartTime = Time.fixedTime;
 
mFollowWayPointsState.begin = true;
 
    }

上面这段代码,实际上就是每隔一定距离,就对Navmesh的路径点进行插点,然后转换为A星的格子位置。

二: A*算法的障碍碰撞

一般的A*算法代码是把所有障碍当做一个格子。但某些时候有的物体大,有的物体小,碰撞范围大小不一,那么需要动态传递参数

public bool CanReach(int x, int y)
{
 
    ///加入自身碰撞器后,需要判断该点周围 全无阻挡///
    for (int i=-selfBlock; i<=selfBlock; i++)
    {
        for(int j=-selfBlock; j<=selfBlock; j++)
        {
            if(MazeArray[x+i, y+j]<=0)
                return false;
        }
    }
    return true;
}

三:客户端navmesh 和 A*算法的混用

因为项目的某些需求,使用单一的寻路方式,性能或者效果可能不好,那么可以混用几种寻路方式。

两种思路:

1.使用navmesh检测静态障碍,在这段代码内部,再使用A*专门检测动态障碍,调整navmesh的路径。

2. 因为一些特殊需求,比如寻找NPC,使用navmesh检测长范围的寻路,在路径的最后一段,使用A*检测小范围的障碍。

三:优化特定需求的A*算法

1.平滑A型算法的路径

2.A*算法的格子静态合并、优化A*算法的寻路性能

3. A*寻路向navmesh寻路的演进(navmes设计思路)

4.A*工具化


平滑A型算法的路径

如图1

我使用一种简单的情况来说明,假设在地图上寻路,如果从A点(灰色格子)寻到X点,那么使用普通A*算法寻路出来可能是这样的
现在格子很不规则,我们想使路径平滑,一般采用的策略是数个角度差异较大的路径点之间,通过插点,替换路径点,来实现。
我们现在采用另一种方式,

步骤1:
先对所有路径点循环,如果两个顶点,比如B-C矢量为垂直/平行 于地图起始的 1-2顶点矢量,则 B-C顶点合并。

图片2


我们可以看到,B-C-D-E与1-2顶点矢量平行,所以可以合并为BCDE点,F-G点与1-2顶点矢量垂直,所以F-G合并为FG点。(这里要注意, ABCDE---F矢量 与ABCDE本身矢量不同,不能再合并。所以中断合并,从F点重新判断,F和G点合并了)

步骤2:
最后我们原本的路径点为: A-B-C-D.....-Y-X 一共10多个路径点, 合并后,为 ABCDE--FG---HOI---JKM---NOX 一共五个点,路径点大大减少
虽说对行走效率有一定优化,但人物按这个路径点来行走,路线并没有平滑。

步骤3:
其实我们可以将ABCDE当做一个大方块,同样的道理,所以这个寻路路径变成了5个大方块的寻路。
我们先将我们理想中的平滑路径画出来,现在看图3


其实我们可以看到黄色路径比较接近我们的想法。
但是黄色点,并不在我们的寻路结果内,所以我们要将黄色点纳入我们的五个大方块里去,
于是成了图3的样子,我们用绿色格子将黄色部分纳入。
我们可以发现,其实现在变成了3个更大的方块(红色圈内)的寻路。我们把三个圈命名为 a b c.
5个大方块朝3个更大方块的演进,实际上是将拐角磨平(比如FGH的拐角被纳入第二个红圈内了)
实际上,就是判断,当矢量FG与下一个点H有拐角时,判断FG与H点的斜上方所有点是否可走,如果可走,则FG与H点可以合并成一个大方块。
则 我们计算把 a- b - c 三个长方形的公共点(如果有公共边,则公共边的中点为公共点,如果只有相邻角,则角作为这个圈到下一个圈的路径点)作为必经点
则新的路径形成, a长方形终点 - b长方形起 ---- c起点---x(x属于C内部)

其实,到这里为止,这已经是一个简化版的动态navmesh寻路算法。


navmesh也是多个区域块行走。但不是使用矩形,而是使用三角形。
navmesh的三角形有一个严格规定,每个三角形的三个顶点所形成的圆形内不能包含其他三角形的顶点(delaunay算法 http://baike.baidu.com/link?url=m1uEA28GLx-NjrhXlozvcJ03-hmXTIUMo03D99bf-4_nEF7Rsrd2h6F-Pq9HpVatIWlQNSOWfMmZs6jlLP5KkK
这样做有个好处,其实navmesh的实际路径不是从一个三角型寻路到另一个三角型,本质上是从一个圆到另一个圆,圆与圆之间有且只有一个交点,这样保证了在很多情况下路径比矩形寻路更短更平滑(长方形无法形成一个不包含其他长方形顶点的圆,所以相邻矩形的内部路径之间的夹角更大)


所以如果我们把矩形拆分成三角型,更接近U3D自带的navmesh算法。

U3D的navmesh烘焙,是读取网格数据后,使用该delaunay算法直接生成三角型,剩余步骤相同。


A*算法的格子静态合并
刚才是先进行寻路,再进行格子的动态合并,对效率影响很大,其实将上面的合并步骤拿出来,使用地图格子之前,先对所有地图格子进行合并,然后生成一个很多矩形的新地图格子。
即可达成格子的静态合并。 然后使用中,直接使用这些格子进行A*寻路,能大大提高效率。但是,静态合并后,如果不单独处理动态障碍,无法识别动态障碍,这也是NAVmesh的通病。

A*地图格子生成工具化
简单说,
1.将所有地图上可行走区域设置为(walk层级),障碍的层级设置为nowalk
2.用摄像机对地图循环扫描(每次扫描x坐标+1,下一次y+1,轮流加坐标),扫描到walk,xml上写0,扫描到nowalk,xml上写1.
(当然,还可以有减速行走的沼泽,则xml可以写2,以此类推,可以无限多地形。顺便提一句,草丛视野的实现,比如可以将草丛部分设置为3,人物中心点处于草丛格子,则判断人物进入草丛。)

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值