Unity A*寻路三个简单实用的算法

17 篇文章 0 订阅

1.怎么判断直线通往目标的路径上有障碍物?

这种情况下,DDA算法比A*更快,如果没有障碍物,那么直接前进即可,不用A*算法,对于coc这种百人寻路是可以节省不少性能的。

代码如下:

public bool CheckCanGoForward(Vector3 startPos, Vector3 endPos, int extNum)
        {
            //init: gather starting location information.
            int startCell = GetCellIndex(startPos);
            int lastCell = GetCellIndex (endPos);
            if(lastCell < 0)
            {
                Debug.Log(“endPos is valid”);
                return false;
            }

            float maxDis = (endPos - startPos).sqrMagnitude;

            Vector2 dir = new Vector2 (endPos.x - startPos.x, endPos.z - startPos.z);
            bool bInBounds = ( startCell >= 0 && startCell < NumberOfCells );
            if (!bInBounds)
            {
                System.Diagnostics.Debug.Assert(bInBounds, “starting position of the ray is not in bounds in call to Raycast2D” +
                                                “Add logic to find the starting cell when the ray position starts out of bounds”);
                return false;
            }
            int X = GetColumn(startCell);
            int Y = GetRow(startCell);
            int stepX = Math.Sign(dir.x);
            int stepY = Math.Sign(dir.y);
            float nearestGridX = (stepX < 0) ? (startPos.x) : (startPos.x + m_cellSize);
            float nearestGridY = (stepY < 0) ? (startPos.z - m_cellSize) : (startPos.z);
            float thetaInDegrees = Vector3.Angle( kXAxis, dir);
            float thetaInRadians = thetaInDegrees * Mathf.Deg2Rad;
            float cosTheta = Mathf.Cos( thetaInRadians );
            float sinTheta = Mathf.Sin( thetaInRadians );
            //parametric form requires taking ray.Position as the origin, hence the ” - ray.Position”
            float tMaxX = Math.Abs((nearestGridX - startPos.x) / cosTheta);
            float tMaxY = Math.Abs((nearestGridY - startPos.z) / sinTheta);
            float tDeltaX = Math.Abs(m_cellSize / cosTheta);
            float tDeltaY = Math.Abs(m_cellSize / sinTheta);
            
            //loop: traverse the cells until there is a collision or ray is out of bounds.
            bool bHitMapEdge = false;
            int prevX = X;
            int prevY = Y;
            int maxCount = 100;
            int nowCount = 0;
            while (nowCount < maxCount)
            {
                nowCount++;
                if (tMaxX < tMaxY)
                {
                    prevX = X;
                    tMaxX += tDeltaX;
                    X += stepX;
                }
                else
                {
                    prevY = Y;
                    tMaxY += tDeltaY;
                    Y += stepY;
                }

                Vector3 nowPos = startPos + new Vector3(stepX * tMaxX, 0f, stepY * tMaxY);
                int cell = GetCellIndex(nowPos);
                if(!checkCanGo(cell, extNum, null))
                {
                    return false;
                }

                if((nowPos - startPos).sqrMagnitude >= maxDis)
                {
                    return true;
                }

            }
            Debug.Log (“can not find forward pos to Go“);
            return false;
        }

2.如果目标点是不可寻路点,怎么找到最近的一点可行走区域。采用螺旋型搜索,可以最快找到目标。

public Vector3 GetNearestCanGoPos(Vector3 position, int extNum, Dictionary<int, bool> otherAgent)
        {
            // Save this value off, in case we need to use it to search for a valid location further along in this function.
            Vector3 originalPosition = position;
            int cellIndex = GetCellIndex(position);
            if(cellIndex == SimpleAI.Planning.Node.kInvalidIndex)
            {
                Debug.Log(“targetPos is out of bounds”);
                return Vector3.zero;
            }
            int maxCount = 25;
            int nowCount = 0;
            int len = 1;
            if (checkCanGo(cellIndex, extNum, otherAgent))
            {
                return position;
            }
            eNeighborDirection dir = eNeighborDirection.kTop;
            int index = cellIndex;
            while(nowCount < maxCount)
            {
                nowCount ++;
                if(len == 0)
                {
                    len = Mathf.FloorToInt((nowCount + 1) / 2);
                    switch(dir)
                    {
                    case eNeighborDirection.kTop:
                        dir = eNeighborDirection.kLeft;
                        break;
                    case eNeighborDirection.kLeft:
                        dir = eNeighborDirection.kBottom;
                        break;
                    case eNeighborDirection.kBottom:
                        dir = eNeighborDirection.kRight;
                        break;
                    case eNeighborDirection.kRight:
                        dir = eNeighborDirection.kTop;
                        break;
                    }
                }
                index = GetNeighbor(index, dir);
                if(checkCanGo(index, extNum, otherAgent))
                {
                    return GetCellPosition(index);
                }
                len–;
            }

            return Vector3.zero;

        }


3.一个物体,怎么判断它踩住了多少个格子。特别是不规则的物体。

首先,你要计算出这个物体的边界,剩下就是对边界内的所有格子进行判断,那么我们怎么知道这个格子到底是被物体踩住了,还是空心的呢?

利用射线,可以很快得出结果:

public int[] GetObstructedCells(Grid grid, out int numObstructedCells)
    {
        numObstructedCells = 0;
        
        if ( collider == null )
        {
            return null;
        }
        
        Bounds bounds = collider.bounds;
        bounds.Expand(m_scale);
        
        // lowerLeftPos Represents the center of the first cell that is covered, in the lower left corner. We march right and up
        // from this cell.
        Vector3 upperLeftPos = new Vector3( bounds.min.x, grid.Origin.y, bounds.max.z );
        Vector3 upperRightPos = new Vector3( bounds.max.x, grid.Origin.y, bounds.max.z );
        Vector3 lowerLeftPos = new Vector3( bounds.min.x, grid.Origin.y, bounds.min.z );
        Vector3 lowerRightPos = new Vector3( bounds.max.x, grid.Origin.y, bounds.min.z );
        Vector3 horizDir = (upperRightPos - upperLeftPos).normalized;
        Vector3 vertDir = (upperLeftPos - lowerLeftPos).normalized;
        float horizLength = bounds.size.x;
        float vertLength = bounds.size.z;

        
        UpdateObstructedCellPool(grid);
        
        // Determine which cells are actually obstructed
        for ( int rowCount = 0; rowCount < m_numObstructedCellPoolRows; rowCount++ )
        {
            float currentVertLength = rowCount * grid.CellSize;
            
            for ( int colCount = 0; colCount < m_numObstructedCellPoolColumns; colCount++ )
            {
                float currentHorizLength = colCount * grid.CellSize;
                Vector3 testPos = lowerLeftPos + horizDir * currentHorizLength + vertDir * currentVertLength;
                testPos.x = Mathf.Clamp(testPos.x, bounds.min.x, bounds.max.x);
                testPos.z = Mathf.Clamp(testPos.z, bounds.min.z, bounds.max.z);
                if ( grid.IsInBounds(testPos) )
                {
                    Vector3 above = testPos + (Vector3.up * 0.5f);
                    Vector3 below = testPos + (Vector3.down * 0.5f);
                    
                    if (Physics.CheckCapsule(above, below, grid.CellSize / 2f - 0.1f, 1 << LayerMask.NameToLayer(“Default”)))
                    {
                        int obstructedCellIndex = grid.GetCellIndex(testPos);
                        m_obstructedCellPool[numObstructedCells] = obstructedCellIndex;
                        numObstructedCells++;
                    }
                }
                
                if ( currentHorizLength > horizLength )
                {
                    break;
                }
            }
            
            if ( currentVertLength > vertLength )
            {
                break;
            }
        }
        
        return m_obstructedCellPool;
    }

by张伟
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值