Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Move from Point A to Point B along Path

Discussion in 'Scripting' started by saltysquid, Jul 17, 2019.

  1. saltysquid

    saltysquid

    Joined:
    May 1, 2017
    Posts:
    41
    This is frustrating me to no end. I have a player which needs to move along a path (not necessarily straight). The path is a List<Vector2> positions. I need the player to move to position 1, then 2, then 3, etc and stop at the last point. This sounds easy enough but I can't seem to get it to work. Here's what I've tried:

    Method A:

    Code (CSharp):
    1.         public IEnumerator MoveOverSeconds(GameObject objectToMove, Vector3 end, Vector3 next)
    2.         {
    3.             float elapsedTime = 0;
    4.             Vector3 startingPos = objectToMove.transform.position;
    5.             var seconds = Vector3.Distance(startingPos, next) * 0.3f;
    6.  
    7.             while (elapsedTime < seconds)
    8.             {
    9.                 objectToMove.transform.position = Vector3.Lerp(startingPos, end, (elapsedTime / seconds));
    10.                 objectToMove.transform.LookAt(next);
    11.  
    12.                 elapsedTime += Time.deltaTime;
    13.  
    14.                 yield return new WaitForEndOfFrame();
    15.             }
    16.  
    17.             objectToMove.transform.position = end;
    18.         }
    The array of positions passes in array[0] as "end" and array[1] as "next" IF there is an array[1]. After this is called as a StartCoroutine I call movementlist.RemoveAt(0). So, basically I'm trying to get to the first position then I remove it from the array and call this again with the array[0] now being the next position. Results are close with this method... except the player walks directly to the end position, skipping all the stops in between.

    Next I tried a different Coroutine:

    Code (CSharp):
    1.         private IEnumerator MoveAlongPath(GameObject objectToMove)
    2.         {
    3.             if (waypoints == null || !waypoints.Any())
    4.                 yield return null;
    5.  
    6.             float distanceThreshold = 0.05f;
    7.             Vector3 startingPos = objectToMove.transform.position;
    8.             if (waypoints.Count > 1)
    9.                 objectToMove.transform.LookAt(waypoints[1]);
    10.  
    11.             while (Vector3.Distance(objectToMove.transform.position, waypoints[0]) > distanceThreshold)
    12.             {
    13.                 //objectToMove.transform.position += objectToMove.transform.forward * 1f * Time.deltaTime;
    14.                 objectToMove.transform.position = Vector3.Lerp(startingPos, waypoints[0], 0.3f);
    15.                 yield return null;
    16.             }
    17.  
    18.             objectToMove.transform.position = waypoints[0];
    19.             waypoints.RemoveAt(0);
    20.             yield return new WaitForSeconds(2f);
    21.         }
    I can't even describe the results of this one... my player basically turns completely horizontal and starts moonwalking seemingly randomly around the screen.

    I'm sure I'm close, but if someone could point me in the right direction please let me know. Thanks!

    EDIT: I forgot to mention that this is in 3 dimensions. I just store the positions as Vector2 because the Y value is always 0f in this scenario.
     
  2. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Method A looks fine at a cursory glance.
    I suspect you aren't waiting for long enough before starting the next MoveOverSeconds with new parameters
    Cant you just wrap the whole method A body into another loop?
    eg.
    Code (CSharp):
    1.             public IEnumerator MoveOverSeconds(GameObject objectToMove, List<Vector3> list )
    2.             {
    3.                 while (list.count>2){
    4.                          //method A body, using list[0] and list[1]
    5.                          //removeAt(0)
    6.                  }
    7.             }
    8.  
     
  3. saltysquid

    saltysquid

    Joined:
    May 1, 2017
    Posts:
    41
    I was thinking the same thing. I just added:

    if (updateMoveLocked)
    return;

    to my update call. I set this to true immediately after this check and set it to false after one full move from within my coroutine. This causes the MoveOverSeconds to "wait" until the first is finished. This gets me about 99% there. A bit of smoothing and I think this issue is resolved. Thanks for the idea hpjohn, and hopefully this helps someone else :)
     
  4. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    You probably want to yield return null instead of WaitForEndOfFrame. WaitForEndOfFrame is designed to run in the same frame but after the GUI is rendered. Yielding null will wait until the normal execution time in the next frame.

    This has a chart showing when everything happens:
    https://docs.unity3d.com/Manual/ExecutionOrder.html