- Home /
Trouble with Camera Rotation Math
Hello everyone, I've got a setup where the player (with a "analog stick") can rotate a camera around an object spherically (actually, the camera is anchored to another GameObject, and the GO rotates and takes the camera with it). My problem is that while I can get the up/down or left/right rotation to work, when I try to use both at the same time or one after another, the rotation gets "wobbly" and becomes increasingly incorrect.
My Method:
My thought was that I would "just" create the rotation for desired left/right and for desired up/down, then add them together, then add that to the current rotation. I'm not really sure which part of this is flawed (if not the entire idea), but any guidance is appreciated.
public void RotateCamera(Vector2 horizVert)
{
//up and down:
vertQ = Quaternion.Euler ( transform.right * ( (maxRotationSpeed * horizVert.y) * Time.deltaTime) );
//verticalV = vertQ.eulerAngles;
//left and right:
horizQ = Quaternion.Euler ( Vector3.up * ( (maxRotationSpeed * horizVert.x) * Time.deltaTime) );
//horizontalV = horizQ.eulerAngles;
transform.rotation *= (vertQ * horizQ);
}
Any thoughts are appreciated, thank you for reading.
Pack your up/down and left/right into a Vector2 and normalise it.
Your suggestion is a bit over my head, if possible could you elaborate on: 1) How you pack a quaternion into a Vec2. 2) Once you've done 1 and normalized it, what do you do with it (make rotation *= Quat.Euler(Vec2) )?
Answer by Rector · Feb 18, 2015 at 09:47 PM
I built something similar (but kind of opposite) from the MouseOrbit script in the Unity standard assets.
Really the big clue there is that keeping a hard value for cumulative X and Y rotation during runtime (in my code below, that's _cameraXRotation
and _cameraYRotation
) and rebuilding your rotation from scratch each time should do what you want. Try this little guy. I assume horizVert
is just Input.GetAxis on your controller axes.
void ApplyCameraRotationController(Vector2 horizVert) {
_cameraXRotation = _cameraXRotation + horizVert.x * maxRotationSpeed * Time.deltaTime;
_cameraYRotation = _cameraYRotation + horizVert.y * maxRotationSpeed * Time.deltaTime;
transform.rotation = Quaternion.Euler(_cameraYRotation, _cameraXRotation, 0);
}
->What you are doing is similar to what I ended up doing just to get something that works. I ended up using (before seeing your answer) Transform.RotateAround(). Would you happen to know if either way of doing this is "better / more efficient" than the other?
public void RotateCamera(Vector2 horizVert)
{
transform.RotateAround(transform.position, Vector3.up, (maxRotationSpeed * horizVert.x) * Time.deltaTime);
transform.RotateAround(transform.position, transform.right, (maxRotationSpeed * horizVert.y) * Time.deltaTime);
}
Using two calls to RotateAround
rather than setting the rotation directly once would give me pause, but that borders on just personal preference. I imagine that the implementation of RotateAround
generally assumes that the position could change along with the rotation, which technically would result in a few more instructions, but I doubt that would amount to any noticeable performance issues using either in an *Update()
or whatever.
->I see, thanks for the info. It occurred to me before co$$anonymous$$g back here, that your implementation is superior (I think) because if I ever want to clamp my rotation, I could run into a Gimbal Lock problem since I would need to go from Quats->Euler->Quats. Your way allows for clamping at the float level and then just one conversion from Eulers->Quats.
Answer by Glurth · Feb 18, 2015 at 04:01 PM
I suspect the issue is that you are adjusting rotations around particular world axis (Quaternion.Euler). However, once you have rotated a bit on both axis, the WORLD axis is no longer what you want to rotate about: you want to rotate about the OBJECT'S current axis.
Take a look at using this function to modify your transforms rotation instead: http://docs.unity3d.com/ScriptReference/Transform.Rotate.html
Note that it has the option to rotate about a specified world space, or local space, axis. (default is local, which is what you want, I think)
EDIT: I think I'm going to delete this answer, i see now you ARE applying the rotation to the local object with *=
@Glurth: "i see now you ARE applying the rotation to the local object with *="
->Yeah, you and Rector (and myself) all seem to have come to the conclusion that just using some Transform.Rotate() method is just easier to work with...I just don't like it when I can't do something the way I would like to and cannot figure out why it doesn't work. Thanks for the effort though.