LookAt() with Explicit Worldup

Hi,

I have run into an issue several times with the LookAt() function.

I am using it to point an AI in the direction of a target by specifying a position and a “worldUp” vector. The code seems to almost work, but the AI seems to tilt along the X and sometimes Z axes, IE, a “Worldup” of (0,1,0) end up being something like (0.1,0.9,0) or something similar.

I suspect this is because, from the Unity API:

“worldUp is only a hint vector. The up vector of the rotation will only match the worldUp vector if the forward direction is perpendicular to worldUp.”

I am not sure how to make the forward direction perpendicular to the worldup, I am simply doing this:

lookpos = target.position;
transform.LookAt(lookpos,target.up);
transform.position += transform.forward * 1.5f * Time.deltaTime;

So, what I need is either a version of LookAt() which doesn’t use Worldup as a “hint” vector, but an explicit, exact, vector, or some way of ensuring that the “forward direction is perpendicular to worldUp”.

Is there something like this Available?

Thanks!

use Quaternion.LookRotation:

Instead of looking at a target, it looks in a direction of a vector. Which is technically what LookAt does… all it does is calculate said vector, and then look in that direction. You’re going to do the direction yourself.

Then what you do is this:

var lookDir = target.position - transform.position;
lookDir.y = 0f; //this is the critical part, this makes the look direction perpendicular to 'up'
transform.rotation = Quaternion.LookRotation(lookDir, Vector3.up);
transform.position += transform.forward * 1.5f * Time.deltaTime;

If you wanted to do this on an arbitrary up vector… it gets slightly more complicated.

But way you look at it is by setting ‘y’ to zero… really what your’e doing is ‘redacting any weight of the vector on the y-axis’. It just happens that the y axis is explicitly defined on the vector. But really you could have seen it as:

dir -= Dot(dir, up) * up

So you can do that for any arbitrary axis:

dir -= Vector3.Dot(dir, upVector) * upVector;

(note, upVector must be a unit/normalized vector)

Ah, I think that’s what I was trying to do before, but I didn’t quite have the mathematical skill to figure out how to set an arbitrary vector. I’ll give that a shot, thank you!

This seems to work great, it solves the tilt on the x axis. Is there a way to do the same thing on the equivalent of the Z-axis?

So, basically this: dir -= Vector3.Dot(dir, upVector) * upVector;

But to make the Z axis 0 for an up vector of (0,1,0)?

Thanks a lot!

If the z-axis is your up… just 0 out the ‘z’ field of the directional vector.

The dot product formula would be if up is some weird axis like <0.7071, 0.7071, 0> (a vector pointing 45 degrees off x and y).

I am using an arbitrary up axis. If I assume it is (0,1,0), I need to basically negate the X and Z axes in the same way the code you posted negates the X axis, so basically the equivalent of:

transform.LookAt(lookpos,new Vector3(0,1,0));

Except with the X and Z axes firmly fixed, and not slightly off.

I seem to have fixed this by setting transform.up to target.up before setting the lookdir.
So:

var lookDir = target.position - transform.position;
transform.up = target.up;
lookDir -= Vector3.Dot(lookDir, target.up) * target.up;
transform.rotation = Quaternion.LookRotation(lookDir, Vector3.up);
transform.position += transform.forward * 1.5f * Time.deltaTime;