Very basic rotation BUG. transform.up = transform.up sets Y rotation to 0.

Okay I’ve nailed it all down to an issue that looks like a bug. To reproduce it, simply create any 3D objects eg. a cube in an empty project, set its Y rotation to anything else than 0, add a rigidbody and a new script. In the script’s Start (or Update) method, simply write:

transform.up = transform.up;

Obviously, this will change nothing, right? Wrong. If you view the cube falling from above, you’ll see that its Y axis gets reset to 0 degrees as soon as you start Play Mode.

This issue is present for all axes. For example if I have an object traveling in direction Z, with a Z rotation different from 0, that Z rotation gets zeroed on startup if my scripts Update has:

transform.forward = transform.forward;

This somehow then evolves into a more general problem, for example I can’t keep my forward moving object’s original Z angle, it gets zeroed just like above, as soon as I set the object to face the direction of its velocity with

transform.forward = transform.GetComponent<Rigidbody> ().velocity; 

In my game, as the object that should at certain moments start to follow the direction of its own velocity is the parent to the first person camera, this means you’ll see the camera view angle making instant rotations which looks awful.

Anyone knows if this is indeed a bug I should report or maybe there’s something I’m missing that explains it?

Any workaround?

This is not a bug. I was expecting someone bringing this up sooner or later ^^. The properties “up”, “right” and “forward” return the objects local space axis. Unity also implements a setter for those properties. Of course when you set them Unity will create a rotation so that the given axis points along your given vector. However a single vector is not enough information to define a rotation in 3d.

Keep in mind that setting those properties do not perform some sort of “rotate towards” but it simply sets the absolute rotation. Imagine “forward” currently points to (0,0,1) so the object is not rotated at all. If you assign this vector (0,0,-1) to forward, what rotation do you expect? Did you mean to rotate around the y axis so up is still up? Or did you mean to rotate around the x axis so up becomes “-up” and the x axis remains? Unity could have implemented them as “choose a rotation that takes the shortest route from the current rotation”, but even that wouldn’t work in that example since both cases have the same distance.

That’s why Quaternion.LookRotation actually takes two vectors as input. You can omit the second in which case it defaults to Vector3.up. This second “up vector” defines the rotation around the first vector. So by default when you use an arbitrary forward vector, the resulting rotation will be rotated so the objects up vector is as close as possible to the given up vector. That’s also the reason why then looking straight down and rotate even further (over 90°) the view will flip 180°. So it’s not possible to get the view upside down without providing a proper up vector to LookRotation.

Of course when you assign something to those properties you can’t specify an additional reference vector so Unity always uses the default orientation as reference which is

up      = Vector3.up
forward = Vector3.forward
right   = Vector3.right

Unity only provides a LookRotation method for the z axis. However you can use it for any other axis with a bit of logic. Say you want to set the up vector to point along a certain vector (targetUp). You can simply use:

transform.rotation = Quaternion.LookRotation(targetUp, -transform.forward);

This will make your object to look straight up along your desired up direction. Now you just need to rotate 90 downwards to get your final rotation. For this you need to rotate around the right axis 90°

transform.Rotate(Vector3.right, 90f);

So those two lines will be the same as setting the up vector to a certain vector but it tries to keep the old forward axis.

If you want to set a new forward and want to keep the last up vector you would simply do:

Vector3 targetDir = transform.GetComponent<Rigidbody> ().velocity;
transform.rotation = Quaternion.LookRotation(targetDir, transform.up);