A*寻路算法——优化

1. 格子节点管理类(主要寻路逻辑)

  1. 针对开启列表OpenList的排序次数进行优化,减少排序的次数。
  2. 针对开启列表OpenList和关闭列表CloseList进行的判是否存在该节点进行优化,用一个数组记录已经在列表的格子,因为数组查找的效率比较高,用空间换时间。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;

public class AStarOptimizeMgr : BaseSingleton<AStarOptimizeMgr>
{
    
    
    private int m_Width;
    private int m_Height;

    private AStarNode[,] m_Nodes;

    public AStarNode[,] Nodes
    {
    
    
        get {
    
     return m_Nodes; }
    }
    private List<AStarNode> m_OpenList = new List<AStarNode>();
    private List<AStarNode> m_CloseList = new List<AStarNode>();
    private int m_RecordSortNum;//记录排序的数量
    private int m_CalculateOrderNum;//计算OpenList的排序次数,用来比较优化
    private AStarNode[,] m_RecordNodes;//记录已经在m_OpenList或者在m_CloseList中的节点
    /// <summary>
    /// 初始化地图信息
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    public void InitMapInfo(int width, int height)
    {
    
    
        Debug.Log("InitMapInfo");
        m_Width = width;
        m_Height = height;
        if (m_Nodes == null)
        {
    
    
            m_Nodes = new AStarNode[width, height];
            m_RecordNodes = new AStarNode[width, height];
        }

        for (int i = 0; i < width; i++)
        {
    
    
            for (int j = 0; j < height; j++)
            {
    
    
                AStarNode pNode;
                if (i == 0 || i == width - 1 || j == 0 || j == height - 1)
                {
    
    
                    pNode = new AStarNode(i, j, E_Node_Type.Boundary);
                }
                else
                {
    
    
                    E_Node_Type eType = _GetRandRomNodeType();
                    pNode = new AStarNode(i, j, eType);
                }
                m_Nodes[i, j] = pNode;

            }
        }
    }

    /// <summary>
    /// 计算寻路
    /// </summary>
    /// <param name="startPos"></param>
    /// <param name="endPos"></param>
    /// <returns></returns>
    public List<AStarNode> CalculateFindPath(Vector2Int startPos, Vector2Int endPos)
    {
    
    
        //目前只考虑坐标都是整数情况

        int iStartX = startPos.x;
        int iStartY = startPos.y;
        int iEndX = endPos.x;
        int iEndY = endPos.y;
        //如果超出界限
        if (_IsOverBound(iStartX, iStartY) || _IsOverBound(iEndX, iEndY))
        {
    
    
            Debug.LogError("所在点超出界限");
            return null;
        }
        AStarNode pStartNode = m_Nodes[iStartX, iStartY];
        AStarNode pEndNode = m_Nodes[iEndX, iEndY];
        //如果是阻挡
        if (pStartNode.type == E_Node_Type.Stop || pEndNode.type == E_Node_Type.Stop)
        {
    
    
            Debug.LogError("所在点是阻挡点,不合规范");
            return null;
        }

        //寻路逻辑
        m_CalculateOrderNum = 0;
        m_RecordSortNum = 0;
        m_OpenList.Clear();
        m_CloseList.Clear();

        pStartNode.nodeParent = null;
        pStartNode.f = 0;
        pStartNode.g = 0;
        pStartNode.h = 0;
        //首先把起点放到关闭列表
        m_CloseList.Add(pStartNode);
        m_RecordNodes[iStartX, iStartY] = pStartNode;
        AStarNode pCurrenNode = pStartNode;
        float fTime = Time.realtimeSinceStartup;
        while (pCurrenNode != pEndNode)
        {
    
    
            //左上
            _AddNearNodeToOpenList(pCurrenNode.x - 1, pCurrenNode.y - 1, 1.4f, pCurrenNode, endPos);
            //上
            _AddNearNodeToOpenList(pCurrenNode.x - 1, pCurrenNode.y, 1.0f, pCurrenNode, endPos);
            //右上
            _AddNearNodeToOpenList(pCurrenNode.x - 1, pCurrenNode.y + 1, 1.4f, pCurrenNode, endPos);
            //左
            _AddNearNodeToOpenList(pCurrenNode.x, pCurrenNode.y - 1, 1.0f, pCurrenNode, endPos);
            //右
            _AddNearNodeToOpenList(pCurrenNode.x, pCurrenNode.y + 1, 1.0f, pCurrenNode, endPos);
            //左下
            _AddNearNodeToOpenList(pCurrenNode.x + 1, pCurrenNode.y - 1, 1.4f, pCurrenNode, endPos);
            //下
            _AddNearNodeToOpenList(pCurrenNode.x + 1, pCurrenNode.y, 1.0f, pCurrenNode, endPos);
            //右下
            _AddNearNodeToOpenList(pCurrenNode.x + 1, pCurrenNode.y + 1, 1.4f, pCurrenNode, endPos);

            if (m_OpenList.Count == 0)
            {
    
    
                Debug.LogError("死路");
                return null;
            }
            if (m_RecordSortNum <= 0)//当排序数量小于等于零时
            {
    
    
                m_OpenList.Sort((x, y) => x.f.CompareTo(y.f)); //升序排序
                m_CalculateOrderNum++;
                m_RecordSortNum = m_OpenList.Count;
            }
            else
            {
    
    
                m_RecordSortNum--;
            }

            pCurrenNode = m_OpenList[0];
            m_CloseList.Add(pCurrenNode);
            m_OpenList.RemoveAt(0);
        }
        Debug.LogFormat("用时:{0}", Time.realtimeSinceStartup - fTime);
        Debug.Log("排序次数" + m_CalculateOrderNum);
        List<AStarNode> lstPathNode = new List<AStarNode>();
        AStarNode pNode = pEndNode;
        while (pNode != null)
        {
    
    
            lstPathNode.Add(pNode);
            pNode = pNode.nodeParent;
        }
        lstPathNode.Reverse();
        return lstPathNode;
    }

    /// <summary>
    /// 随机地块类型
    /// </summary>
    /// <returns></returns>
    private E_Node_Type _GetRandRomNodeType()
    {
    
    
        int iRandNum = UnityEngine.Random.Range(0, 10);
        if (iRandNum >= 0 && iRandNum < 8)
            return E_Node_Type.Pass;
        else
            return E_Node_Type.Stop;
    }

    /// <summary>
    /// 判断是否超出边界
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    private bool _IsOverBound(int x, int y)
    {
    
    
        if (x < 0 || x >= m_Width || y < 0 || y >= m_Height)
            return true;
        else
            return false;
    }

    /// <summary>
    /// 判断是否是阻碍
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    private bool _IsCanPass(int x, int y)
    {
    
    
        E_Node_Type type = m_Nodes[x, y].type;
        if (type == E_Node_Type.Stop)
            return false;
        else
            return true;
    }

    /// <summary>
    /// 获取曼哈顿距离
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="endPos"></param>
    /// <returns></returns>
    private int _GetManhattanDistance(int x, int y, Vector2Int endPos)
    {
    
    
        int iHorizontal = Mathf.Abs(endPos.x - x);
        int iVertical = Mathf.Abs(endPos.y - y);
        return iHorizontal + iVertical;
    }

    /// <summary>
    ///  把周围节点添加到开放列表中
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="parentNode"></param>
    private void _AddNearNodeToOpenList(int x, int y, float g, AStarNode parentNode, Vector2Int endPos)
    {
    
    
        if (!_IsOverBound(x, y) && _IsCanPass(x, y))
        {
    
    
            AStarNode pNode = m_Nodes[x, y];
            if (m_RecordNodes[x, y] != pNode)
            {
    
    
                pNode.nodeParent = parentNode;
                pNode.g = parentNode != null ? g + parentNode.g : g;
                pNode.h = _GetManhattanDistance(x, y, endPos);
                pNode.f = pNode.g + pNode.h;

                if (m_RecordSortNum > 0)
                {
    
    
                    if (pNode.f < m_OpenList[0].f)
                        m_OpenList.Insert(0, pNode);
                    else
                        m_OpenList.Add(pNode);
                }
                else
                {
    
    
                    m_OpenList.Add(pNode);
                }
                m_RecordNodes[x, y] = pNode;
            }
        }
    }


}



猜你喜欢

转载自blog.csdn.net/weixin_42205218/article/details/114805648