- Home /
slerp vs lerp for rotation
setup I have a AI agent (turret) that has a set arc that it rotates along (90 degrees), with a given field of view (30 degree cone). I have got the agent to rotate on the arc, but I have a problem with having the turret when the player is inside the FOV to get the agent to track the player, and follow.
I tried to use the following method to have the agent track the player:
void FiringPos(Vector3 target) {
if(angle < 1){
print ("fire");
}else{
transform.forward = Vector3.Slerp(transform.forward, target, Time.deltaTime);
}
}
this tracks the player at the rate that I want, but when this gets within 10-15 degrees it stops tracking, and starts gittering (bouncing between values that are less then 1.0 from each other), and if the player moves to a greater angle within the FOV the agent starts tracking again until it is back within 10-15 degrees and starts gittering again.
so I tried to replace the Slerp()
call with a Lerp()
.
this as soon as the player enters the FOV at any point it jumps to the exact angle of the player.
is there any way that I could meld the 2 of these together, or get rid of the gitter in the Slerp()
.
Not really an answer, but an option. Have you ever used the free library called iTween? You should be able to get it from the app store or its own website. It has rotateTo functions that allow it to make things like turrets that track to a transform with very little extra coding effort. There is an example on the website that tracks a turret to point toward the cursor, for example.
I second @shadowriffe's comment. I've never used iTween for this specifically, but our group has used it for other things and been pleased. Note that the library itself is free and open source but if you want the examples as code you have to buy them.
I'll even add that I bought the examples, but the documentation alone provides enough help that you should be able to implement at least the turrets rotation with little effort.
Is it possible that target isn't a direction, but a point in world space? I guess you mixed up positions with directions...
Answer by Bunny83 · May 23, 2012 at 01:15 AM
Here are three ways to look "towards" a target. Note that the target vector is a position in world space coordinates.
// This will rotate with a certain speed
void SmoothLookAt(Vector3 target, float speed)
{
Vector3 dir = target - transform.position;
Quaternion targetRotation = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, Time.deltaTime * speed);
}
// This will rotate with a decelerated speed (not frame independent)
void SmoothLookAt(Vector3 target, float smooth)
{
Vector3 dir = target - transform.position;
Quaternion targetRotation = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * smooth);
}
// This should be the same as the decelerated version (also not frame independent)
void SmoothLookAt(Vector3 target, float smooth)
{
Vector3 dir = target - transform.position;
transform.forward = Vector3.Slerp(transform.forward, dir, Time.deltaTime * smooth);
}
All those functions haven't beed tested.
First option is implemented, and works. Have not tested others.
Answer by Julien-Lynge · May 22, 2012 at 07:42 PM
First off, it would be nice to have a bit more context - where is angle defined, what does it mean, what calls FiringPos (is it called every Update()?), etc. Second, depending on how angle is defined, you may want to be using (Mathf.Abs(angle) < 1).
However, those are both minor things. The meat of the problem that I see is that you aren't using Slerp correctly. Slerp basically figures out a rotation path from a start orientation to an end orientation. You then pass it a number between 0 and 1 telling it where on that path you want to be. So passing in Time.deltaTime doesn't make sense. What you need is to pick some start time, and then pass it (Time.time - startTime). Thus, in the first frame you'll be passing it 0 (the start of the path), the next frame some further point along the path, and so on until your reach 1 (the end of the path).
Hope that helps.
Absolutely correct ;) But he's doing the common thing and abuses Lerp for a smoothed (decelerated) rotation. I'm not sure if he actually want this behaviour, but if you use lerp that way it makes almost no difference is you use Lerp or Slerp, since the start rotation changes each iteration and he moves a certain percent value (Time.deltaTime) twowards the target. The big difference is that Slerp will preserve the vectors length (which should be 1.0 for directions) while lerp literally draws a straight line between start and end and moves on this "line".
angle is defined from a static reference to the players position using Vector3.Angle()
, and the method is being called if the player is withing set distance, and angle is within that 30 degrees.
looking over this it is not conducive to the desired behavior that if the player moves then the time wouldn't make to much sense.
@Julien @Bunny83 I kind of understand what your saying here, but at the same time I don't think it fits into my problem space. I think that if the Vector3 target
were stored then using the difference in time would be beneficial, or possibly if the player was at a fixed point, but the player, and therefore the vector are changing.
Slerp() is actually working with the syntax that I am using in the question, but it just starts to jitter (upon further review at values less then 12 degrees). I am actually liking the way that Slerp()
is working up until that point. I even tried to if(angle < 12){ /*use Lerp()*/}
this causes a noticeable jump that I don't like at all.
besides on the using time.deltatime
http://unity3d.com/support/documentation/ScriptReference/Transform-rotation.html shows that it is perfectly legal, and functional to use time values.
Sorry, but most examples in the scripting reference are poorly choosen or simply wrong.
In your case it makes absolutely no difference if you use Lerp or Slerp. Read the explanation of Slerp & Lerp on wikipedia.
Time.deltaTime is more or less a constant value as long as your framerate keeps the same. For example at 50 fps deltaTime is 1/50 == 0.02
This is the lerp formular : (1-t)*From + t * To
which can also be written like this:`From + t * (To - From)`
Since your t value is a "constant", your lerp just calulates:
pos = pos + 0.02 * (target - pos).
Slerp is doing exactly the same. It will just use a slightly bigger t value for greater angles and it will be almost the same for small angles.
Just to be clear: There is no right or wrong! A lot people abuse the Lerp function as smoothing function. Slerp will be better for rotations, but using lerp or slerp this way the result will never be frame-independent since it's not a linear function anymore, it's an exponential function which can't be lineary scaled by deltaTime.
This use of Lerp can be called a Lerp-filter - it's the equivalent of a resistor-capacitor low pass filter in electronics. After a time t, the value returned by Lerp in this configuration is at a distance from the destination equal to 100%/exp(k * t), where k is the value multiplied by Time.deltaTime.
In other words, for k = 1, after 1 second the distance will be 36%, 13.5% after 2 seconds, 5% after 3 seconds, 1.8% after 4 seconds, 0.7% after 5 seconds etc.
NOTE: These values are precise at infinite framerate - real world framerates "slowdown" the curve, taking more time to reach some specific distance.