Skip to main content
Distinguishing per-tick vs per-second values
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401
progress = Mathf.Clamp01(progress + speedspeedPerTick);
current = Mathf.Lerp(start, end, progress);
current = Mathf.Lerp(current, target, sharpnesssharpnessPerTick);

This parameter isn't quite a "speed" anymore, because we approach the target in a Zeno-like fashion. If sharpnesssharpnessPerTick waswere 0.5, then on the first update wewe'd move halfway to our goal. Then on the next update wewe'd move half the remaining distance (so a quarter of our initial distance). Then on the next wewe'd move half again...

This gives an "exponential ease-out" where the movement is fast when far from the target and gradually slows down as it approaches asymptotically (though with infinite-precision numbers it will never reach it in any finite number of updates - for our purposes it gets close enough). It's great for chasing a moving target value, or smoothing a noisy input using an "exponential moving average," usually using a very small sharpnesssharpnessPerTick parameter like 0.1 or smaller.

progress = Mathf.Clamp01(progress + speedspeedPerSecond * Time.deltaTime);
// or progress = Mathf.Clamp01(progress + Time.deltaTime / durationSeconds);
current = Mathf.Lerp(start, end, progress);
progress = Mathf.Clamp01(progress + speed);
current = Mathf.Lerp(start, end, progress);
current = Mathf.Lerp(current, target, sharpness);

This parameter isn't quite a "speed" anymore, because we approach the target in a Zeno-like fashion. If sharpness was 0.5, then on the first update we move halfway to our goal. Then on the next update we move half the remaining distance (so a quarter of our initial distance). Then on the next we move half again...

This gives an "exponential ease-out" where the movement is fast when far from the target and gradually slows down as it approaches asymptotically (though with infinite-precision numbers it will never reach it in any finite number of updates - for our purposes it gets close enough). It's great for chasing a moving target value, or smoothing a noisy input using an "exponential moving average," usually using a very small sharpness parameter like 0.1 or smaller.

progress = Mathf.Clamp01(progress + speed * Time.deltaTime);
// or progress = Mathf.Clamp01(progress + Time.deltaTime / durationSeconds);
current = Mathf.Lerp(start, end, progress);
progress = Mathf.Clamp01(progress + speedPerTick);
current = Mathf.Lerp(start, end, progress);
current = Mathf.Lerp(current, target, sharpnessPerTick);

This parameter isn't quite a "speed" anymore, because we approach the target in a Zeno-like fashion. If sharpnessPerTick were 0.5, then on the first update we'd move halfway to our goal. Then on the next update we'd move half the remaining distance (so a quarter of our initial distance). Then on the next we'd move half again...

This gives an "exponential ease-out" where the movement is fast when far from the target and gradually slows down as it approaches asymptotically (though with infinite-precision numbers it will never reach it in any finite number of updates - for our purposes it gets close enough). It's great for chasing a moving target value, or smoothing a noisy input using an "exponential moving average," usually using a very small sharpnessPerTick parameter like 0.1 or smaller.

progress = Mathf.Clamp01(progress + speedPerSecond * Time.deltaTime);
// or progress = Mathf.Clamp01(progress + Time.deltaTime / durationSeconds);
current = Mathf.Lerp(start, end, progress);
Adding duration form
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401
progress = Mathf.Clamp01(progress + speed, 0, 1);
current = Mathf.Lerp(start, end, progress);
progress = Mathf.Clamp01(progress + speed * Time.deltaTime);
// or progress = Mathf.Clamp01(progress + Time.deltaTime / durationSeconds);
current = Mathf.Lerp(start, end, progress);
progress = Mathf.Clamp01(progress + speed, 0, 1);
current = Mathf.Lerp(start, end, progress);
progress = Mathf.Clamp01(progress + speed * Time.deltaTime);
current = Mathf.Lerp(start, end, progress);
progress = Mathf.Clamp01(progress + speed);
current = Mathf.Lerp(start, end, progress);
progress = Mathf.Clamp01(progress + speed * Time.deltaTime);
// or progress = Mathf.Clamp01(progress + Time.deltaTime / durationSeconds);
current = Mathf.Lerp(start, end, progress);
Clarity
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401

Linear1. Linear blending between a start and an end

Exponential2. Exponential ease toward a target

Note here that in this version the current value appears as both the output and an input. It displaces the start variable, so we're always starting from wherever we moved to on the last update. This is what gives this version of Lerp a memory from one frame to the next. From this moving starting point, we then then move a fraction of the distance toward the target dictated by a sharpness parameter.

This parameter isn't quite a "speed" anymore, because we approach the target in a Zeno-like fashion. If sharpnesssharpness was 0.5, then on the first update we move halfway to our goal. Then on the next update we move half the remaining distance (so a quarter of our initial distance). Then on the next we move half again...

This gives an "exponential ease-in"out" where the movement is fast when far from the target and gradually slows down as it approaches asymptotically (though with infinite-precision numbers it will never reach it in any finite number of updates - for our purposes it gets close enough). It's great for chasing a moving target value, or smoothing a noisy input using an "exponential moving average," usually using a very small sharpness parameter like 0.1 or smaller.

 

But you're right, there is an error in the upvoted answer you link. It's not correcting for deltaTime the right way. This is a very common mistake when using this style of LerpLerp.

But our exponential easing is non-linear, so just multiplying our sharpness parameter by deltaTime will not give the correct time correction. This will show up as a judder in the movement if our framerate changesfluctuates, or a change in the easeeasing sharpness if you go from 30 to 60 consistently.

Instead we need to apply an exponential correction for our exponential ease:

 

There's one other arguable error in that code, which is using Slerp - spherical linear interpolation is useful when we want an exactly consistent rate of rotation through the whole movement. But if we're going to be using a non-linear exponential ease anyway, Lerp will give an almost undistinguishable result and it's cheaper. ;) Quaternions lerp much better than matrices do, so this is usually a safe substitution.

Linear blending between a start and an end

Exponential ease toward a target

Note here that the current value appears as both the output and an input. It displaces the start variable, so we're always starting from wherever we moved to on the last update. This is what gives this version of Lerp a memory from one frame to the next. From this moving starting point, we then then move a fraction of the distance toward the target dictated by a sharpness parameter.

This parameter isn't quite a "speed" anymore, because we approach the target in a Zeno-like fashion. If sharpness was 0.5, then on the first update we move halfway to our goal. Then on the next update we move half the remaining distance (so a quarter of our initial distance). Then on the next we move half again...

This gives an "exponential ease-in" where the movement is fast when far from the target and gradually slows down as it approaches asymptotically (though with infinite-precision numbers it will never reach it in any finite number of updates - for our purposes it gets close enough). It's great for chasing a moving target value, or smoothing a noisy input using an "exponential moving average," usually using a very small sharpness parameter like 0.1 or smaller.

But you're right, there is an error in the upvoted answer you link. It's not correcting for deltaTime the right way. This is a very common mistake when using this style of Lerp.

But our exponential easing is non-linear, so just multiplying our sharpness parameter by deltaTime will not give the correct time correction. This will show up as a judder in the movement if our framerate changes, or a change in the ease sharpness if you go from 30 to 60 consistently.

There's one other arguable error in that code, which is using Slerp - spherical linear interpolation is useful when we want an exactly consistent rate of rotation through the whole movement. But if we're going to be using a non-linear exponential ease anyway, Lerp will give an almost undistinguishable result and it's cheaper. ;)

1. Linear blending between a start and an end

2. Exponential ease toward a target

Note that in this version the current value appears as both the output and an input. It displaces the start variable, so we're always starting from wherever we moved to on the last update. This is what gives this version of Lerp a memory from one frame to the next. From this moving starting point, we then then move a fraction of the distance toward the target dictated by a sharpness parameter.

This parameter isn't quite a "speed" anymore, because we approach the target in a Zeno-like fashion. If sharpness was 0.5, then on the first update we move halfway to our goal. Then on the next update we move half the remaining distance (so a quarter of our initial distance). Then on the next we move half again...

This gives an "exponential ease-out" where the movement is fast when far from the target and gradually slows down as it approaches asymptotically (though with infinite-precision numbers it will never reach it in any finite number of updates - for our purposes it gets close enough). It's great for chasing a moving target value, or smoothing a noisy input using an "exponential moving average," usually using a very small sharpness parameter like 0.1 or smaller.

 

But you're right, there is an error in the upvoted answer you link. It's not correcting for deltaTime the right way. This is a very common mistake when using this style of Lerp.

But our exponential easing is non-linear, so just multiplying our sharpness parameter by deltaTime will not give the correct time correction. This will show up as a judder in the movement if our framerate fluctuates, or a change in the easing sharpness if you go from 30 to 60 consistently.

Instead we need to apply an exponential correction for our exponential ease:

 

There's one other arguable error in that code, which is using Slerp - spherical linear interpolation is useful when we want an exactly consistent rate of rotation through the whole movement. But if we're going to be using a non-linear exponential ease anyway, Lerp will give an almost undistinguishable result and it's cheaper. ;) Quaternions lerp much better than matrices do, so this is usually a safe substitution.

added 84 characters in body
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401
Loading
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401
Loading