C# 360 Orbital Camera Controller (Gimbal Lock Issue)
I have a stationary cube in my scene that I'm orbiting a camera around. I have my MainCamera nested under a GameObject that I'm calling 'OrbitalCamera'.
I setup the script so that a click (or tap) and drag will rotate the camera around the object in space so it feels like you're rotating the cube (ie: if I click the top of a face on the cube, and pull down, I'm rotating the X value) but you'll actually be rotating the camera.
For the most part, my script works. However, after rotating the Y so much, the camera is upside down and the X gets inverted. Here's my (updated) script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OrbitalCamera : MonoBehaviour {
public bool cameraEnabled;
[SerializeField] private float touchSensitivity = 5f;
[SerializeField] private float orbitDampening = 10f;
protected Transform xFormCamera;
protected Transform xFormParent;
protected Vector3 localRotation;
protected float cameraDistance;
void Start () {
cameraEnabled = true;
xFormCamera = transform;
xFormParent = transform.parent;
cameraDistance = transform.position.z * -1;
}
void LateUpdate () {
if (cameraEnabled) {
if (Input.GetMouseButton(0)) {
if (Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0) {
localRotation.x += Input.GetAxis("Mouse X") * touchSensitivity;
localRotation.y -= Input.GetAxis("Mouse Y") * touchSensitivity;
}
}
}
// TODO:: FIX CAMERA ROTATION / GIMBAL LOCK ISSUE
Quaternion yaw = Quaternion.AngleAxis(localRotation.x, transform.up);
Quaternion pitch = Quaternion.AngleAxis(localRotation.y, transform.right);
//Quaternion qt = Quaternion.Euler(localRotation.y, localRotation.x, 0);
// NOTE:: ROTATING PITCH AND YAW SEPARATELY WORK FLAWLESSLY, BUT ROTATION SHOULD HAPPEN TOGETHER
//xFormParent.rotation = Quaternion.Slerp(xFormParent.rotation, pitch, Time.deltaTime * orbitDampening);
//xFormParent.rotation = Quaternion.Slerp(xFormParent.rotation, yaw, Time.deltaTime * orbitDampening);
// NOTE:: MULTIPLYING PITCH AND YAW STILL PRODUCES GIMBAL LOCK AND CAMERA SPINS INFINITELY AT CERTAIN ANGLES
xFormParent.rotation = Quaternion.Slerp(xFormParent.rotation, pitch * yaw, Time.deltaTime * orbitDampening);
// NOTE:: USING QUATERNION EULERS GENERATES A GIMBAL LOCK AND CAMERA DOES NOT SPIN
//xFormParent.rotation = Quaternion.Lerp(xFormParent.rotation, qt, Time.deltaTime * orbitDampening);
}
}
Is there a good method to achieve this type of 360 camera? I'd like dragging from right to left to always move the camera left and dragging left to right to always move the camera right -- no matter how the camera is oriented.
I still have not fully figured this out, but I definitely understand the problem better. I've found tons of solutions on forums that suggest clamping the pitch so that the camera can't pass the object on the top or bottom and 'flip' itself. I still would like help on this issue. Thank you.
This is why I wrote I always run into issues: at first it seems logical that combining the 2 rotations should work, but then something happens, and it breaks. $$anonymous$$aybe if you apply them one after the other, and use the updated transform.up and transform.right vectors...
Another possible solution is that you parent your camera to a rotating object, set a displacement (for example (0, 0, -5)), and then just rotate the parent object, without fear of gimbal lock?
I think I solved this the last time this way, parenting my camera. This also has the advantage that zoo$$anonymous$$g/getting closer to the target is almost done!
Thanks for being the only person to reply on this issue in general. I currently have an empty object called 'OrbitalCamera' and the '$$anonymous$$ainCamera' as the child, with the script on it. I'm manipulating the 'OrbitalCamera' rotation with this script via 'xFormParent'.
Additionally, I've tried modifying one after the other as that makes a lot of sense, but I'm still running into the same issue.
Answer by Harinezumi · Feb 06, 2018 at 10:32 AM
If you want completely free rotation you need to eliminate Quaternion.Euler()
and related functions from your code (because Euler angles can't properly represent all rotations without gimbal lock). Instead you could use Quaternion.AngleAxis()
for the separate axes.
Also, Quaternion.Slerp()
is a lot more useful than Lerp()
, because it correctly "wraps around".
Thanks for the advice - I'll give this a shot after work. If you think this conversion will be simple with my current code, post a snippet and I'll 'accept' this answer. Otherwise, I think I have some messing-around to do.
No, sorry, I didn't post a snippet, because I always run into issues when doing rotations, even when it seems something simple. The solution will be something similar to:
Quaternion yaw = Quaternion.AngleAxis(localRotation.x, transform.up);
Quaternion pitch = Quaternion.AngleAxis(localRotation.y, transform.right);
xFormParent.rotation = yaw * pitch;
I hope this helps!
This is doing some freaky stuff. The initial rotation works, but if rotated too far, the camera starts orbiting like crazy. It looks like the yaw and pitch both work perfectly separately... but not together for some reason. I updated my script and will continue to test.
Your answer
Follow this Question
Related Questions
FPS Character not moving in relation to direction/rotation 0 Answers
Text that always looks at you 1 Answer
How to rotate camera diagonally over players shoulder while still facing towards players direction 0 Answers
3d camera not working as I want it to 0 Answers
Make continuous camera rotation whilst holding the RMB 0 Answers