Transform.LookAt() causes spinning

The code is simple. On a quad (named character), I have this code:

	// Update is called once per frame
	void Update () {

		//look away from camera so that th token picture is always 'flat' towards the camera
		transform.LookAt (transform.position - mCamera.position, transform.up);
	}

And on a camera (mCamera) I have this code:

if (Input.GetMouseButton (2)){
	//		Debug.Log ("Middle Clicked.");


			//mouse moving also?
			if (Input.GetAxis ("Mouse X") > 0 ){
				if (Input.GetKey(KeyCode.LeftControl)){
					transform.RotateAround (character.position, -transform.up, rotateSpeed * Time.deltaTime);
				}
			}
			else if (Input.GetAxis ("Mouse X") <0){
				if (Input.GetKey (KeyCode.LeftControl)){
					transform.RotateAround (character.position, transform.up, rotateSpeed * Time.deltaTime);
				}
			}/**/


			if (Input.GetAxis ("Mouse Y") < 0){
				if (Input.GetKey (KeyCode.LeftAlt)){
					transform.RotateAround (character.position, transform.right, rotateSpeed * Time.deltaTime);
				}
			}
			else if (Input.GetAxis ("Mouse Y") > 0){
				if (Input.GetKey (KeyCode.LeftAlt)){
					transform.RotateAround (character.position, -transform.right, rotateSpeed * Time.deltaTime);
				}
			}/**/
		}

The idea is that the quad (character) is always centered in the camera (mCamera) as the camera rotates 3d. The quad always faces “away” from the camera so that it looks flat within the 3d environment.

The problem is that when I move the camera Y down so that the camera looks at the horizon, then rotate the X left or right the quad starts to spin very slowly. If I take out the “transform.up” from LookAt() the spinning is much faster.

How do I stop the spinning completely?

You have several problems here. The most important one is that “LookAt”, as the name suggest, wants “something” to look at. However you passed a relative direction vector instead. So the point you’re looking at is just around the world origin since your vector is taken as world space position.

If you want to use a direction vector you have to use Quaternion.LookRotation(fwd, up);. It does basically the same thing but takes two direction vectors instead of a worldspace position and a direction.

transform.rotation = Quaternion.LookRotation(transform.position - mCamera.position, transform.up);

Next thing is Input.GetAxis returns the amout the mouse has moved that frame. You usually want to use that value as the amount you want to rotate this frame. So your code can even be simplified to:

if (Input.GetMouseButton (2))
{
    if (Input.GetKey(KeyCode.LeftControl))
    {
        transform.RotateAround (character.position, transform.up, -Input.GetAxis ("Mouse X") * rotateSpeed);
    }
    if (Input.GetKey(KeyCode.LeftAlt))
    {
        transform.RotateAround (character.position, transform.right, -Input.GetAxis ("Mouse Y") * rotateSpeed);
    }
}

Though RotateAround is a bad choice for such a control as it applies a relative cirular motion to the camera object so over time it might “drift” due to floating point precision.

Since you rotate your plane object exaclt in the same direction the camera is facing, wouldn’t it be much easier to just rotate the object directly and have the camera as child object behind the object? That way the camera always views the object from the same angle.

Most independent camera orbiting scripts usually use absolute rotation values (either twi eulerangles or a quaternion) and place the camera based on the rotation at a specified distance behind the object.

public Transform target;
public float distance = 5f;

void Update()
{
    if (Input.GetMouseButton (2))
    {
        if (Input.GetKey(KeyCode.LeftControl))
        {
            transsform.rotation = transsform.rotation * Quaternion.AngleAxis(-Input.GetAxis ("Mouse X") * rotateSpeed, transform.up);
        }
        if (Input.GetKey(KeyCode.LeftAlt))
        {
            transsform.rotation = transsform.rotation * Quaternion.AngleAxis(-Input.GetAxis ("Mouse Y") * rotateSpeed, transform.right);
        }
        transform.position = target.position -transform.forward*distance;
    }
}

If you don’t want to specify the distance in the inspector but instead use the initial distance you can add this in Start:

void Start()
{
    distance = Vector3.Distance(transform.position, target.position);
}