I can’t quite get why using it gives the desired results… and then at other times using Time.time
works (eg in the lerp scripting reference examples Time.time
is used?)
Thanks.
I can’t quite get why using it gives the desired results… and then at other times using Time.time
works (eg in the lerp scripting reference examples Time.time
is used?)
Thanks.
Time.deltaTime
is the time since the last frame. In Lerp
, the third parameter is a float between 0
and 1
. If it’s 0
, you get the first parameter in Lerp
. If it’s 1
, you get the second.
Therefore, with a statement like:
transform.position = Vector3.Lerp(transform.position, target.position, Time.deltaTime);
in Update
, you get a position that’s part-way between transform.position
and target.position
, depending on the time difference between the current frame and the last frame.
For example, a Time.deltaTime value of .01
(100fps) would return a value that’s a combo of 99% transform.position
and 1% target.position
. As this is repeated each frame, the transform.position
, assuming target.position
is stationary, becomes closer and closer to target.position
but never quite reaches it. (Unless the framerate drops to 1fps or less, in which case Time.deltaTime
would be 1
or greater, so the return value would be 100% target.position
.)
Note that this is a “trick” usage of Lerp
, which creates a “slow down as you approach the target” effect. It’s mathematically incorrect, though, since it will produce results that vary somewhat depending on the framerate.
This is better:
function MoveObject (thisTransform : Transform, startPos : Vector3, endPos : Vector3, time : float) : IEnumerator {
var i = 0.0;
var rate = 1.0/time;
while (i < 1.0) {
i += Time.deltaTime * rate;
thisTransform.position = Vector3.Lerp(startPos, endPos, i);
yield;
}
}
That’s a Coroutine
that moves a transform
from startPos
to endPos
over a given time. This happens as i
advances from 0.0
to 1.0
, and results in completely linear movement, without the “slowdown” effect.
As for Time.time
, consider:
transform.position = Vector3.Lerp(startPos, endPos, Time.time);
in Update
. This moves the transform from startPos
to endPos
as the time advances from 0.0
to 1.0
. Once the time is past one second, no further movement will occur since the third parameter in Lerp
is clamped between 0
and 1
. Obviously this isn’t very useful unless you actually only want movement to happen for the first second of gameplay for some reason.
Another script based on Eric’s, that prevents the jag effect. Thanks to @Eric5h5 for the original script, the idea of rate is very useful.
var pointA:Vector3;
var pointB:Vector3;
var time:float = 10.0;
private var i:float = 0.0;
private var rate:float = 0.0;
function Update ()
{
MoveObject(this.transform, pointA, pointB, time);
}
function MoveObject (thisTransform : Transform, startPos : Vector3, endPos : Vector3, time : float)
{
rate = 1.0/time;
if (i < 1.0)
{
i += Time.deltaTime * rate;
thisTransform.position = Vector3.Lerp(startPos, endPos, i);
}
}
Eric mentioned the FPS can vary and it will throw unexpected high values for deltaTime
. I think this deserves more attention.
In practice, when I was using Time.deltaTime
and Quaternion.Lerp (it’s actually irrelevant if it’s quaternion or vector3 or whatever lerp) I was getting this nasty looking error:
CompareApproximately (aScalar, 0.0F)
UnityEngine.Quaternion:Lerp(Quaternion, Quaternion, Single)
This is because, eventually the deltaTime would make it bigger than 1, thus clamping to 1 and, in my code, for the 1st frame only, the destination rotation was a very bad (0, 0, 0, 0);
. The point here is: both the clamping and the bad value were unexpected, resulting into a little hard to debug error. Mostly because it’s hard to reproduce.
The true effect was nothing that makes the lerping noticeable. But this is something to consider for point 2:
As we can see in (1) there is this extra problem with the trick as it will not behave the same every time, precisely because the FPS can vary. But it’s not that easy to reproduce the trick also, because it kind of balances out processor speeds and so it’s one of the best tricks to make a Lerp work similarly in different machines.
For instance, a much simpler way to achieve the same ease out trick effect is to just set a const
:
transform.rotation = Quaternion.Lerp(originalRotation, targetRotation, 0.1f);
But if you run that on a i7 quad core versus on an iPhone, you’ll already see the difference.
So, there is in fact a good use for Time.detalTime
on lerping. You just need to be aware of the caveats!
I have a potentially simpler solution to that of @Eric5h5’s involving the “slow down while approaching target” effect (the curvature is slightly different though):
curPos = Vector3.Lerp(curPos, endPos, 1-Pow(2, -Time.deltaTime * rate));
curPos = current position
endPos = end position
2
- arbitrary, could be anything larger than 1 (changing it does the same thing as changing the rate).
rate
- the rate of the movement - higher value means approaching the end position faster.
Here’s an example to prove this works:
For simplicity let’s just assume the positions are real numbers.
curPos = 1 (at the beginning), endPos = 0, rate = 1.
We’ll track curPos
over the duration of 1 time unit to determine the final result in two cases:
in this case the calculation will run 1 time:
curPos = Lerp(1, 0, 1-Pow(2, -1*1)) = Lerp(1, 0, 0.5) = 0.5;
so the final result is 0.5
.
in this case the calculation will run 4 times:
curPos = Lerp(1, 0, 1-Pow(2, -0.25*1)) = Lerp(1, 0, 0.16) = 0.84;
curPos = Lerp(0.84, 0, 1-Pow(2, -0.25*1)) = Lerp(0.84, 0, 0.16) = 0.705;
curPos = Lerp(0.705, 0, 1-Pow(2, -0.25*1)) = Lerp(0.705, 0, 0.16) = 0.59;
curPos = Lerp(0.59, 0, 1-Pow(2, -0.25*1)) = Lerp(0.59, 0, 0.16) = 0.5;
so the final result is still 0.5
, regardless of Time.deltaTime
’s value.
The reason this works is actually pretty simple.
Going with the previous example, what we actually did was this:
Time.deltaTime
= 1
:curPos = 1*2^(-1)
Time.deltaTime
= 0.25
:curPos = 1*2^(-0.25) *2^(-0.25) *2^(-0.25) *2^(-0.25) = 1*2^(0.25-0.25-0.25-0.25) = 1*2^(-1)
I hope this can help someone.
Hopefully someone reads this part as well because this took me some testing to figure out since Im such a noob…
If you move an gameObject
over frames like this:
currentDistance = (currentFrame / target_Frame);
gameObject_ToMove.transform.position = Vector3.Lerp (startingPosition, targetPosition, currentDistance);
To make it frame independent it’s incredibly simple. You just want to add Time.deltaTime
onto the end of the current distance like so:
currentDistance = (currentFrame / target_Frame) + (0.01f * Time.deltaTime);
gameObject_ToMove.transform.position = Vector3.Lerp (startingPosition, targetPosition, currentDistance);
The reason I use 0.01f
is because if you just add Time.deltaTime
onto the end of your equation alone it will go too fast.