///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // 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; } }