Lookat, FromToRotation and LookRotation?

I have been reading the documentation and it isn’t clear to me what the difference between them are. What I am trying to achieve is to get the objects Up (Y) axis aligns with the normal of the ground underneath and the objects Right (X) axis aligns points to a target. Thanks for you help :slight_smile:

Transform.LookAt() points the positive ‘Z’ (forward) side of an object at a position in world space. Quaternion.LookRotation() points the positive ‘Z’ side of an object in a specified direction. Quaternion.FromToRotation() creates a rotation that from one direction to another direction. Note the use of the positive ‘Z’ (forward) in the descriptions. The easiest way to make things work is to make sure that you are aligning the positive ‘Z’ of an object. So if at all possible you should change your setup. That can be done in the modeling program, or that can be done by using an empty game object. Create an empty object. Make the visible object a child at the same position (i.e. local position of (0,0,0). Then rotate the visible object 90 degrees so that the right (x) side of the object is aligned with the positive ‘z’ (forward) side of the empty game object.
The script goes on the empty game object.

To solve your problem, you need to start with a ProjectPointOnPlane() function:

function ProjectPointOnPlane(planeNormal : Vector3 , planePoint : Vector3 , point : Vector3 ) : Vector3 {
	planeNormal.Normalize();
	var distance = -Vector3.Dot(planeNormal.normalized, (point - planePoint));
	return point + planeNormal * distance;
}

Then you can do:

var q = Quaternion.FromToRotation(transform.up, hit.normal);
transform.rotation = q * transform.rotation;
var pos = ProjectPointOnPlane(transform.up, transform.position, target.position);
transform.LookAt(pos, transform.up);

If you want the current ‘X’ axis without remodeling or using an empty game object, the following additional line should work (untested):

transform.rotation = Quaternion.AngleAxis(90.0, transform.up) * transform.rotation;

You may need to negate the angle or the axis in this line of code.

Let me go through the code so you understand what is going on. The first line creates a rotation that goes from the current ‘up’ of the object to the normal of the surface (I’m assuming you got that normal from a Raycast hence the hit.normal in the line of code). The second line rotates the object by that amount aligning the ‘up’ of the object with the normal. The third line takes the target positition and projects it onto a plane passing through the object’s center and aligned with the up of the object. This projection is necessary so that the LookAt() will only rotate on the objects ‘y’ axis. The fourth line uses LookAt() to align the positive ‘z’ side of the object with the point that was created by projecting the target position onto a plane. The optional additional line spins the object on its ‘y’ axis 90 degrees so that instead of the ‘z’ side pointing at the target, the ‘x’ side should be pointing at the target. Pretty complicated for doing something that is so simple to describe.

To align something (building or RTS unit etc.) to the ground use:

ROTATION = Quaternion.LookRotation(
    Vector3.ProjectOnPlane( DIRECTION , SURFACENORMAL ) ,
    SURFACENORMAL
);

I found this post more or less by accident and it really helped me to solve the problem how to align an object with two different axis!

I had the same problem as the Author since my player is moving around a spherical world and I need to align Objects with the planet surface but still rotate towards the player.

Thank you very much! Since It’s a very old post and I needed to change the code a little to get this working I thought I’ll post it here. Maybe that helps another noob like me to get this working more quickly :wink:

Vector3 ProjectPointOnPlane(Vector3 planeNormal, Vector3 planePoint, Vector3 point)
{
    planeNormal.Normalize();
    var distance = -Vector3.Dot(planeNormal.normalized, (point - planePoint));
    return point + planeNormal * distance;
}

void rotateToPlayer()
{
    Vector3 playerPosition = player.transform.position;

    var q = Quaternion.FromToRotation(transform.up, playerPosition);
    transform.rotation = q * transform.rotation;
    var pos = ProjectPointOnPlane(transform.up, transform.position, player.position);
    transform.LookAt(pos, transform.up);
}