Difference Between Two Ways to Automatically Point to Object

I am making a game involving 2D turrets. The turrets need to rotate around the z-axis and aim at the closest gameobject on the screen automatically. Isolating just the aiming portion, here are two ways I found on the internet:

Vector3 lookDirection = target.transform.position - transform.position;
float angle = Mathf.Atan2(lookDirection.y, lookDirection.x) * Mathf.Rad2Deg;
Quaternion newRotation = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * 2f);

and

Vector3 lookDirection = target.transform.position - transform.position;
Quaternion newRotation = Quaternion.LookRotation(lookDirection);
newRotation.x = 0; newRotation.z = 0;
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * 2f);

Both work pretty well, but if I use the second one, the turret will flip around and point backwards at its target if the target moves “behind” the turret (on the x axis). I haven’t seen any problems with the first one.

Could anyone tell me what the difference is, or more specifically, why the second one doesn’t work?

well the second one is modifying the components of a quaternion, which unless you know what your doing is a big no-no. and the fact that the are simply setting x and z to 0 clues you in that they don’t know what they are doing.

heres a more solid implementation of the second one

Vector3 lookDirection = target.transform.position - transform.position;
lookDirection.z = 0;// always pivot on z axis
if(lookDirection.sqrMagnitude <= float.Epsilon)
   return;// can't rotate, since two points are along the same Z-axis, thus do nothing


Quaternion newRotation = Quaternion.LookRotation(lookDirection.normalized);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * 2f);

the sqrMagnitude check is simply there so you don’t get spammed by Debug logs saying that the look rotation is zero

you can look into a third way
transform.LookAt(target);

LookAt is instant, he wants to animate from his current rotation to the target rotation over time. this is why he is using Slerp (in an acceptable, albeit technically incorrect, way).

https://vimeo.com/217685714

https://vimeo.com/217685727

Okay the first video shows the (desired) behavior for the first method, and the second shows Josh’s new and improved version of the second method.

Why does the left turret only rotate around the z-axis in the first video, while it rotates weirdly and does a flip when the right turret passes behind it in the second video?

Also could you let me know what is technically incorrect about my slerping?

you can always just lock the z axis in the inspector. this will lock it in place and never move

Any lerp function has three parameters, and is meant to be used as:
current = Lerp(start, end, normalizedTime)
Where normalizedTime is from 0 to 1.

What you’re doing is
current = Lerp(current, end, smallTimeValue)
The main way this will break down is if you have variations in framerates. If your deltatime is usually 0.01, it will very slowly approach (but never actually reach) the “end”. If it’s 0.1, it’ll approach the “end” much faster. It’s easiest to imagine this effect if you imagine 1 frame per second - deltaTime will be 1.0, and it’ll reach the “end” immediately.

RotateTowards can be used the way you’re doing it - it’s meant to be:
current = RotateTowards(current, end, smallDegreesValue)

Look rotation points orients the object’s forward axis (the blue arrow) to the object, the second optional parameter specifys which way the it should favor its up axis (green arrow), by default this is world up.

looking at your video you can see the issue, you want to point the sprite’s y-axis at the target, and constrain the local z-axis ( assumed you meant world z-axis).

thinking about it, you can probably swap the values you put into look rotation

// use transform.forward or Vector3.forward, depending on your game
// if its facing the opposite way use a negative forward
Quaternion newRotation = Quaternion.LookRotation(transform.forward, lookDirection.normalized);

this should be sufficient.

Or you could use the previous look rotation and then multiply the rotation by the needed offset:

Vector3 side = lookDirection.x<0?Vector3.left:Vector3.right;
Quaternion offset = LookRotation(side, Vector3.forward);
Quaternion newRotation = Quaternion.LookRotation( lookDirection.normalized )  * offset;

Slerp stands for spherical-Linear Interpelation. you would animate the 3rd value from 0 to 1, where 0 would return first quaternion, and 1 would return the second quaternion.

and you would get a smooth, constant, and linear rotation (by degrees).

however the way you are using slerp now you’ll end up with the effect where the rotations seem rubberbandy. slower the closer the current rotation gets to the target rotation.

I said “acceptible” cause it still works and the “rubberband” effect may be what you want anyway.

Thanks for the responses.