How to utilize A* pathfinding

I have a functional A* 2D pathfinder in place. The pathfinder returns a List (where Node refers to my grid node class). However, what is the best way to actually utilize it?

A have for instance an enemy that I’d like to follow the player. The enemy has its path laid out, but what should I do for him to actually follow the path?

At the moment I have something like this in my enemy class:

public Rigidbody2D rb;
private List currentPath;
  
void Update()
{
    currentPath = Pathfinder.FindPath(transform.position, player.position);
  
    if (currentPath[0].worldPoint.x > transform.position.x) {
      movement.x = 1;
    } else {
      movement.x = -1;
    }
  
    if (currentPath[0].worldPoint.y > transform.position.y) {
      movement.y = 1;
    } else {
      movement.y = -1;
    }
}
  
void FixedUpdate() {
      rb.MovePosition(rb.position + movement * speed * Time.fixedDeltaTime);
}

However this is quite buggy. What would be the best way to make the enemy follow his path?

Don’t recalculate path every frame ( Update) as this will probably kill performance. InvokeRepeating() helps with that.

[SerializeField] float _acceleration = 1;
[SerializeField] float _speed = 1;
[SerializeField] Rigidbody2D _rigidbody;
List _path;
int _pathIndex;
float _pathfindingUpdateRate = 0.5f;// 0.5 means twice a second
float _stoppindDistance = 1f;

void Start ()
{
    InvokeRepeating( nameof(PathfindingUpdate) , Random.Range(0f,1f) , _pathfindingUpdateRate );
}

void PathfindingUpdate ()
{
    _path = Pathfinder.FindPath( transform.position , player.position );
    _pathIndex = 0;
}
  
void FixedUpdate()
{

    // get destination from path:
    Vector2 destination = _path[_pathIndex].worldPoint;

    // move toward destination:
    Vector2 destinationVector = destination - _rigidbody.position;
    float distanceLeft = destinationVector.magnitude;
    if( distanceLeft > _stoppindDistance )// above stopping distance
    {
        // accelerate toward destination:
        _rigidbody.velocity = Vector2.MoveTowards( _rigidbody.velocity , destinationVector.normalized * _speed , _acceleration*Time.fixedDeltaTime );
    }
    else// below stopping distance
    {
        // full stop:
        _rigidbody.velocity = Vector2.MoveTowards( _rigidbody.velocity , Vector2.zero , _acceleration*Time.fixedDeltaTime );

        // switch to next path destination (node)
        if( _pathIndex<(_path.Length-1) )
        {
            _pathIndex++;
        }
    }
}

In addition to the answer that andrew provided , i usually try to only recalculate path when necessary.

I dunno your exact scenario , but in a lot of games paths can get updated/changed while someone is following , so instead of periodically re-calculating the path , i usually keep track of all paths being currently walked , and as soon as a grid cell changes (path gets blocked , road get broken or whatever) i mark it as dirty.

Then i go over the dirty blocks , if there are none that means nothing changed and everything is safe , else i go over all the paths that include a “dirty” block and only update those.

That’s one way for you to avoid unnecessary path updates when nothing actually changes.

In terms of the info needed to follow the path , i usually keep a small struct/class like so for every pathfinding unit

public class PathingInfo
{
     // the points for the whole path
     public List<GridNode> pathGridNodes;
     // the last index that the unit traversed
     // ex : the unit starts at 0 , as soon as we go through the node at 1 , this becomes 1
     public int previousNodeIndex;
     
    // the current progress between (previousNodeIndex) and (previousNodeIndex+1)
    // ex : if this is 0.5f , and (previousNodeIndex == 0) 
    // that means we're in the middle between pathGridNodes[0] and pathGridNodes[1]
     public float percentageBetweenPrevAndNext;
}

Normally this is all you need (mostly) to retrace where you are in the path and advance , i normally keep the speed of the unit outside of this since i find it variable and unit-specific