Camera rotation using mouse

I have tried to implement a basic camera movement around a third person character which is controlled by the mouse, however I get strange behavior and it’s been a while since I did this maths.

I have a camera it is not a child of the character the camera is supposed to follow the character but rotate relative to the scene. Therefore rotating the character should not affect the camera and vice-versa.

Here is what I have got so far (gameObject is the camera):

public void Update() {
	float yawAmount = Input.GetAxis ("Mouse X");
	float pitchAmount = Input.GetAxis ("Mouse Y");
	RotateCamera(yawAmount, -pitchAmount);

	// position the camera
	gameObject.transform.position = (camLocalPos + curTarget);

	// look at target
	gameObject.transform.LookAt (curTarget);
}

public void RotateCamera(float x, float y) {
	yaw += x*0.1f;
	pitch += y*0.1f;
	if (pitch > maxPitch)
		pitch = maxPitch;
	if (pitch < minPitch)
		pitch = minPitch;
	UpdateCamera ();
}

private void UpdateCamera() {
	camLocalPos.x = distance * Mathf.Cos(pitch) * Mathf.Sin(yaw);
	camLocalPos.y = distance * Mathf.Sin(pitch) * Mathf.Sin(yaw);
	camLocalPos.z = distance * Mathf.Cos(yaw);
}

It mostly works, but depending on which way the camera is facing it flips the pitch such that looking one way up is up and looking the other way up is down :confused: (and everything in between).

Can anyone point out the error please?

Update

I realised that I had the pitch and yaw the wrong way round it should be:

	camLocalPos.x = distance * Mathf.Cos(yaw) * Mathf.Sin(pitch);
	camLocalPos.y = distance * Mathf.Sin(yaw) * Mathf.Sin(pitch);
	camLocalPos.z = distance * Mathf.Cos(pitch);

but changing this makes it worse! I’m 99% sure that the error is in the above three lines but I have been over and over it and cannot see anything wrong:

alt text

alt text

alt text

alt text

Update 2

Are the axes in the correct orientation?

Okay, I think I see the problem here.

To answer your second update, no, the axes DO NOT appear to be in the correct orientation. From what you’ve described, your resultant view is primarily top-down in world space, rather than perpendicular to the world up/down on the Y-axis.

Why does this matter? LookAt() is based around the Unity’s world coordinate system, in which X runs left to right, Y runs bottom to top, and Z runs back to front. By default, Lookat() assumes that the camera should look at its target where “up” corresponds to the global “up” direction.

The problem you’re encountering based on your current script is known as Gimbal Lock. In short, it occurs as a result of Euler angles (0-360 degrees on X, Y, and Z axes) having multiple ways of describing the same rotation. You can either rotate an object 180 degrees on the Y axis, or you can get an identical result by rotating both the X and Z axes the same way. Unity doesn’t automatically know what you’re trying to accomplish, so it simply guesses.

Well, this same principle applies when you cross over any axis boundary. To support as much sensible, logical circumstance as possible, the Y-axis is left as the only ambiguous line to cross. As a result, looking any further than straight up or straight down will not cope with Lookat() in an expected manner in most circumstances. This is primarily a problem for you because you’re misappropriating the axes and basically have turned the world on its side, resulting in an unintentionally easy crossing over the Y-axis which, again, is mainly only a problem for LookAt() in this situation.

To summarize, Unity’s axis alignment is one in which X and Y are a 2-dimensional left/right/down/up. The Z axis adds a third dimension from back to front, giving Unity a Left-Handed Coordinate System.