1. 程式人生 > >Unity NavMesh尋路檢測的bug(或者特性),爬坡卡住問題。(角色高度和網格高度不一致造成)

Unity NavMesh尋路檢測的bug(或者特性),爬坡卡住問題。(角色高度和網格高度不一致造成)


Unity專案,由於人物移動時一般用搖桿或者方向鍵控制, 需要有八方向方式控制朝向,  所以沒有用 NavMesh Agent, 而是自己控制人物方向移動,然後貼合地面。

用了NavMesh.CalculatePath只是用於目標點的尋路, 尋找出路經後自己計算實現移動。


實際專案中發現,方向鍵控制移動後,到了邊界碰撞檢測後,人物就停在那裡。 就算與障礙物很小一個夾角,也會卡在那裡,體驗很不好。


打個部分,比如上圖,角色在紅色點, 方向鍵往綠色方向移動,結果角色檢測到碰撞,就不移動, 一直停在了紅點。  希望它能按藍色箭頭方向緩慢移動。 怎麼實現呢?

用幾何的 什麼切線啥的好像太複雜了, 數學學得不怎麼好。    後來有同事提示,可以用尋路網格的輔助, 確實是一個好辦法。 

NavMesh 想要從紅點到不可行走的綠點, 會自動尋找一個路徑到藍色點附近。 程式碼調整移動朝向沿著藍線走就行了。


實際試下來效果還行,除了某些網格的凹角仍然會卡住。覺得可以交差時,意想不到的問題出現了,  爬坡(樓梯)時,會在明顯可以直接穿過去的地方,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)
        {
            //尋路網格認為不可走了,再用不可行走區域做一下檢測,防止很陡的空的卡住
        }