A*寻路算法——优化
·
1. 格子节点管理类(主要寻路逻辑)
- 针对开启列表OpenList的排序次数进行优化,减少排序的次数。
- 针对开启列表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;
}
}
}
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)