So I have an object and I want to align it to a surface, but still give freedom of movement along some of the axises. I have two behaviors I want (also made a video, linked below, to help explain).
In some instances, I want the roll to be aligned to the surface and allow freedom of rotation along the pitch and yaw (scenario 1) – this would let users look up/down and left/right relative to the surface (imagine FPS mouselook, but standing on a wall). In other instances, I want the roll and pitch to be aligned to the surface and only allow freedom of rotation along the yaw (scenario 2) – this would let users only look left/right along the surface.
I need to be able to smoothly transition from/to arbitrary rotations, so for Scenario 1, I’m thinking I’d need to rotate along the roll axis (euler z) until the roll is lined up with the surface horizon. This is where I get totally lost. How would I even measure how far off the roll is from the surface horizon (knowing the pitch/yaw could be anything) so that I can smoothly roll the object into alignment?
Video to help describe what I’m trying to do: http://www.youtube.com/watch?v=VFVCiFz1STE (demonstrates Scenario 1 – I’m hoping after solving scenario 1, it wouldn’t be too hard to solve scenario 2)
I would do it this way:
add an empty game object to each of the four “corners” of your plane (nose, tail, each wingtip)
do a raycast straight down in world space from each wingtip, and compare the distance to the ground… if the right is higher, it will return a greater distance… then you can rotate the object around its own (z?) axis until they are even… the same method should work with the nose/tail for the pitch I believe.
in your case, however, this may be overkill… if the surface is always flat, we can probably just use a parent object with the default orientation, for each rotation…
i.e. an empty gameobject which you parent the plane to, and apply ONLY z rotation to this object… then just keep the parent oriented… if that makes sense…
okay, maybe that’s not clear enough…
by separating each axis of the rotation to a separate gameobject in the hierarchy, we can achieve the same end result without the need for complex rotation calculations… so the hierarchy could be like :
empty parent(z rotation only)>child empty(y rotation only)>child plane(x rotation only)
by keeping each one separate, adjustments in general become much more manageable imho
since you took the time, here’s what I mean:
If 1) you can make the object a child of the reference plane (not strictly necessary), and 2) if you have a central place in your code where you make yaw, pitch, and roll changes, and 3) if your code directly makes the rotation changes (i.e. the object is not getting tossed around by Rigidbody forces), then this problem becomes easy. Make your object a child of the reference plane and encode your yaw, pitch and roll in a Vector3. The Vector3 is then assigned to Transform.localEulerAngles. The trick to making this work is to never read Transform.eulerAngles or Transform.localEulerAngles. All calculations are placed in your Vector3 and then assigned to Transform.localEulerAngles.
If you don’t want to make the object a child, you can just multiply the rotation by the rotation of the reference plane. Something like:
transform.rotation = refPlane.transform.rotation * Quaternion.Euler(myRotationVector3);
Scenario 2 is also easy for the general case (i.e. no Vector3 and object impacted by forces).
Quaternion q = Quaternion.FromToRotation(refPlane.transform.up, transform.up);
transform.rotation = q * transform.rotation;