Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Vector3.Lerp not working in Coroutine

Discussion in 'Scripting' started by Unlimited_Energy, Mar 15, 2016.

  1. Unlimited_Energy

    Unlimited_Energy

    Joined:
    Jul 10, 2014
    Posts:
    469
    I am sure it is something simple but i am new to coding. the object is not moving to the end position in the coroutine. "transform.position = Vector3.Lerp(start.position, end.position, Time.time);" works in Update function but not in the coroutine where I need it to since the clouds are only suppose to move when R is true.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class cloudMove : MonoBehaviour {
    5.  
    6.     public bool r;
    7.  
    8.  
    9.     public Transform start;
    10.     public Transform end;
    11.  
    12.  
    13.  
    14.     public void CloudMove ()
    15.     {
    16.         if (r)
    17.         {
    18.             StartCoroutine (GreyCloudMove ());
    19.         }
    20.  
    21.  
    22.     }
    23.  
    24.     IEnumerator GreyCloudMove()
    25.     {
    26.        
    27.         transform.position = Vector3.Lerp(start.position, end.position, Time.time);
    28.         yield return new WaitForSeconds(18);
    29.     }
    30. }
    31.  
     
  2. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    The way Lerp works is that the third value you pass in (in your case Time.time) should be a value between 0 and 1. This is a percentage that tells how far between the start and ending position to go between. If it's 0, then it's the start position that gets returned. If it's 1, the end position gets returned. And if it's 0.5, it will be halfway between.

    If you want to use Lerp to continuously move something along (rather than simply move to a single position and then stop), you can pass in a value such as Time.deltaTime * some_speed (assuming start position is the current position of the object).

    If you do want to move it between two positions that you've defined in advance, then create a value set to 0, t, and increment it a certain amount each frame. If you increment it by Time.deltaTime, then it will take 1 second to move between start and end. Multiply or divide this value to change how long it takes.

    If you pass in Time.time, then this Lerp will work for 1 second (assuming time scale is 1) and then will always return the end position immediately thereafter, because Time.time will be above 1 each time it's used after that.
     
    MikeTeavee likes this.
  3. Unlimited_Energy

    Unlimited_Energy

    Joined:
    Jul 10, 2014
    Posts:
    469
    not sure I am following you on Time.time) should be a value between 0 and 1. what would you change in my code? the cloud are not moving at all.

     
    Last edited: Mar 15, 2016
  4. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    897
    Time.time is the time in seconds the game has been playing. lerp just hides this and just clamps any value higher than 1 second to just one. which if you were looping properly would only have it animate over one second not 18 seconds. that tied in with the slight lag in when a scene loads and you may never see it animate at all. further more since Time.time is from startup not from when the function is called it will usually mean that the clouds would start at the end postion if called during runtime. its better not to rely on Time.time for this. have the coroutine track and count its own time via adding from Time.deltaTime..

    Its important to understand that single Lerp function call only returns a single "frame" of the animation, the function by itself doesn't animate anything. you need to call lerp each frame, in a loop that runs on each frame. Waitforseconds just puts the coroutine to sleep (in a loose use of the term) for that many seconds, the coroutine itself doesn't run in the background during this. below I'm also using inverseLerp to convert the time into a number that lerp will use properly over the duration of 18 seconds

    Code (CSharp):
    1.     public float duration= 18;
    2.  
    3. IEnumerator GreyCloudMove()
    4.     {
    5.         float current = 0;
    6.         float lerpValue = 0;
    7.  
    8.         while(current< duration)
    9.         {
    10.             //convert the current time to a lerp value that rises from 0.0 to 1.0 as current rises from 0.0 to 18.0
    11.             lerpValue = Mathf.InverseLerp(0, duration, current);
    12.  
    13.             transform.position = Vector3.Lerp(start.position, end.position, lerpValue);
    14.            
    15.             current += Time.deltaTime;
    16.  
    17.             //. continue this loop on the next frame
    18.             yield return null;
    19.         }
    20.     }
     
    Unlimited_Energy and JasonBricco like this.
  5. Unlimited_Energy

    Unlimited_Energy

    Joined:
    Jul 10, 2014
    Posts:
    469
    Thank you for the code but i must be doing something wrong as it still is not working...

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class cloudMove : MonoBehaviour {
    5.  
    6.     public bool r;
    7.  
    8.  
    9.     public Transform start;
    10.     public Transform end;
    11.  
    12.     public float duration = 18;
    13.  
    14.     public    void apple()
    15.     {
    16.  
    17.  
    18.         if (r)
    19.             {
    20.             StartCoroutine (GreyCloudMove());
    21.             }
    22.  
    23.  
    24.         }
    25.  
    26.  
    27.     IEnumerator GreyCloudMove()
    28.     {
    29.         float current = 0;
    30.         float lerpValue = 0;
    31.  
    32.             while(current< duration)
    33.             {
    34.                 lerpValue = Mathf.InverseLerp(0, duration, current);
    35.  
    36.                     transform.position = Vector3.Lerp(start.position, end.position, lerpValue);
    37.  
    38.                 current += Time.deltaTime;
    39.  
    40.                 yield return null;
    41.             }
    42. }
     
  6. MikeTeavee

    MikeTeavee

    Joined:
    May 22, 2015
    Posts:
    194
    This code worked fine for me. Although "apple" needed to be called manually from an outside script. If you want the object to automatically lerp, then change "public void apple()" to "void Start()".

    Like so:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class cloudMove : MonoBehaviour {
    5.     public bool r;
    6.     public Transform start;
    7.     public Transform end;
    8.     public float duration = 18;
    9.  
    10.     void Start(){
    11.         if (r){
    12.             StartCoroutine (GreyCloudMove());
    13.         }
    14.     }
    15.     IEnumerator GreyCloudMove() {
    16.         float current = 0;
    17.         float lerpValue = 0;
    18.         while(current< duration)
    19.         {
    20.             lerpValue = Mathf.InverseLerp(0, duration, current);
    21.             transform.position = Vector3.Lerp(start.position, end.position, lerpValue);
    22.             current += Time.deltaTime;
    23.             yield return null;
    24.         }
    25.     }
    26. }
     
    Unlimited_Energy likes this.
  7. Unlimited_Energy

    Unlimited_Energy

    Joined:
    Jul 10, 2014
    Posts:
    469
    beautiful. Works very well now. Question is how do I get the clouds to return to there starting state with lerp so when R is not active the cloud lerps back to its original transform starting position.
     
  8. MikeTeavee

    MikeTeavee

    Joined:
    May 22, 2015
    Posts:
    194
    I'm not sure what type of functionality you need exactly, so please let me know if this code is of any use. I hastily scrapped the coroutine and threw everything in the Update() and changed a couple things. The cloud now moves towards the End-destination when "r" is checked, and will start heading back to the Start-destination when "r" is unchecked.

    Note: Unlike the previous code, the cloud can still move back to the Start-destination even after it has reached the End-destination ...remember the While (current < destination)? .... hope this change is to your liking, if not, let me know!


    Here's the code (it's good, not great):
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class cloudMove : MonoBehaviour {
    5.     public bool r;
    6.     public Transform start;
    7.     public Transform end;
    8.     public float duration = 18;
    9.     public float current = 0;
    10.     public float lerpValue = 0;
    11.     public int toggle;
    12.  
    13.     void Update(){
    14.         current = Mathf.Clamp(current, 0, duration);
    15.  
    16.         if (r)
    17.             toggle = 1;
    18.         else
    19.             toggle = -1;
    20.  
    21.         lerpValue = Mathf.InverseLerp(0, duration, current);
    22.         transform.position = Vector3.Lerp(start.position, end.position, lerpValue);
    23.         current += Time.deltaTime * toggle;
    24.     }
    25. }
     
    Last edited: Mar 16, 2016
  9. Unlimited_Energy

    Unlimited_Energy

    Joined:
    Jul 10, 2014
    Posts:
    469
    hmm did not work. Clouds did not move with that script i copy and pasted it.
    so I tried the code below also and calling the GreyCloudMoveBack function on the other script when r is false, but that did not move the cloud back at all either. I also made sure to use the end transform object as the starts transform position and then ends transform is another target object the cloud moves back to which is the cloud beginning position.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class cloudMove : MonoBehaviour {
    5.  
    6.     public bool r;
    7.  
    8.  
    9.     public Transform start;
    10.     public Transform end;
    11.     public Transform starts;
    12.     public Transform ends;
    13.     public float duration = 18;
    14.     public float durations = 18;
    15.  
    16.     public void MoveBack()
    17.     {
    18.             StartCoroutine (GreyCloudMoveBack ());
    19.     }
    20.  
    21.     public    void Move()
    22.     {
    23.             StartCoroutine (GreyCloudMove ());
    24.     }
    25.  
    26.  
    27.     IEnumerator GreyCloudMove()
    28.     {
    29.         float current = 0;
    30.         float lerpValue = 0;
    31.  
    32.             while(current< duration)
    33.             {
    34.                 lerpValue = Mathf.InverseLerp(0, duration, current);
    35.  
    36.                     transform.position = Vector3.Lerp(start.position, end.position, lerpValue);
    37.  
    38.                 current += Time.deltaTime;
    39.  
    40.                 yield return null;
    41.             }
    42. }
    43. IEnumerator GreyCloudMoveBack()
    44.  
    45.     {
    46.         float current = 0;
    47.         float lerpValue = 0;
    48.  
    49.          while(current< durations)
    50.         {
    51.             lerpValue = Mathf.InverseLerp(0, durations, current);
    52.  
    53.             transform.position = Vector3.Lerp(starts.position, ends.position, lerpValue);
    54.  
    55.             current += Time.deltaTime;
    56.  
    57.             yield return null;
    58.         }
    59.     }
    60. }
    61.  
     
  10. MikeTeavee

    MikeTeavee

    Joined:
    May 22, 2015
    Posts:
    194
    Hmm, that's strange that my code didn't work... and nothing happens when you check-off "r" in the inspector? ("r" is false by default"). Also, did you make sure to drag and drop the gameobjects into the public Transforms for Start/End?

    By the way the code you posted is doing something very unorthodox. Each time you call a coroutine it is added on the stack, since previous calls run forever (at least until current >= destination). In other words, calling GreyCloudMove or GreyCloudMoveBack 50 times, will have 50 instances running at once!

    I would definitely work on code that only sits only inside Update(). Maybe you can get the code that I gave you another chance, or start something from scratch.

    I simplied the code a little:
    (Note: "r" is true by default now)

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class cloudMove : MonoBehaviour {
    5.     public bool r = true;
    6.     public Transform start;
    7.     public Transform end;
    8.     public float duration = 18;
    9.     public float current = 0;
    10.     public int toggle;
    11.  
    12.     void Update(){
    13.         current = Mathf.Clamp(current, 0, duration);
    14.         if (r)
    15.             toggle = 1;
    16.         else
    17.             toggle = -1;
    18.         transform.position = Vector3.Lerp(start.position, end.position, (current += Time.deltaTime * toggle)/(duration));
    19.     }
    20. }
     
    Last edited: Mar 17, 2016
  11. Unlimited_Energy

    Unlimited_Energy

    Joined:
    Jul 10, 2014
    Posts:
    469
    no nothing happens when r is enabled
     
  12. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    @MikeTeavee's script works, but only in one direction because the boolean is never toggled, at least not in that script.

    There are several ways to achieve that effect though, you could also swap start and end once the duration has been surpassed.
     
    Unlimited_Energy likes this.
  13. MikeTeavee

    MikeTeavee

    Joined:
    May 22, 2015
    Posts:
    194
    I meant to make it so you can toggle direction from the inspector. I guess one way to change it's direction is for an outside script to use GetComponent and change "r" to true/false.

    Also, I'm hoping my scene is not two different. In my scene, this script is sitting in a cube, and a spere to the left is set as the Start Trasform via drag-and-drop, and a sphere on the right is set to End. Like so...
     
    Last edited: Mar 17, 2016
    Unlimited_Energy likes this.
  14. Unlimited_Energy

    Unlimited_Energy

    Joined:
    Jul 10, 2014
    Posts:
    469
    your right it does work in update. I cannot get it to work in a coroutine that is called when R is enabled true by my other script. see, when it is raining with the particle system, the clouds are moving into the scene triggered by R Boolean to start the coroutine on the CloudMove script, when it is done raining and x is no longer true which shuts off the rain on my other script then it truns R to false on the CloudMove script. I never seem to be able to reverse the clouds direction in a coroutine. i even tried setting up a second coroutine named CloudMoveBack where I used the same code you gave me but I changed the start and end transforms to the reverse positions because after the first co routine the clouds end at target 2 and when the second coroutine kicks in the clouds start at target 2 and move back to target 1, but I never seem to be able to get the clouds to move back....
     
  15. MikeTeavee

    MikeTeavee

    Joined:
    May 22, 2015
    Posts:
    194
    If it doesn't work in a coroutine, then why do you want to use a coroutine?
     
    JoeStrout likes this.
  16. Unlimited_Energy

    Unlimited_Energy

    Joined:
    Jul 10, 2014
    Posts:
    469
    I was thinking using update would be performance costly on that but maybe I am wrong. that is why i was trying to modify your code into a coroutine but I guess I am not experienced enough yet with programming to understand how to use corutines like that and maybe overthinking one function in update. That will probably be the only thing in update that Ill have is your code. I tried to find out the performance hit of using update but could not find anything really. Ill just stick to using your code in update. Thanks for all the responses mike.
     
  17. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I would strongly advise those new to coding not use coroutines. Period. They're full of issues and do not save performance unless a) you know exactly what you're doing and b) have a firm grasp of programming.

    I would suggest you wait until your game on profiler is actually costing you any performance. Co routines actually cost you performance in gc, so you should be experienced before using them. They're not threaded or anything, merely that you can get them to run your code less - something you can easily do in Update with a timer.
     
  18. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    It's not gonna cost you much performance. That is literally nothing.

    The coroutine runs in the main thread as well, just like the Update method. The only difference is that the coroutine stops running once it has completed, while the Update method runs as long as the behaviour is alive and activated.

    In most cases it's a matter of design. I personally take coroutines in such situations, but the code runs just as fine in Update. If you've got a better understanding of how things have to be done in Update, then just keep doing it there as long as you haven't got much into coroutines.
     
    Unlimited_Energy and hippocoder like this.
  19. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Excellent reply: it's a matter of design. Best explanation I've read in a while.
     
    Unlimited_Energy likes this.