Camera Problem (When walking around a sphere)

Howdy to everyone.

This is my first post and I have “extensively” searched for the answer to my problem but either for lack of understanding or simply not knowing the mechanics of what I am trying to do I cannot find the answer.

I am a regular application developer (C#, C++, Java) who is trying his hand at building a casual game whereby you can roll a marble around a sphere (read: planet).

Now I have a fake gravity working at the moment and have basic movement around the sphere worked out (I am using a mouse orbit camera and have the movement controlled by applying torque to my marble (rigid body) in the direction my camera is facing. Now this works great for forwards and backwards but a I having a problem when trying to turn the camera (ie move the mouse left and right to change orientation).

The problem manifests itself as the camera tilting as opposed to turning by the time I am about a 1/4 to 1/2 the way around the planet. I think I just need to “level” the camera but for the life of me cannot figure out how to do it. I have tried snippets from the multiple Wow cameras on here and the planet camera’s etc but nothing seems to solve my issue.

To give a basic example see my attached pictures…
Image1: is how I want it to be I can move the mouse left or right and the orientation of the camera turns/pans left or right and I can move forward in that direction. (It is like this at the top starting point of the planet (sphere)

Image2: is when I am over a quarter of the way around the planet or more when I move the mouse left or right the camera tilts as opposed to turning/panning left or right.

I am sure I am just missing something silly but I have read soooo many posts now that I think I am just going in circles and not really getting anywhere (I’ve noticed that no matter how many times you try the same code that doesn’t work, it doesn’t get any closer to working)

Thanks in advance… and Unity… WOW loving it… :smile:


431312--14953--$screenshot2.png

Can you post your camera control code? (In absence of that, my only guess is that you’re using Quaternion.LookRotation() without specifying the reference ‘up’ vector.)

Hi Jesse

Thanks for replying. The camera control script is just the mouse orbit script that is bundled with Unity. I have placed the source below.

i think your suggestion about the up vector might be right and it is getting confused because up is constantly changing due to the fact that we are walking around a sphere. (Up and down are not in the direction unity is expecting… I’m a noob so I am just guessing)

As a “think outside the box” solution I tried rotating the planet under the marble (instead of actually moving the marble itself) and it worked quite well except I have other planets(spheres) in the scene and they never came into view (whereas when moving the marble around the planet you see the other planets come into view as you go round).

I am thinking that I need to make the “up” direction for the camera the surface normal of the planet that is directly under the camera but I have NO idea how to achieve this (3D math is making my brain numb).

var target : Transform;
var distance = 10.0;

var xSpeed = 250.0;
var ySpeed = 120.0;

var yMinLimit = -20;
var yMaxLimit = 80;

private var x = 0.0;
private var y = 0.0;

@script AddComponentMenu("Camera-Control/Mouse Orbit")

function Start () {
    var angles = transform.eulerAngles;
    x = angles.y;
    y = angles.x;

	// Make the rigid body not change rotation
   	if (rigidbody)
		rigidbody.freezeRotation = true;
}

function LateUpdate () {
    if (target) {
        x += Input.GetAxis("Mouse X") * xSpeed * 0.02;
        y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02;
 		
 		y = ClampAngle(y, yMinLimit, yMaxLimit);
 		       
        var rotation = Quaternion.Euler(y, x, 0);
        var position = rotation * Vector3(0.0, 0.0, -distance) + target.position;
        
        transform.rotation = rotation;		
        transform.position = position;
		
    }
}

static function ClampAngle (angle : float, min : float, max : float) {
	if (angle < -360)
		angle += 360;
	if (angle > 360)
		angle -= 360;
	return Mathf.Clamp (angle, min, max);
}

If the surface of the planet is a smooth sphere then getting the surface normal is quite straightforward. If you subtract the planet’s transform.position from the character’s, then the resulting vector will be normal to the surface and suitable to pass to Quaternion.LookRotation. If the surface is bumpy then you might need to do a raycast down from the character to get the normal (which is returned in a RaycastHit object):-

var hit: RaycastHIt;

if (Physics.Raycast(transform.position, -transform.up, hit)) {
  // Surface normal is now in the hit.normal variable.
}

Can anyone elaborate a bit more on how the above code is implemented with the camera script? I’m not sure how to get this to work as my character camera is still rotating incorrectly around a planet.

You’re setting the camera angle to be based purely on the mouse movement, which works fine if your target object isn’t changing orientation itself. But since it is, then your camera is going to be pointing the wrong way once you leave the “top” of the planet. I think you want to basically:

  1. Get the normal.
  2. Orient the camera to that normal.
  3. Apply x/y rotation based on that orientation, instead of from 0,0,0

Thanks for the reply. I understand the concept of all of this but I am unsure how to write the code so the camera rotation plane is based off of my character normal, or the surface normal. Sorry if the solution is simple as I am still learning the scripting for normals, vectors etc…

I had another unanswered post long ago about this same issue.

http://answers.unity3d.com/questions/398868/character-camera-movementshootergamecamera-on-plan.html

Okay this is my stab at it…No idea if this is right as I cant test while I am at work, but i’ve done a bit more reading and this is what i’ve come up with. Still just trying to have the camera properly rotate around the correct x/y plane according to the constantly changing surface normal. Does this seem correct? Or am I way off base?

	   if (Physics.Raycast(transform.position, -transform.up, hit)){
        	Quaternion rot = Quaternion.FromToRotation(transform.up, hit.normal);
        	transform.rotation = rot * Quaternion.Euler(x, y, 0);
	   }

I couldn’t get the above code to work. I tried a new approach under LateUpdate and this is now what i’ve come up with and it partially works. The camera rotates properly along the x-axis according to the surface normal anywhere on the planet, SOME of the time, while the y axis has a weird rotation going on. For the most part the camera is just snapping around everywhere though.

function LateUpdate () {
    
        Debug.DrawRay (target.position, -target.up * 200, Color.red);
    
        if (target) {
            x += Input.GetAxis("Mouse X") * xSpeed * 0.02;
            y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02;
           
            y = ClampAngle(y, yMinLimit, yMaxLimit);
                   
            var rotation = Quaternion.Euler(y, x, 0);
            var position = rotation * Vector3(0.0, 0.0, -distance) + target.position;
           
            transform.rotation = rotation;     
            transform.position = position;
            
            if (Physics.Raycast(transform.position, -transform.up, hit)){
			transform.rotation = Quaternion.FromToRotation (transform.up, hit.normal) * transform.rotation;
			}
   	        }
        }

Any ideas on how to fix this?

Hmm. still going hard on trying to figure this out but nothing… Any help would greatly be appreciated.