I currently have a camera which orbits a specific target object, always looking at it.
The user can drag the mouse to move the camera left/right/up/down and the camera will move over the surface of a sphere of fixed radius (note, I clamp the vertical angle to ± 89°).
Currently it looks something like this...
// Store position of camera for future reference
private Vector2 rotation = new Vector2(120, 25);
// Was intended to be the vector from planet to target. Only works with `up`
private rotationalPole = Vector3.up;
if (isDragging) {
rotation += new Vector2(
Input.GetAxis("Mouse X") * xSpeed * radius * 0.02f,
-Input.GetAxis("Mouse Y") * ySpeed * 0.02f);
}
//target is the GameObject to center on...
var pos = target.transform.position;
camObj.transform.position = pos + (Vector3.back * actualRadius);
camObj.transform.LookAt(pos, rotationalPole);
camObj.transform.RotateAround(pos, Vector3.right, rotation.y);
camObj.transform.RotateAround(pos, rotationalPole, rotation.x);
This works fine when up
is in the y
direction.
Now, however, instead of an object on the ground, the target is something in orbit around a planet (currently on an equatorial orbit but hopefully on an arbitrary one in future).
I want the camera to behave the same as before, but with up
being the vector from the center of the planet through the target. Nominally forward will be the direction of orbit (but since the user can spin through 360 "horizontally", that's less important).
Currently it looks like this...
Basically, I'd like to rotate the image above counter-clockwise by 90° (which I believe I can do by playing with the camera transform's up
and right
vectors) but also that my arbitrary rotation honors the new orientation.
Some research shows that I need to use quaternions but while my mental model for basic trig is fine, I can't picture how to use quaternions correctly in this situation.
Following on from @Jon's answer below...
I've now got the following.
var planetToTarget = (target.transform.position - planet.transform.position).normalized;
// "Reset" the camera before I do any transforms
this.transform.position = target.transform.position - tgtMovement * actualRadius;
this.transform.up = Vector3.up;
this.transform.LookAt(target.transform.position);
var camToTarget = (transform.position - target.transform.position).normalized;
Debug.Log(string.Format("targetMotion = {0}, planetToTarget: {1}, camToTarget: {2}", tgtMovement, planetToTarget, camToTarget));
// Line that's not working...
this.transform.Rotate(camToTarget, -90);
// To be added in when the above is working...
//this.transform.Rotate(Vector3.Cross(camToTarget, planetToTarget), rotation.y);
//this.transform.Rotate(planetToTarget, rotation.x);
//this.transform.up = planetToTarget;
The uncommented code (excluding the last line) keeps the camera behind the target, looking forward...
Initially, I get :
targetMotion = (0.0, 0.0, -1.0), planetToTarget: (-1.0, 0.0, 0.0), camToTarget: (0.0, 0.0, 1.0)
Which generates:
However, as the target moves around the planet, it changes to:
targetMotion = (0.4, 0.0, -0.9), planetToTarget: (-0.9, 0.0, -0.4), camToTarget: (-0.4, 0.0, 0.9)
By which point, the camera is slewing to the side...
So I'm clearly still missing something. I don't appear to be rotating around camToTarget
correctly?
Rotate()
takes a Vector3 and an Angle (Scroll to the last section of this page. The default "Space" (reference frame) is "Self"). So I did get it right (more by luck than judgement). I verified by creating an explicit Quaternion rotation and multiplying it by the camera transform and got identical (slewing) results. That said, thanks for the suggestion... Never rule out stupidity \$\endgroup\$Space.World
as your reference.camToTarget
is relative to the world, not toSelf
. \$\endgroup\$target.transform.position
work forRotateAround(point, ..., ...)
?planet.transform.position
should work too, because both would define the same line/ray. \$\endgroup\$