/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// D E R E L I C T
//
/// // (c) 2003..2025
using Godot;
///
/// A path follower that treats the agent as a sphere. The target point is the
/// intersection of this sphere with the current path segment.
///
public class SpherePathFollower
{
private Vector3[] _path;
private float _radius = 4.0f;
private int _currentIndex = 0;
public Vector3[] Path => _path;
public int Index => _currentIndex;
public int MaxIndex => _path.Length;
///
/// Checks if the agent has reached the end of the path.
///
public bool IsFinished => _currentIndex >= _path.Length;
///
/// Checks if the agent has reached the end of the path.
///
public bool IsNearlyFinished => _currentIndex >= ( _path.Length - 1 );
///
/// Initializes a new path follower with a path and a sphere radius.
///
/// The array of Vector3 points to follow.
/// The radius of the agent's collision/interaction sphere.
public SpherePathFollower( float sphereRadius )
{
_radius = sphereRadius;
SetPath( new Vector3[0], sphereRadius );
}
///
/// Initializes a new path follower with a path and a sphere radius.
///
/// The array of Vector3 points to follow.
/// The radius of the agent's collision/interaction sphere.
public SpherePathFollower( Vector3[] path, float sphereRadius )
{
SetPath( path, sphereRadius );
}
///
/// Assigns a new path and radius, resetting the follower's progress.
///
public void SetPath( Vector3[] newPath )
{
SetPath( newPath, _radius );
}
///
/// Assigns a new path and radius, resetting the follower's progress.
///
public void SetPath( Vector3[] newPath, float sphereRadius )
{
_path = newPath ?? new Vector3[0];
_radius = Mathf.Max( 0.01f, sphereRadius ); // Ensure radius is positive
_currentIndex = 0;
}
///
/// Calculates the next target point on the current path segment for the agent to move towards.
///
/// The current world position of the agent.
/// The world-space target point on the path segment.
public Vector3 GetTargetPoint( Vector3 agentPos, Vector3 goalPos, out bool advance )
{
advance = false;
if( IsNearlyFinished )
{
return goalPos;
}
Vector3 p1 = _path[_currentIndex];
Vector3 p2 = _path[_currentIndex + 1];
//DebugDraw3D.DrawLine(p1, p2, Colors.Blue);
Vector3 intersects = p2;
var intersectLine = Geo.SphereSegment( agentPos, _radius, p1, p2, out intersects, out var intersectType );
/*
if (!intersectLine)
{
intersects = p1;
intersectType = Geo.IntersectType.Before;
}
*/
//log.info($"{log.loc().Log}: Seg:({p1.Log})->({p2.Log}) On {intersectLine} Type {intersectType} To {intersects.Log}");
switch( intersectType )
{
case Geo.IntersectType.After:
advance = true;
break;
}
return intersects;
}
///
/// Updates the follower's progress along the path. Call this after the agent moves.
///
/// The agent's new world position after moving.
/// True if updated
public int Update( Vector3 agentPos, Vector3 goalPos, bool advance )
{
if( IsNearlyFinished )
{
if( !IsFinished )
return ++_currentIndex;
return _currentIndex;
}
Vector3 endOfSegment = _path[_currentIndex + 1];
// Check if the agent's sphere has reached the end of the current segment
if( advance || agentPos.DistanceSquaredTo( endOfSegment ) <= _radius * _radius )
{
++_currentIndex;
}
var targetPos = GetTargetPoint( agentPos, goalPos, out advance );
return _currentIndex;
}
///
/// Updates the follower's progress along the path. Call this after the agent moves.
///
/// The agent's new world position after moving.
/// True if updated
public int Update_orig( Vector3 agentPosition, out bool advance )
{
advance = false;
if( IsFinished )
return _currentIndex;
Vector3 endOfSegment = _path[_currentIndex + 1];
// Check if the agent's sphere has reached the end of the current segment
while( agentPosition.DistanceTo( endOfSegment ) <= _radius )
{
_currentIndex++;
advance = true;
if( IsFinished )
return _currentIndex;
endOfSegment = _path[_currentIndex + 1];
}
return _currentIndex;
}
}