- Home /
how to stop Lerp?? Hot to reach EXACT end position.
I got in trouble with Lerp. I want to make one gameobject go to another. I am using Vector3.Lerp.
object1.transform.position = Vector3.Lerp (object1.transform.position, object2.transform.position,Time.deltaTime*speed)
object2.transform.position.x = 50, but when it lerps object1 goes straight to this point (x=50),but never reaches it.It stops on 49.9999. I need object1 to reach exactly 50. How to make it ? Maybe there are some ways to stop lerping ? I was thinking to make like this.
object1.transform.position = Vector3.Lerp (object1.transform.position, object2.transform.position,Time.deltaTime*speed)
(STOP LERPING)
object1.transform.position = object2.transform.position
Answer by tanoshimi · Feb 16, 2015 at 01:58 PM
Using Lerp in this way is one of the most widespread anti-patterns in Unity - including in the Unity tutorials themselves :(
From the manual:
"Lerp(from, to, t)
interpolates between from
and to
by the fraction t
. When t = 1
returns to
. When t = 0.5
returns the point midway between from and to."
So, to actually ever get to the end value, to
, the third parameter must be 1
(or greater). Your code is passing Time.deltaTime*speed
. Unless speed
is greater than your framerate per second (which, from your description, I'm assuming it's not), this value is always going to be less than 1. So on every frame you move a fraction of the remaining distance closer to to
, but never quite get there.
You'll also experience other problems from using this approach - the movement itself will not be linear - the distance covered in each step will decay (fast at first, then slow down as it approaches, which may or may not be what you want). You'll also be making your movement framerate dependent, so that the rate of movement varies on different machines, which you almost certainly don't want.
There's a good explanation of these problems, and the "correct" way to use Lerp at http://www.blueraja.com/blog/404/how-to-use-unity-3ds-linear-interpolation-vector3-lerp-correctly
I$$anonymous$$O this is very much not an anti-pattern. It's just something that a lot of people don't understand (and yes, tutorials don't help with that when they only show one way of using Lerp).
I think the problem is that people think they understand the usage of Lerp in this manner, and it seems to be a convenient way, in a single line of code, to smoothly move a transform towards a destination. But this approach has multiple subtle problems associated with it which I attempted to point out (the framerate dependence being the most important, I$$anonymous$$O). Perhaps anti-pattern is not quite the correct term... perhaps "dangerous bad habit" would have been better :)
I deliberately referred to the "correct" way to use Lerp in quotation marks, since I don't personally believe any method of using Lerp can be more correct than any other. But I think that people need to understand how and why they're getting the results they are. In the context of the question here, I don't think the OP does.
That's fair, and you're right that your explanation does explain things. I'm sorry for jumping on the "anti-pattern" thing a bit quick. It's a bit of a bugbear of $$anonymous$$e that more and more people seem to use it when actually what they're wanting to say is that someone's using the wrong pattern for what they're trying to do. What we have here are 2 ways of using Lerp, either or both of which can be correct. It's all in how and when you use them. If you do understand how the 'slowing down' Lerping works, and use it right, then it's often the best pattern to use and I think that may well be the case for the OP.
Hi there,
I'm new to this and I know this is an old question but I was wondering if you could help me.
I've read that blog you linked and others like it, but but I'm a little confused about the nature of Time.deltaTime*speed not being frame rate independent.
I've ran some tests and it seems that even with a wildly fluxing frame rate, sometimes between 500 and 2000, the difference never seems to much more than 100 milliseconds.
Is this why it is incorrectly assumed to be frame rate independent by a lot of people?
Would I be foolish to assume I could get away with this difference?
You can prove it's not framerate independent by just thinking about the logic: moving 10% towards a destination 25 times a second will not get the same result as moving 5% towards it 50 times a second. Whether that matters or not to you is up to you to decide.
Answer by otg2 · Feb 16, 2015 at 11:24 AM
You could do that.
Thus your code could look something like
private boolean _followObject = true;
private float _minDistance = 0.5;
void Update(){
if(_followObject)
{
if(Vector3.Distance(object1.transform.position, object2.transformation.position >_minDistance)
{
object1.transform.position = Vector3.Lerp(object1.transform.position,object2.transform.position,time.deltaTime*speed);
}
else object1.transform.position = object2.transform.position
}
}
Answer by Bonfire-Boy · Feb 16, 2015 at 01:11 PM
If you use Lerp the way you have then it gets slower and slower as it gets closer, and hence never reaches. It's one of Zeno's "paradoxes".
[Note - technically, since we're talking about discrete quantities rather than theoretical tortoises, it would get there eventually, in that the distance would get to be less than Mathf.epsilon. But how long this takes depends on the interpolation fraction and it can be a very long time].
The simplest fix is simply to check the distance between the 2 positions after the Lerp call, and if it's less than some (small) threshold of your choosing, set the moving object's position to that of the target.
You could also use Lerp the other way, using starting position as the first param and a cumulative time for the interpolation fraction. Then it'd stop because your interpolation fraction would reach 1. But you'd have to find another way of making it slow down as it got closer.
Please do consider the other answers before writing yours. All your info are valuable and right but they are also already given in other answers.
@fafase I did consider them. When I wrote $$anonymous$$e, I saw no other answers that I considered as answering the question usefully.
@fafase - I tend to be of the belief that, until the OP accepts an answer, it's absolutely right that different users should be free to propose their solutions.
There have been many similar attempts to explain the problem in this thread but, until the OP responds, we won't know which one has got the message through and solved their problem :)
well I just think 6 answers (5 now) is quite a few for a simple lerp question. but ok.
Answer by coolraiman · Apr 12, 2016 at 07:27 PM
create a static class without mono behavior then use this function
//interpolate an object from A to B over a fixed time
public static IEnumerator Interpolate(Transform obj, Vector3 destination, float overTime)
{
Vector3 source = new Vector3(obj.position.x,obj.position.y,obj.position.z);//deep copy
float startTime = Time.time;
while(Time.time < startTime + overTime && obj != null)
{
obj.position = Vector3.Lerp(source,destination,(Time.time - startTime)/overTime);
yield return null;
}
obj.position = destination;
}
Then from anywhere in any other class: StartCoroutine(NameOfTheStaticClass.Interpolate(theObjectTransformToMove, vectorEndPos, timeOfTheInterpolation));
and if you start this coroutine from a coroutine, you can wait for it to end like that
yield return StartCoroutine(NameOfTheStaticClass.Interpolate(theObjectTransformToMove, vectorEndPos, timeOfTheInterpolation));
you can modify this function to interpolate colors, rotation and etc...
This helped me. (Also to better understand coroutines) Thanks!
Answer by giulio-pierucci · Feb 16, 2015 at 10:59 AM
setting right position at and not is really wrong, however maybe that there is only a precision problem ( float error )
Try:
vector3 startPoint = object1.transform.position;
float t=0;
while(t<=1)
{
t += Time.deltaTime*speed;
object1.transform.position = Vector3.Lerp (startpoint, object2.transform.position,t);
}
And then, if position isn't always correct:
object1.transform.position = object2.transform.position;