Search Unity

LERP multi points: A to B to C

Discussion in 'Scripting' started by etchthaboss, Jan 30, 2017.

  1. etchthaboss

    etchthaboss

    Joined:
    Jun 13, 2016
    Posts:
    12
    I'm trying to create two lerps that go sequentially. I've done a lot of searching and there weren't any good answers. I tried using things like dottween and iTween and have ended up with nothing.
    I'm trying to move 2000 objects around. Essentially a timeline of data points from period 1 to period 2 to period 3. Using iTween destroyed my performance.

    My initial method was create the first coroutine to go A to B. Then at the end of that coroutine, call my second one. The first one gets called in update. But i did debug statements and seems that they don't get called sequentially.

    Any help would be appreciated.
     
  2. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Code (csharp):
    1.  
    2.     float Lerp3(float a, float b, float c, float t)
    3.     {
    4.         if (t <= 0.5f)
    5.         {
    6.             return Mathf.Lerp(a, b, t * 2f);
    7.         }
    8.         else
    9.         {
    10.             return Mathf.Lerp(b * 2f, c, t);
    11.         }
    12.     }
    13.  
    Something like this? Untested.
     
    Last edited: Nov 14, 2020
    Bunny83 and RodrigoSeVeN like this.
  3. etchthaboss

    etchthaboss

    Joined:
    Jun 13, 2016
    Posts:
    12
    Yes, something like that, but for a vector3 lerp, i was doing this:

    Code (CSharp):
    1.         float distCovered = (Time.time - start_time) * lerp_speed;
    2.         float fracJourney = distCovered / journey_length_1;
    3.  
    4.         transform.position = Vector3.Lerp (start_position, middle_position, fracJourney);
    then i had the same thing but with middle_position and end_position and journey_length_2. The journey lengths are simply the vector 3 lengths.

    In your example there is the same t values. What would i use for that?
     
  4. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    t is a value between 0 and 1, which is common for any lerping library. 0 is A, 0.5 is B and 1 is C. Plug it in changing Mathf.Lerp to Vector3.Lerp and of course float ABC becomes Vector3 ABC.
     
  5. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    Just a note, iTween is not the best choice. I would use Leantween or Dotween. Personally I use LeanTween for many things and it works great. There are different calls you can do. There are ways to create paths or at the end of a LeantTween call, you can do a OnComplete that has it do the next step when the tween is finished. Works super well.

    Code (CSharp):
    1.         LeanTween.move(targetObject, someVector, .5f).setOnComplete(() =>
    2.         {
    3.            //Tween is complete, do something next.
    4.         });
    5.  
    This is just a basic example of moving something over .5 secs and then when it's done, it will do something else for example.
     
  6. etchthaboss

    etchthaboss

    Joined:
    Jun 13, 2016
    Posts:
    12
    @Brathnann thanks, i will check it out.

    @hippocoder,
    So if i need to go from point A to B, i can't just use a discrete value. i need to keep incrementing it. So my first thought is, call the Lerp3 function in Update, and then increment t by .1. Is this the correct approach, so that it smoothly goes from a to b to c?
     
  7. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    t += Time.deltaTime;
    if (t>=0.5f) Debug.Log("Reached B");

    Not sure what's hard to understand, it behaves exactly like Unity's Mathf.Lerp or Vector3.Lerp, except with 3 values instead of 2?
     
  8. etchthaboss

    etchthaboss

    Joined:
    Jun 13, 2016
    Posts:
    12
    Okay, so i created my lerp3 function and called it in update. I have 2 problems.

    One, it runs very fast. I want it to be slower and have control over the speed. Ideally it should run about 5 seconds.

    The second problem is that while the animation is running it runs at about 2 fps. Probably because there's 2000 objects and this function is being called on update in all of them. So how can i speed this up.
     
  9. image28

    image28

    Joined:
    Jul 17, 2013
    Posts:
    457
    Coroutine's would be faster than update. But without looking at the code/profiler it would be hard to tell where to optimise
     
  10. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Just slow down your time parameter. A typical way to do this is to multiply time by a speed float.

    Don't call Update on 2000 objects. Use a manager class instead, with an array of 2000 objects. And loop through the objects in an array.

    If that doesn't work, consider switching the whole thing to a particle effect.
     
    hippocoder and Brathnann like this.
  11. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    To add to what @BoredMormon says about a manager. This great article that Unity put out https://blogs.unity3d.com/2015/12/23/1k-update-calls/ which describes calling updates vs using a manager.
     
    Kiwasi likes this.
  12. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    A few other general performance tips.
    • Cache the Transforms of each object for looping (the transform getter has some safe-checking that only really needs to be done once during the lerp animation)
    • Set localposition instead of position. (setting position causes unity to internally figure out the local position via traversing the ancestry of that object's heirarchy)
    • multiply all your floats together before multiplying a vector with the float. A Vector*(float*float*float) gives the same result as Vector*float*float*float. But the first is 6 multiplications while the latter is 9 multiplications.
    • keep the hierarchy of those 2000 objects as simple as possible. if each object has one child then the loop is actually affecting 4000 transforms. if each object has 4 children thats 10000 transforms to update.
    • affecting the rotation will also multiply the number of calculations changing the local position and rotation of each object that has 4 children is actually 20000 updates
    • Unity 5.5 has plans to greatly improve the performance of updating the transforms by using a dirty transform concept.
    • and as BoredMormon said, use a manager. the less work that you don't have unity do for you automatically, the faster your code is going to run. One Update message is far faster than 2000 Update messages

    However, the most important thing to do off to bat to figure out this problem is.

    Use the Profiler!


    It'll tell you exactly whats killing your fps.
     
    PBergsten and Kiwasi like this.
  13. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    Here is a rethink of what you are doing....

    Create methods that are reusable later on...

    (vs didnt say there were any errors, but I didnt run it.)
    Code (csharp):
    1.  
    2. public Vector3 LerpOverDistance(Vector3[] vectors, float time){
    3.     time = Mathf.Clamp01(time);
    4.     if(vectors == null || vectors.Length == 0){
    5.         throw(new Exception("Vectors input must have at least one value"));
    6.     }
    7.     if(vectors.Length == 1){
    8.         return vectors[0];
    9.     }
    10.    
    11.     if(time == 0){
    12.         return vectors[0];
    13.     }
    14.    
    15.     if(time == 1){
    16.         return vectors[vectors.Length - 1];
    17.     }
    18.    
    19.     float[] distances = new float[vectors.Length - 1];
    20.     float total = 0;
    21.     for(int i=0; i<vectors.Length; i++){
    22.         distances[i] = (vectors[i] - vectors[i + 1]).sqrDistance;
    23.         total += distances[i];
    24.     }
    25.    
    26.     float current = total * time;
    27.     int p = 0;
    28.     while(current - distances[p] > 0){
    29.         current -= distances[p++];
    30.     }
    31.     if(distances[p] == 0) return vectors[p];
    32.    
    33.     return Vector3.Lerp(vectors[p], vectors[p + 1], current / distances[p]);
    34. }
    35.  
    36. public Vector3 LerpOverNumber(Vector3[] vectors, float time){
    37.     time = Mathf.Clamp01(time);
    38.     if(vectors == null || vectors.Length == 0){
    39.         throw(new Exception("Vectors input must have at least one value"));
    40.     }
    41.     if(vectors.Length == 1){
    42.         return vectors[0];
    43.     }
    44.    
    45.     if(time == 0){
    46.         return vectors[0];
    47.     }
    48.    
    49.     if(time == 1){
    50.         return vectors[vectors.Length - 1];
    51.     }
    52.    
    53.     float t = time * vectors.Length;
    54.     int p = (int)Mathf.floor(t);
    55.     t -= p;
    56.     return Vector3.Lerp(vectors[p], vectors[p + 1], t);
    57. }
    58.  
     
    aaronauty, ekh4rt and hippocoder like this.
  14. lagallardo5426

    lagallardo5426

    Joined:
    May 28, 2020
    Posts:
    1
    this worked for me but in the else statement you need to multiply "b" by 2. "return Mathf.Lerp(b*2, c, t)"
     
    D3nnisV and hippocoder like this.
  15. The_Button_King

    The_Button_King

    Joined:
    Apr 11, 2021
    Posts:
    1
    I hate to come and be the "Well, actually," guy, but multiplying b by 2 is also wrong. You need to multiply t by the number of points - 1, and then subtract by the whatever number the leftmost point in the lerp is (so if you are lerping between points a & b, a is the 1st point and b is the 2nd, so you subtract 1).

    A bad formula for this is Lerp(point1, point2, t * (numberOfPoints - 1) - leftPointNumber

    So for 3 points and the 2nd lerp, it's Mathf.Lerp(b, c, (t * 2f) - 1f);

    Here's the code for this too. Hopefully, this saves someone in the future from 3 hours of figuring it out.

    Code (CSharp):
    1.  
    2.     float Lerp3(float a, float b, float c, float t)
    3.     {
    4.         if (t <= 0.5f)
    5.         {
    6.             return Mathf.Lerp(a, b, t * 2f);
    7.         }
    8.         else
    9.         {
    10.             return Mathf.Lerp(b, c, (t * 2f) - 1f);
    11.         }
    12.     }
    13.  
     
    Bunny83 likes this.
  16. Renardjojo

    Renardjojo

    Joined:
    May 15, 2019
    Posts:
    13
    Hey ! Here is a version less visible but without condition (indispensable for shader) :
    Code (CSharp):
    1.  
    2. // t between -1 and 1
    3. float Lerp3(float a, float b, float c, float t)
    4. {
    5.     return lerp(lerp(a, b, min(t, 0.0f) + 1.0f), c, max(t, 0.0f));
    6. }
    7.  
    With t include in [-1, 1]. To convert t from [0, 1] to [-1, 1] apply:
    Code (CSharp):
    1.  
    2. // t between 0 and 1
    3. float Lerp3(float a, float b, float c, float t)
    4. {
    5.     t = t * 2.0f - 1.0f;
    6.     return lerp(lerp(a, b, min(t, 0.0f) + 1.0f), c, max(t, 0.0f));
    7. }
    8.  
    I would also like to state a limitation of all the proposed formulas. If we consider the area under the curve that occupies a, b and c. So, a will occupy 25%, b 50% and c 25%. If anyone has a mathematical solution to this problem, I would be curious to know it.
     
    Last edited: May 16, 2022
    benthroop and The_Button_King like this.
  17. benthroop

    benthroop

    Joined:
    Jan 5, 2007
    Posts:
    262
    -1 to 1 is super handy for dot product compares ^