Unity NavMesh寻路检测的bug(或者特性),爬坡卡住问题。(角色高度和网格高度不一致造成)


Unity项目,由于人物移动时一般用摇杆或者方向键控制, 需要有八方向方式控制朝向,  所以没有用 NavMesh Agent, 而是自己控制人物方向移动,然后贴合地面。

用了NavMesh.CalculatePath只是用于目标点的寻路, 寻找出路经后自己计算实现移动。


实际项目中发现,方向键控制移动后,到了边界碰撞检测后,人物就停在那里。 就算与障碍物很小一个夹角,也会卡在那里,体验很不好。

Unity NavMesh寻路检测的bug(或者特性),爬坡卡住问题。(角色高度和网格高度不一致造成)_第1张图片

打个部分,比如上图,角色在红色点, 方向键往绿色方向移动,结果角色检测到碰撞,就不移动, 一直停在了红点。  希望它能按蓝色箭头方向缓慢移动。 怎么实现呢?

用几何的 什么切线啥的好像太复杂了, 数学学得不怎么好。    后来有同事提示,可以用寻路网格的辅助, 确实是一个好办法。 

NavMesh 想要从红点到不可行走的绿点, 会自动寻找一个路径到蓝色点附近。 代码调整移动朝向沿着蓝线走就行了。


实际试下来效果还行,除了某些网格的凹角仍然会卡住。觉得可以交差时,意想不到的问题出现了,  爬坡(楼梯)时,会在明显可以直接穿过去的地方,2个网格交界的地方一根网格线卡住

Unity NavMesh寻路检测的bug(或者特性),爬坡卡住问题。(角色高度和网格高度不一致造成)_第2张图片

比如上图,往绿色的方向移动,结果它计算出来的路径却是沿着这根网格线 红色箭头方向慢慢移动。。。。遇到了空气墙一样,囧。


打了很多日志,发现它确实计算出这个路径来了,而且只有2个corner(起点和目标点)。

发现只要路径移动远一点 就能计算正确,如果移动很短,在这种上坡的边缘出错概率就很大(下坡正常)。 这难道是navmesh bug? 不应该,网上也没人提到过这个问题。


仔细看了坐标, 发现角色的y坐标 还有移动后的目标点 y 坐标,和寻路网格的高度不一样。 是不是这个原因造成的呢?


怎么找到自己的点在网格上的投射点呢?  用 NavMesh.RayCast 发现不能满足我的要求, 还是会有问题。


后来发现一个接口,NavMesh.SamplePosition

static function SamplePosition (sourcePosition : Vector3, out hit : NavMeshHit, maxDistance : float, allowedMask : int) : boolean

Parameters

   
sourcePosition The origin of the sample query.
hit Holds the properties of the resulting location.
maxDistance Sample within this distance from sourcePosition.
allowedMask A mask specifying which NavMesh layers are allowed when finding the nearest point.

Returns

boolean - True if a nearest point is found.

Description

Sample the NavMesh closest to the point specified.


在NavMesh 最近的一个可行走点, 好像是我需要的,试了一试发现解决了这个BUG。



参考代码:

        m_movement = moveVelocity * deltaTime;  //这次要移动的方向和距离
        Vector3 cur_position = transform.position;
        NavMeshHit hit;
        if (NavMesh.SamplePosition(cur_position, out hit, 0.5f, -1))
        {
            cur_position = hit.position;    //校准起始点
        }
        Vector3 temp = cur_position + m_movement;
        if (NavMesh.SamplePosition(temp, out hit, 0.5f, -1))
        {
            temp = hit.position;    //校准目标点
        }

        bool cannot_move = false;
        //用NavMesh计算目标点可否过去,
        if (!NavMesh.CalculatePath(cur_position, temp, Layer.NavWalkableMask, path))
        {
            //m_movement = Vector3.zero;
            cannot_move = true;
        }
        else
        {
            if (path.corners.Length < 2)
            {
                if (path.corners.Length == 1)   //有时会寻路出一个点的情况,这个还没好好研究
                {
                    m_movement = path.corners[0] - cur_position;
                    m_movement.y = 0;
                }
                else
                {
                    //m_movement = Vector3.zero;
                    cannot_move = true;
                }
            }
            else
            {
                m_movement = path.corners[1] - cur_position;  //调整方向
                m_movement.y = 0;
                moveTargetDirection = m_movement.normalized;
                float dot = Vector3.Dot(moveTargetDirection, moveVelocity.normalized);   //新旧方向的夹角余弦值, 可以算出在新方向的速度分量
                moveVelocity = moveTargetDirection * moveSpeed * dot;   //移动朝向也要跟着调整
                m_movement = moveVelocity * deltaTime;
            }
        }

        if (cannot_move)
        {
            //寻路网格认为不可走了,再用不可行走区域做一下检测,防止很陡的空的卡住
        }


你可能感兴趣的:(Unity,NavMesh)