各位看官们好呀~AStar算法一搜一大把,大神们讲的都很详细,不过启发函数会影响算法的性能。简而言之我这个AStar在权值计算的时候加了判断去看看目前的路径的权值是否已经大于目前已经找到的最短路径权值,可以有效减少遍历的成本。如果有大神感觉这样写不对,欢迎留言给予指教丫。
ps.排序用到了之前帖子写的堆排序,配合食用更加哟。排序工具类
AStar.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 此A*算法用于路径搜索,故代价计算公式便是:当前节点权值 = 初始节点到当前节点的代价 + 当前节点到目标节点的距离
/// 此类可以不继承MonoBehavior直接使用对应方法,将对应方法以及变量转为静态即可。
/// </summary>
namespace AlinTools {
public class AStar : MonoBehaviour{
public Vector2Int mapSize;//地图格子行列数
public Vector3 centerWorldPos;//地图中心世界坐标
public float cellSize;//格子大小
public Vector2Int startPos;//开始坐标
public Vector2Int aimPos;//目标坐标
[HideInInspector]
public AStarCell[,] map;//地图
private float distance = float.MaxValue;//当前最短路径距离
private List<AStarCell> nearestWay;//当前最短路径
/// <summary>
/// 开始A*寻路,定制地图的话只需要赋值mineMap即可,使用默认值只需头尾坐标,格子不需要定制但想改地图属性只需要将mineMap赋值为null然后再对对应值进行赋值即可
/// </summary>
/// <param name="startPos">寻路开始的点</param>
/// <param name="aimPos">寻路目标点</param>
/// <param name="mineMap">定制地图</param>
/// <param name="mapSizeX">地图宽</param>
/// <param name="mapSizeY">地图高</param>
/// <param name="posX">地图中心世界坐标x值</param>
/// <param name="posY">地图中心世界坐标y值</param>
/// <param name="posZ">地图中心世界坐标z值</param>
/// <param name="cellSize">格子大小</param>
public void StartFindWay(Vector2Int startPos, Vector2Int aimPos, AStarCell[,] mineMap = null,int mapSizeX = 0,int mapSizeY = 0,float posX = 0,float posY = 0,float posZ = 0,float cellSize = 0)
{
if (mapSizeX != 0 && mapSizeY != 0)
{
mapSize = new Vector2Int(mapSizeX, mapSizeY);
}
else if(mapSize.x==0||mapSize.y == 0)
{
mapSize = new Vector2Int(7, 7);
// Debug.LogError("地图大小出错,默认使用7*7大小");
}
if(centerWorldPos == Vector3.zero||posX!=0||posY!=0||posZ!=0)
{
centerWorldPos = new Vector3(posX, posY, posZ);
}
if(this.cellSize == 0&&cellSize!=0)
{
this.cellSize = cellSize;
}
else if(this.cellSize == 0&&cellSize == 0)
{
this.cellSize = 10;
//Debug.LogError("地图格子大小出错,默认使用10大小");
}
distance = float.MaxValue;
nearestWay = new List<AStarCell>();
if (mineMap == null)
{
map = new AStarCell[mapSize.x, mapSize.y];
Vector2 centerPos = new Vector2(map.GetLength(0) / 2f, map.GetLength(1) / 2f);
for (int i = 0; i < map.GetLength(0); i++)
{
for (int j = 0; j < map.GetLength(1); j++)
{
map[i, j] = new AStarCell()
{
isBarrier = false,
cellPos = new Vector2Int(i, j),
worldSpacePos = new Vector3(centerWorldPos.x + (i - centerPos.x) * cellSize, centerWorldPos.y + (j - centerPos.y) * cellSize, centerWorldPos.z)
};
}
}
}
else
{
map = mineMap;
}
this.startPos = startPos;
this.aimPos = aimPos;
AStarFindWay(map[startPos.x, startPos.y], new List<AStarCell>());
}
/// <summary>
/// A*算法
/// </summary>
/// <param name="nowCell">当前格子</param>
/// <param name="logCells">记录走过的格子</param>
/// <returns></returns>
private bool AStarFindWay(AStarCell nowCell, List<AStarCell> logCells)
{
List<AStarCell> nextCells = new List<AStarCell>();
bool isAlreadyGet = false;
nowCell.logWay = true;
float nowDistance;
logCells.Add(nowCell);
for (int i = nowCell.cellPos.x - 1; i <= nowCell.cellPos.x + 1; i++)
{
for (int j = nowCell.cellPos.y - 1; j <= nowCell.cellPos.y + 1; j++)
{
if (i >= 0 && i < map.GetLength(0) && j >= 0 && j < map.GetLength(1)&&(new Vector2Int(i,j) !=nowCell.cellPos)&&!map[i, j].isBarrier&& !map[i, j].logWay)//筛选周围九宫格内能走的
{
if(map[i, j].cellPos == aimPos)//如果周围九宫格已经有了终点便结束此次遍历
{
isAlreadyGet = true;
break;
}
nextCells.Add(map[i, j]);
}
}
if (isAlreadyGet)
{
break;
}
}
if (isAlreadyGet)
{
nowDistance = nowCell.realityWeightedValue;
if (nowDistance < distance)//判断此时是否最短路径
{
distance = nowDistance;
nearestWay = new List<AStarCell>(logCells);
nowCell.logWay = false;
logCells.RemoveAt(logCells.Count - 1);
return true;
}
else
{
nowCell.logWay = false;
logCells.RemoveAt(logCells.Count - 1);
return false;
}
}
nowDistance = nowCell.realityWeightedValue + nowCell.forecastWeightedValue;
if ((nowDistance!=0&& nowDistance > distance)|| nextCells.Count <= 0)//如果现在的权值已经大于最短路径或者无路可走便结束这趟遍历
{
nowCell.logWay = false;
logCells.RemoveAt(logCells.Count - 1);
return false;
}
WeightedValue(nextCells, nowCell);//还未到终点便计算各点权值
bool isThisPointCanArriveAtAim = false;
foreach(var v in nextCells)
{
if (AStarFindWay(v, logCells))//按照权值低到高遍历周围所有点
{
isThisPointCanArriveAtAim = true;
}
}
nowCell.logWay = false;
logCells.RemoveAt(logCells.Count - 1);
return isThisPointCanArriveAtAim;
}
/// <summary>
/// 权值计算
/// </summary>
/// <param name="nextCells">周边各个点</param>
/// <param name="nowCell">现在遍历的点</param>
/// <param name="isNeedSort">是否需要排序</param>
private void WeightedValue(List<AStarCell> nextCells,AStarCell nowCell, bool isNeedSort = true)
{
foreach(var v in nextCells)
{
v.realityWeightedValue = nowCell.realityWeightedValue + Vector3.Distance(nowCell.worldSpacePos, v.worldSpacePos);
v.forecastWeightedValue = Vector3.Distance(v.worldSpacePos, map[aimPos.x,aimPos.y].worldSpacePos);
}
if (isNeedSort)
{
SortTools.HeapSort(nextCells, false);
}
}
}
/// <summary>
/// A*格子数据结构
/// </summary>
public class AStarCell : System.IComparable<AStarCell> {
public bool isBarrier = false;//是否障碍物
public Vector2Int cellPos;//格子中的坐标
public Vector3 worldSpacePos;//世界坐标位置
public bool logWay = false;//记录是否已经走过
public float realityWeightedValue;//实际加权值
public float forecastWeightedValue;//预测加权值
/// <summary>
/// 实现比较方法
/// </summary>
/// <param name="other">对比的数据</param>
/// <returns></returns>
public int CompareTo(AStarCell other)
{
if (realityWeightedValue + forecastWeightedValue > other.forecastWeightedValue + other.realityWeightedValue)
{
return -1;
}
else
{
return 1;
}
}
}
}