- Home /
How to smoothly align an object to a surface, but only partially
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)
[2]: http://forum.unity3d.com/threads/33987-Character-align-to-surface-normal
first off, great post, very clear (upvoted)
now, does the surface change orientation, or is it simply parallel to the xz plane at all times? in other words, will the object need to continuously check the orientation of the particular surface below it (such as a terrain map, versus a simple plane)?
@Seth_Bergman - The surface won't change orientation -- it's safe to assume the surface is always a flat plane :)
Answer by Seth-Bergman · Jul 26, 2013 at 01:28 AM
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.
EDIT:
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:
That makes a lot of sense -- it's usually the way I solve things (in a more physical, rather than raw mathematical, sense) haha. I'll give it a shot tomorrow. I think ideally it'd be done through math, but that sounds like a solid backup plan.
I've used similar tactics in past projects, to get vehicles to conform to terrain, so it is definitely viable.. But I got nothin against maths :)
Answer by robertbu · Jul 26, 2013 at 06:40 AM
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;