最小堆
使用最小堆设计OpenList
具体实现
地图数据管理
格子对象
public abstract class BaseCell
{
/// <summary>
/// 位置信息
/// </summary>
public Vector pos { get; set; }
/// <summary>
/// 能否行走
/// </summary>
public bool walkable { get; set; }
/// <summary>
/// 簇
/// </summary>
internal Cluster cluster { get; set; }
/// <summary>
/// 邻接点对应的消耗
/// </summary>
public Dictionary<BaseCell, float> neighbours { get; set; }
/// <summary>
/// 计算邻接点
/// </summary>
/// <param name="map"></param>
/// <param name="mode"></param>
/// <param name="piercing"></param>
public void CalculateNeighbours(IMap map, NeighbourMode mode, bool piercing)
{
List<Vector> expects = new List<Vector>();
neighbours = new Dictionary<BaseCell, float>();
BaseCell next;
if (mode == NeighbourMode.Eight && piercing)
{
expects.Clear();
foreach (var offset in Vector.four)
{
next = map[pos + offset];
if (next == null || !next.walkable)
{
expects.Add(offset);
if (Vector.dicExcept.TryGetValue(offset, out Vector[] expect))
{
expects.AddRange(expect);
}
}
}
}
foreach (var offset in Vector.Neighbours(mode))
{
if (expects.Contains(offset))
{
continue;
}
next = map[pos + offset];
if (next != null && next.walkable)
{
neighbours.Add(next, Cost(next, offset));
}
}
}
/// <summary>
/// 到邻接点的消耗
/// </summary>
/// <param name="offset">方向</param>
/// <returns></returns>
protected virtual float Cost(BaseCell next, Vector offset)
{
return offset.magnitude;
}
}
K-Means聚类分析划分给地图划分簇
簇
public struct Cluster
{
public int index;
/// <summary>
/// 质心点
/// </summary>
public Vector centroid;
public List<Vector> points;
public float po;
public float pt;
}
p o = n o ( 簇内障碍数 ) / N ( 簇内格子总数 ) p_o = n_o(簇内障碍数) / N (簇内格子总数) po=no(簇内障碍数)/N(簇内格子总数)
p t = n t ( 簇内无障碍条数 ) / ( L + D ) ( 簇内总条数 ) p_t = n_t(簇内无障碍条数) / (L + D)(簇内总条数) pt=nt(簇内无障碍条数)/(L+D)(簇内总条数)
K-Means分簇
private class Clusters
{
private const int N = 128;
private int m_Count;
private Cluster[] m_Items;
public Clusters(IMap map, int count)
{
m_Count = Initialize(map, count);
KMeansPartition(map, 1);
for (int i = 0; i < m_Count; i++)
{
m_Items[i].Finish(map);
}
}
private int Initialize(IMap map, int count)
{
m_Items = new Astar.Cluster[count];
int index = 0;
BaseCell cell;
for (int i = 0; i < map.rows; i++)
{
for (int j = 0; j < map.cols; j++)
{
cell = map[i, j];
if (cell == null || !cell.walkable)
{
m_Items[index] = new Astar.Cluster(index, new Vector(i, j));
index++;
}
if (index == count)
{
return index;
}
}
}
return index;
}
private void KMeansPartition(IMap map, int depth)
{
for (int i = 0; i < m_Count; i++)
{
m_Items[i].Reset();
}
Vector v = Vector.zero;
float min, d;
int index;
for (int x = 0; x < map.rows; x++)
{
v.x = x;
for (int y = 0; y < map.cols; y++)
{
v.y = y;
min = (v - m_Items[0].centroid).sqrMagnitude;
index = 0;
for (int i = 1; i < m_Count; i++)
{
d = (v - m_Items[i].centroid).sqrMagnitude;
if (d < min)
{
min = d;
index = i;
}
}
m_Items[index].Add(v);
}
}
bool changed = false;
for (int i = 0; i < m_Count; i++)
{
if (m_Items[i].Update())
{
changed = true;
}
}
if (changed && depth < N)
{
return;
}
KMeansPartition(map, depth + 1);
}
}
评价函数设计
定义当前节点为n, 其父节点为p, 起始点为s, 终点为e
g ( n ) = g ( p ) + d i s t a n c e ( n , p ) g(n) = g(p) + distance(n,p) g(n)=g(p)+distance(n,p)
h ( n ) = c h e b y s h e v ( n , e ) h(n) = chebyshev(n,e) h(n)=chebyshev(n,e)
f ( n ) = ( p t + d s n / d s e ) ∗ g ( n ) + ( ( 1 − p o ) + e d n e / d s e f(n) = (p_t + d_sn/d_{se}) * g(n) + ((1-p_o) + e^{d_{ne}/d_{se}} f(n)=(pt+dsn/dse)∗g(n)+((1−po)+edne/dse
/// <summary>
/// 契比雪夫(对角线)
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static float Chebyshev(Vector a, Vector b)
{
float dx = Math.Abs(a.x - b.x);
float dy = Math.Abs(a.y - b.y);
if (dx > dy)
{
return D1 * (dx - dy) + D2 * dy;
}
else
{
return D1 * (dy - dx) + D2 * dx;
}
}
Astar
protected override void ScanHandle()
{
m_OpenList.Push(m_StartNode);
Node current = null;
while (m_OpenList.Count > 0)
{
if (!m_OpenList.TryPop(ref current))
{
break;
}
m_ClosedList[current.cell.pos] = current;
if (current == m_EndNode)
{
m_Result.searched = true;
break;
}
foreach (var it in current.cell.neighbours)
{
if (m_ClosedList.ContainsKey(it.Key.pos))
{
continue;
}
Node node = GetNode(it.Key);
if (node.UpdateG(current, it.Value))
{
node.parent = current;
m_OpenList.Push(node);
}
}
}
}