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

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.

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_.nLength = Vector3.Distance(segments[i-1].pos, segments*.pos);*_

totalLength += segments*.nLength ;*
}
for(int i = 1; i < segments.Count; i++)
{
segments*.nLength /= totalLength;*
}

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.

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