- Home /
How can I make a Lerp move in an arc instead of a straight line?
I have 3 transforms set up as per the left diagram below. When lerping a gameobject from one position to the next (eg. transform 1 to transform 2, or transform 2 to transform 3) it moves in a straight direct line, however what I would like to do is add curvature to the lerp as per the right diagram below. There would need to be a variable controlling the amount of "bending" in the path; so for example if this variable had a value of 0 the lerp would be completely straight as usual, but the more it is increased the more the path would bend. What needs to be added to the lerp code to move in an arc instead of a straight line? Or is a lerp not the best approach for this?
I have managed to get this working for a single axis using the following code. At the moment it is ping ponging which I would like to change to only move from A to B and stop. Also, how can make this work for both the X and Z axes?
public Transform capsuleTransform;
public Transform start;
public Transform end;
public float t;
public float bendFactor = 1;
public float speed;
// Use this for initialization
void Start () {
capsuleTransform = gameObject.GetComponent<Transform>() as Transform;
}
// Update is called once per frame
void Update () {
t = Mathf.PingPong(Time.time * speed, 1f);
Vector3 pos = capsuleTransform.position;
pos.y = Mathf.Lerp(start.position.y, end.position.y, t);
pos.x = (Mathf.Sin(t*Mathf.PI))*bendFactor;
pos.z = Mathf.Lerp(start.position.z, end.position.z, t);
capsuleTransform.position = pos;
}
Answer by Bunny83 · May 05, 2015 at 01:03 PM
You might want to use a cubic bezier curve. A little method like this should be helpful ^^:
// C#
public static Vector3 cubeBezier3(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
return (((-p0 + 3*(p1-p2) + p3)* t + (3*(p0+p2) - 6*p1))* t + 3*(p1-p0))* t + p0;
}
You have to pass the 4 control points of the bezier curve. So p0 would be your start point p3 your end point. p1 and p2 has tp be calculated "somehow". If your transforms have a direction (which it looks like) you simply use the forward axis and add it to the position to get the "tangent point". At the end point of course you use -forward of the target transform. Keep in mind that you might have to scale the forward vector according to the distance along that direction.
This is an slternative implementation. It requires less additions (and even saves a multiplication). It's not at compact, but you can see how it actually works:
public static Vector3 cubeBezier3(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
float r = 1f - t;
float f0 = r * r * r;
float f1 = r * r * t * 3;
float f2 = r * t * t * 3;
float f3 = t * t * t;
return f0*p0 + f1*p1 + f2*p2 + f3*p3;
}
The important point is that f0+f1+f2+f3 always equals 1 (when t is in range 0..1)
edit
Since the return line does execute several method calls (4x vector3 scalar multiplication + 3x vector3 addition) it would be way faster to actually inline all those operations:
So instead of :
return f0*p0 + f1*p1 + f2*p2 + f3*p3;
do this:
return new Vector3(
f0*p0.x + f1*p1.x + f2*p2.x + f3*p3.x,
f0*p0.y + f1*p1.y + f2*p2.y + f3*p3.y,
f0*p0.z + f1*p1.z + f2*p2.z + f3*p3.z
);
If you use this method a lot ( > 1000 times per frame ) this will really make a difference.
Another one would be to use Catmull-Rom. The annoying part is how to use it.
This gives an interpolation by t between p1 and p2 while p0 and p4 only serve for the tangent calculation (derivative). So in the case of the first and last point you need to provide a Vector3.zero for the p0 (first point) and p4 (last point).
can you give an example for how to use this function with code? i cant make it work
What kind of example do you expect? Did you actually look up Bezier curves? The wiki article has several images to get a proper understanding what p0 - p3 represents:
(source wikimedia commons) (source wikimedia commons)
It just works exactly the same as Lerp but ins$$anonymous$$d of providing two points and interpolating a long a line we have to provide 4 points and it will interpolate along a bezier curve.
If you have trouble understanding how this equation is actually obtained, it's actually just several nested lerps. See this paste I've created. Referring to the animated gif the points a0, a1 and a2 are the 3 endpoints of the two green lines. Likewise b0 and b1 are the endpoints of the blue line while "c" is the final lerp along that blue line to give us the actual black dot. Actually perfor$$anonymous$$g those 6 nested Lerp calls would give you the same result but would be much less efficient. Each method call adds overhead.
Answer by siaran · May 03, 2015 at 12:31 PM
Lerp is not really the way to go - Lerp stands for *L*inear Int*erp*olation.
Which means you'll get, well, lines. You'll probably have to write your own interpolation function that gives the kind of shape you want.
I can create a new question if need be, but for simplicity sake, would using Vector3.$$anonymous$$oveTowards ins$$anonymous$$d of Lerp make any difference in this case? Or is $$anonymous$$oveTowards also intended for straight lines only?
Yes. However! You can still compute an arc with Lerp: Lerp along the line from source to target, and use the current distance to feed q quadratic term (you know, like y = x²).
I have updated the code in the question. There is a way but I could use some advice for the final adjustments.
Your answer
Follow this Question
Related Questions
Determine transform.position of location between two waypoints on bezier path 0 Answers
Using iTween.PutOnPath 1 Answer
how to create a smooth curve with no edges 0 Answers
How to make a circle with three known points 4 Answers
How to make a custom arc 0 Answers