Search code examples
c#unity-game-enginemove

Moving towards WayPoints [C#]


I've got this problem: I need that enemies move towards a target position, but my problem is that i wrote this inside the Update function, so the "foreach" statement is done in just one frame. Also, the "waypoint is reached" sentence is never shown. How can I solve this?

Here is the code: `public class WayPointMovement : MonoBehaviour {

public EnemyDataSpawn enemy;
private Vector3 lastPos; 

void Start ()
{
    lastPos = gameObject.transform.position; 
}

void Update ()
{
    gameObject.transform.LookAt (LevelManager.current.playerObject.transform);
    //WaypointData is a scriptable object that contains the definition of "x" and "z" coordinates.
    foreach (WaypointData waypoints in enemy.enemy.waypoint) {
        Debug.Log (waypoints.x);
        gameObject.transform.position = Vector3.MoveTowards (gameObject.transform.position, new Vector3 (lastPos.x + waypoints.x, 0f, lastPos.z + waypoints.z), Time.deltaTime * enemy.enemy.easySpeedMovement);
        if (gameObject.transform.position.x == lastPos.x + waypoints.x && gameObject.transform.position.z == lastPos.z + waypoints.z) {
            Debug.Log("waypoint reached");
            lastPos = gameObject.transform.position;
        }
    }
}

}`


Solution

  • You need to rework Update so that it only does one frame's worth of work. That means that you have to remember a) which waypoint you're going toward, and b) how far you were through going to it. You only move on to the next waypoint when you actually get to it.

    There are two ways to track the progress through waypoints:

    1. You can simply delete waypoints once they are reached. Then, the waypoint you're trying to get to is always the first one in the list.

    2. If you want to keep the waypoints, then you need an extra variable to track which waypoint you're currently moving toward. This could be something like an index into an array or list, or it could be an enumerator (IEnumerator<WaypointData>) on which you call MoveNext each time you finish a waypoint.

    You've indicated (in comments on this answer) that you don't want to delete waypoints as you reach them. The most flexible arrangement, allowing you to add new waypoints as you go, is to use an index variable:

    int waypointIndex;
    
    void Start()
    {
      waypointIndex = 0;
    }
    
    void Update()
    {
      if (waypointIndex < waypoints.Count)
      {
        update position;
    
        if (reached waypoint)
        {
          waypointIndex++;
        }
      }
    }
    

    Then, in your Update method, instead of writing a loop, you update your game object's position exactly once, check if it has reached the waypoint just once, and if it has, move on to the next waypoint before returning. If multiple moves are needed to get to a waypoint, you allow those to be multiple calls to Update; each Update is only one step of the movement.

    You will need better logic for determining when you have reached a waypoint. As other commenters have indicated, with floating-point values, simply checking if they are exactly equal is not likely to work. Instead, you can check whether the distance to the waypoint is less than Time.deltaTime * enemy.enemy.easySpeedMovement, and if it is, then instead of using Vector3.MoveTowards, just set the position to the waypoint exactly, and treat that step as having reached the waypoint.

    Here's the rough logic (in pseudocode) (but not as pseudocode as when I first wrote it):

    Vector3 lastPos;
    int waypointIndex;
    
    void Start()
    {
      lastPos = gameObject.transform.position;
      waypointIndex = 0;
    }
    
    void Update()
    {
      if (waypointIndex < waypoints.Count)
      {
        waypointPosition = new Vector3 (lastPos.x + waypoints.x, 0f, lastPos.z + waypoints.z);
    
        if (Vector3.Distance(gameObject.transform.position, waypointPosition) > length of one step)
        {
          gameObject.transform.position = Vector3.MoveToward(gameObject.transform.position, waypointPosition);
        }
        else
        {
          gameObject.transform.position = waypointPosition;
          log("Waypoint reached");
          waypointIndex++;
        }
      }
    }