Represents a computed A* path. Stores the builder settings and a list of AStarNode nodes, computes cached path length, and can simplify the node list by removing intermediate nodes when a line of sight exists between farther nodes.
using System;
using System.Collections.Generic;
using System.Linq;
namespace GridAStar;
public struct AStarPath
{
public AStarPathBuilder Settings { get; internal set; }
public List<AStarNode> Nodes { get; set; } = new();
public Grid Grid => Settings.Grid;
public int Count => Nodes?.Count() ?? 0;
public bool IsEmpty => Nodes == null || Count == 0;
float length = 0f;
public float Length
{
get
{
if ( length == 0 )
{
var newLength = CalculateLength();
length = newLength;
}
return length;
}
}
public AStarPath() { }
public AStarPath( AStarPathBuilder builder, List<AStarNode> nodes ) : this()
{
Settings = builder;
Nodes = nodes;
}
public static AStarPath From( AStarPathBuilder builder, List<AStarNode> nodes ) => new AStarPath( builder, nodes );
public static AStarPath Empty() => new AStarPath();
public float CalculateLength()
{
var length = 0f;
for ( int i = 0; i < Nodes.Count - 1; i++ )
length += Nodes[i].EndPosition.Distance( Nodes[i + 1].EndPosition );
return length;
}
/// <summary>
/// Simplify the path by iterating over line of sights between the given segment size, joining them if valid
/// </summary>
public void Simplify( int segmentAmounts = 2, int iterations = 8, params string[] tagsToExclude )
{
for ( int iteration = 0; iteration < iterations; iteration++ )
{
var segmentStart = 0;
var segmentEnd = Math.Min( segmentAmounts, Count - 1 );
while ( Count > 2 && segmentEnd < Count - 1 )
{
var currentNode = Nodes[segmentStart];
var nextNode = Nodes[segmentStart + 1];
var furtherNode = Nodes[segmentEnd];
if ( !currentNode.Tags.Has( tagsToExclude ) && !nextNode.Tags.Has( tagsToExclude ) && !furtherNode.Tags.Has( tagsToExclude ) )
if ( nextNode.MovementTag == "" || nextNode.MovementTag == string.Empty || furtherNode.MovementTag == "" || furtherNode.MovementTag == string.Empty )
if ( Settings.Grid.LineOfSight( currentNode.Current, furtherNode.Current, Settings.PathCreator ) )
for ( int toDelete = segmentStart + 1; toDelete < segmentEnd; toDelete++ )
Nodes.RemoveAt( toDelete );
if ( segmentEnd == Count - 1 )
break;
segmentStart++;
segmentEnd = Math.Min( segmentStart + segmentAmounts, Count - 1 );
}
}
length = CalculateLength();
}
}