Vector2 Lerp not working when angle is 180 degrees
So I am trying to code a top down controller where the character rotates towards where my joystick is pointing.
The problem I am having is that it doesn't work when the angle between the old vector and new one is 180 degrees. I understand why it doesn't work, I just can't think of another way to do it that wouldn't have this problem. Was thinking of just handling the edge case and doing something different when the angle was 180 but that just doesn't feel right.
Here is the code I have:
xVel = Input.GetAxis ("Horizontal");
yVel = Input.GetAxis ("Vertical");
Vector2 newUp = new Vector2(xVel, yVel).normalized;
transform.up = Vector2.Lerp(transform.up, newUp, plStats.rotationFactor * Time.deltaTime);
Thank you for any help you can give me!
Answer by LCStark · Sep 16, 2018 at 09:36 AM
So, if my understanding of the problem is correct, the issue here is that you're directly lerping the direction vector, which - when lerping between opposite vectors, like (0,-1) and (0,1) - makes the character stand still, until the lerped value reaches (0,0) and the character simply rotates by 180 degrees immediately.
This works good for small rotations, but the bigger the change in your vector the worse the result will look - near the beginning and the end of lerping the object will barely rotate at all. The closer to the middle, the faster the rotation will be. If you want smooth rotation, you need something else.
Instead of working directly on a directional vector, try calculating and lerping between rotation angles. Get the rotation angle between your current transform.up
and some reference vector (like (0,1)), get the angle between the newUp
and that same reference vector, and then lerp between these two angles. You can get the lerped directional vector by multiplying the reference vector by a rotation quaternion: Quaternion.Euler(0.0f, 0.0f, angle) * Vector2.up
.
Use Vector2.Angle or Vector2.SignedAngle to get the angle of rotation between vectors.
Thank you! This what I ended up with in the end. I used $$anonymous$$oveTowardsAngle ins$$anonymous$$d of lerping to control the speed of the rotation more, I also checked if my movement vector was zero so that the rotation would only happen if there was movement, otherwise it would reset back to the default position:
xVel = Input.GetAxis ("Horizontal");
yVel = Input.GetAxis ("Vertical");
Vector2 newUp = new Vector2(xVel, yVel).normalized;
if(newUp != Vector2.zero)
{
float angle1 = Vector2.SignedAngle(Vector2.up, transform.up);
float angle2 = Vector2.SignedAngle(Vector2.up, newUp);
float newAngle = $$anonymous$$athf.$$anonymous$$oveTowardsAngle(angle1, angle2, plStats.rotationFactor * Time.deltaTime);
transform.rotation = Quaternion.Euler(0, 0, newAngle);
}
You're welcome! Using $$anonymous$$oveTowardsAngle
is actually a good idea. Lerp
would have caused another issue - Lerp
is a linear interpolation between two values, using the third as the point of interpolation (with values between 0.0f and 1.0f). If we assume that plStats.rotationFactor * Time.deltaTime
wouldn't change during the program execution, Lerp
would modify the value by smaller and smaller amount with each frame, moving the value closer to the target, but never actually reaching it.
I would suggest, though, still using the Time.deltaTime
multiplier in the third parameter. If you use your code in the Update
method, the rotation will be dependent on the frame rate. If the rotation is too slow, you can always either increase the value of plStats.rotationFactor
, or add an additional multiplier value ( 100.0f * Time.deltaTime * plStats.rotationFactor
).
Yeah, that's what I was getting at when I said I wanted more control of the rotation. I have this in FixedUpdate so it should be fine without Time.DeltaTime right, since it's called on a constant timeline ins$$anonymous$$d of as fast as it can go like Update?