Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
12 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by TurboHermit · Aug 06, 2018 at 09:26 AM · animationvector3lerpmathwaypoint

Constant total time over multiple Lerp functions

Hey there, I could use some math help.

I want to move a transform over path using Lerp and an array of Vector3, like a waypoint system. I have access to the distances between the waypoints and the total distance of the path.

How would I implement the Lerp function per waypoint so that the total time for the transform to move from the beginning to the end of the path would a fixed number? For example: I have 12 waypoints with various distances and I want the total time to be 3 seconds.

Before suggesting using MoveTowards or anything else than Lerp, I'm using the Lerp values for some other data too, so I want to keep using it.

Thanks in advance,

Daan

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

4 Replies

· Add your reply
  • Sort: 
avatar image
0
Best Answer

Answer by Bunny83 · Aug 06, 2018 at 11:18 AM

You just want to "normalize" the time for the whole path. Since you know the distances of the individual path segments you can calculate a weigthed fraction depending on the whole path.


In relation to the whole path (0 - 1) each segment only has a length of "segment length" / total length. Of course if you add up all those numbers in order you will get "1". You can either store the absolute normalized starting time of each segment as well as the normalized length, or just store the normalized length and calculate the accumulated length as you're searching for the current segment.


Imagine something like this:

 public class Segment
 {
     public float nLength;
     public Vector3 pos;
 }

Here nLength is the normalized length as described above (so the segment length divided by the totel length). "pos" is just the target position. Usually when lerping along a path you could optimise the process be remembering the current used segment and the accumulated start time. If you want "random access" to the whole lerp range you would need to determine the right segment each time.

     public static Vector3 PathLerp(List<Segment> aSegments, float aTime)
     {
         if (aSegments == null || aSegments.Count == 0)
             throw new System.ArgumentException("segments not set","aSegments");
         if (aSegments.Count == 1 || aTime <= 0f)
             return aSegments[0].pos;

         float acc = 0f;
         float start = 0f;
         int index = 0;
         while(aTime > acc)
         {
             start = acc;
             index++;
             if (index >= aSegments.Count)
                 return aSegments[aSegments.Count - 1].pos;
             acc += aSegments[index].nLength;
         }
         var seg1 = aSegments[index - 1];
         var seg2 = aSegments[index];
         return Vector3.Lerp(seg1.pos, seg2.pos, (aTime - start) / seg2.nLength);
     }

Not that this is not tested. However the idea of the while loop is to determine the current target segment. Note that the first segment would just be the start position of your path and shouldn't have any "length". Since a path with n points only has "n-1" segments between those points. "aTime" is just the normalized lerp time for the whole path. "start" is the calculated starting time of the current segment. So we just subtract the starting time from the current time and divide by the normalized length of the current segment to get a value between 0 and 1. As long as you increase "aTime" at a constant speed you should move along the path with a constant speed.


As i said in most cases you just want to linearly move along the path. In this case you can get rid of the while loop as long as you remember the last index.

 List<Segment> segments;
 int current = 1;
 float t = 0f;
 
 void Update()
 {
     if (current > 0 && current < segments.Count)
     {
         t += speed * Time.deltaTime / totalLength / segments[current].nLength;
         if (t>1f)
         {
             t -= 1f;
             t *= segments[current].nLength;
             current++;
             if (current >= segments.Count)
                 return;
             t /= segments[current].nLength;
         }
         pos = Vector3.Lerp(segments[current-1].pos, segments[current].pos, t);
     }
 }

This, again, is untested but should work. To actually initialize the segments you have to calculate the normalized length.

 float totalLength = 0f;
 
 for(int i = 1; i < segments.Count; i++)
 {
     segments[i].nLength = Vector3.Distance(segments[i-1].pos, segments[i].pos);
     totalLength += segments[i].nLength ;
 }
 for(int i = 1; i < segments.Count; i++)
 {
     segments[i].nLength /= totalLength;
 }

Comment
Add comment · Show 7 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Aug 06, 2018 at 11:22 AM 0
Share

Note that "speed" is in world units per second. So if you want to go though the whole path in 3 sec you need a speed of

 speed = totalLength / 3f;

It's common to base such thing on the actual speed. However if you prefer just to define a time ins$$anonymous$$d of a speed you can do;

  t +=  Time.deltaTime / segments[current].nLength / timeDuration;
avatar image TurboHermit · Aug 06, 2018 at 07:22 PM 0
Share

I think this is what I was looking for. I currently deter$$anonymous$$e speed based on length per "segment" (I calculate distance between waypoints everytime it reaches the next one, but storing them should be more efficient since the waypoints don't move at all), and divide them by the total time, which I thought worked but apparently only in the setup I was using.

I'll try this out tomorrow and let you know how it works out, thanks =D

avatar image TurboHermit · Aug 07, 2018 at 07:55 AM 0
Share

I'm having a bit of trouble with understanding the math behind how you calculate t in your second example. Arguably t could just be reset to 0 after getting the next segment?

I don't see why you would subtract 1 and divide and multiply it afterward.

Also doesn't speed need to be calculated every time you target the next segment?

avatar image Bunny83 TurboHermit · Aug 08, 2018 at 01:36 PM 0
Share

Ok actually there was an error in my code ^^. I've fixed my answer ^^. We need to divide by nLength, not multiply.


Now just break down the parts of this code:

     t += speed * Time.deltaTime / totalLength / segments[current].nLength;
     if (t>1f)
     {
         t -= 1f;
         t *= segments[current].nLength;
         current++;
         t /= segments[current].nLength;
     }

"speed" is just your desired speed in world units per second. "speed / totalLength" gives us the overall speed in "percentage" per second ("programmer percentage" so a value between 0 and 1 ^^). This affects just the overall unifed lerp speed. Remember the "aTime" in "PathLerp" was a linear lerp value for the whole path.


So speed / totalLength is the required "t" increase per second. then we multiply by Time.deltaTime so actually get the required percentage change per frame. Finally we divide by our normalized "nLength" of the current segment which accounts for the different speeds of the different segments.


$$anonymous$$eep in $$anonymous$$d that t is a floating point value and the time per frame is not constant. So as we reach the end of a section we will most likely overshoot our final t value "1". Just for example "1.12". Though at 1 a new section starts. So we are already partially into the next section. To fix this we subtract "1" (for the completed segment) and "unscale" our time so it's no longer "fixed" for our last segment. Then we switch to the next segment and scale the time again with the new section specific factor. This should ensure to get a linear motion.


Imagine you have a very short segment followed by a long segment. nLength of the current will be way smaller than nLength of the next (long) segment. That means t has to increase faster for the short segment, but slower for the long segment. Since we divide by nLength we actually get a larger increase for the short segment. Since we overshoot 1 by 0.12 for example we first "normalize" the overshoot time by multiplying back in nLength. Say the length of the short segment is "0.1" so the normalized time would become "0.012". Say the next long segment has a nLength of 0.5 (so half of the entire path). We divide 0.012 by 0.5 and get "0.024" which is the correct point on the new segment. $$anonymous$$eep in $$anonymous$$d the new segment is longer so t has to increase slower

avatar image TurboHermit Bunny83 · Aug 08, 2018 at 07:57 PM 0
Share

This is quite hilarious. I actually had the code fixed before you replied (one of my roommates is a math $$anonymous$$cher), but encountered a different bug that I think your t calculations actually fix.

I reset t to 0 everytime I get to the next point and can only "get" the new point after a check made in update, basically hard limiting getting a single point per frame. Now what would happen was that the more points with tiny length segments I had, the slower the animation would go. Effectively getting approximate good results with long straight segments, but diverge more for every small curve.

With your t calculations, you allow for overshoot and could entirely skip, even multiple points if the deltaTime was larger then the time to take to reach the next point, if that makes any sense.

Thanks so much! I believe we can call this questions closed!

Show more comments
avatar image Bunny83 TurboHermit · Aug 08, 2018 at 02:15 PM 0
Share

I just had a bit of time and just tested both ways, PathLerp as well as the manual update solution and both work now as expected. I've measured the distance traveled independently from the movement by comparing the position from the last frame with the current and divide by deltaTime to get the current speed of the object. I set the speed to 4 and i get a perfect 4 all along the path:

avatar image
0

Answer by $$anonymous$$ · Aug 06, 2018 at 11:08 AM

You can add a timer variable and divide it by 3:

 float counter = 0;
 void Update ()
 {
      Vector3 start = new Vector3(0,0,2);
      Vector3 end = new Vector3(0,0,10);
      counter += Time.deltaTime;
      transform.position = Vector3.Lerp(start , end , counter / 3);
 }

You can replace the start and end variable with your waypoint positions.

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image TurboHermit · Aug 06, 2018 at 07:10 PM 0
Share

This would only work for a single Lerp, what I'm trying to achieve is a constant speed over multiple Lerps with various distances.

avatar image $$anonymous$$ TurboHermit · Aug 08, 2018 at 12:47 PM 0
Share

Yeah i know that, as i said in the answer:

You can replace the start and end variable with your waypoint positions.

You'll have to replace them by something else, for example if you have a List/Array of transforms, then make an "Index" variable of type (Int), and when waypoints[index].position == nextWaypoint.position then index++; and counter = 0;.

And the lerp will look like: Yeah i know that, as i said in the answer:
> You can replace the start and end variable with your waypoint positions. You'll have to replace them by something else, for example if you have a List/Array of transforms, then make an "Index" variable of type (Int), and when waypoints[index].position == nextWaypoint.position then index++; and counter = 0;.

And the lerp will look like: float counter = 0; void Update () { Vector3 start = new Vector3(0,0,2); Vector3 end = new Vector3(0,0,10); counter += Time.deltaTime; transform.position = Vector3.Lerp(waypoints[index].position , nextWaypoint.position , counter / 3); }

The code doesn't look like a code in the site which i don't know why, but.. you only have to replace start by waypoints[index].position, and end by nextWaypoint.position.
avatar image TurboHermit $$anonymous$$ · Aug 08, 2018 at 07:48 PM 0
Share

I don't think I've made myself clear. I know how to implement the code.

Your example however, would make every single individual Lerp last exactly 3 seconds. What I'm trying to achieve is make all the Lerps in total last a total amount of 3 seconds and make sure that the object moves at a constant speed (so NOT 3 seconds per Lerp) to do so.

avatar image
0

Answer by madks13 · Aug 06, 2018 at 11:22 AM

You will have to calculate the total distance of the path. Then you have your speed units/second. Then multiply by deltaTime. You got your distance to move by. Then move along the path.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by DawidNorasDev · Aug 07, 2018 at 09:39 AM

Easy (but not exactly what you asked) solution is to use SmoothDamp instead. or even better Vector3.MoveTowards.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

254 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Get the rotation matrix if you have the start and end position 1 Answer

Making a Lerp + Collisions waypoint system? 2 Answers

Vector3.MoveTowards and Quaternion.RotateTowards 1 Answer

Vector3.lerp doesn't work in local position 1 Answer

Vector3.Lerp does not linearly interpolate, any ideas? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges