Planar movement on spherical surface

I’m not sure how to explain this, but imagine you have a camera looking at an object in the distance. It’s a perfect “top down” view from the camera and it’s using a LookAt during the update. Now I want the object to move in two directions, as if it was a 2D game - BUT - I want the distance to the camera to always be the same and I always want the top of the object facing the camera.

Best way to imagine this is if all the objects were moving around on the inside surface of a large sphere.

I’m currently moving the object forward by applying AddRelativeForce, and I can turn the object left and right using AddTorque. The camera looks at the object using LookAt(target) but obviously the object disappears in the distance instead. I should also add that I want to fire weapons and bullets and projectiles should also have this behavior.

Does anyone know how to achieve this effect? I would be most thankful for any assistance.

So, basically, you want to look top down on a guy, who is walking on a small planet, and you want the distance from the camera to the person the same.

The term here is up Vector.

There are two up vectors you have to worry about, one is the up vector of the planet, the second is the up vector, which is actually the forward vector of the person.

Up vector from the planet is (person.position - planet.position).normalized. This is the direction which is up for the person to work at all.

Next, we need to fit the person vertically, to do this we will have to reach into a mathematical bag of tricks. First we need the distance from the planet’s core to the person (person.position - planet.position).magnitude. Then we get the normalized direction of the current velocity, normalized. ((person.position + rigidbody.velocity.normalized) - planet.position).normalized and multiply that by the distance we got before. What this does is gives us the position on the planet where the person is actually facing. Now, we simply look at that spot, and use the up direction we had before. (person.LookAt(lookAtVector * personDistance, upVector)).

Next onto the camera. Lets say the camera is always 10 units “above” the person. We have the position of the person, the distance he is from the center of the planet and the upvector. All we need to do is add 10 to the distance, and use an up vector from the person’s forward position. (cam.position=planet.position + upVector * (personDistance + 10)). And the look at: (cam.LookAt(planet.position, person.forward))

var planet=GameObject.Find("Planet").transform;
var cam=Camera.main.trasnform;
var person=transform;

var upVector=(person.position - planet.position).normalized;
var personDistance=(person.position - planet.position).magnitude;
var lookAtVector=((person.position + rigidbody.velocity.normalized) - planet.position).normalized;

person.LookAt(lookAtVector * personDistance, upVector);
cam.position=planet.position + upVector * (personDistance + 10);
cam.LookAt(planet.position, person.forward));

Do test this stuff… Sadly, its understanding with no testing. lol

Hi BigMisterB - thank you very much for taking the time to answer. I will try to use the different tricks to adapt it to my needs, but I think you misinterpreted my question (but that’s OK, I’m finding it difficult to explain =)

  • The camera is actually always stationary at position 0,0,0 and it just looks around
  • Imagine you are holding a kite flying in the wind and your eyes are the camera
  • You fly the kite and it can never go beyond the length of a string (which is a fixed length)
  • The kite always has the bottom facing you perpendicular

This means that it is the direction of travel and rotation of the kite that will change. If the kite could fire a missile it should also have an imaginary string back to me so it will travel in the same fashion as the kite.

Does this make any more sense or should I try with yet another explanation? =)

I’ll keep working at it myself as well… The way I see it I probably need to rotate my object every update with the difference between its own angle and the angle that the camera is looking at to ensure the top of the object is always perpendicular to the camera. I’m not sure if this will break my forces which is propelling the object and I’m not sure how to do the angle difference adjustments yet - but if I make any progress I’ll be sure to post here right away.

Oddly, it is the same math, except that you don’t set the camera’s position. Now, up and whatever for the camera may be different in your case.

The math is based off of distance, you can always clamp the distance to your maximum length.

Been trying to get it to work for a while now but it won’t be possible to get it the way I’m imagining.

Have a look at this (use arrow keys) :
http://www.imphenzia.com/tmp/space/

As you can see the ship just flies away now.

What I wanted to achieve was to have the view always looking directly down on the ship, yet utilizing the skybox / spacebox to create a nice roomy universe for the ships to battle in. I realize that it’s not natural that they would fly around in that way, but it would sort of create a seamless (spherical) arena which appealed to me.

Using LookAt function for both the camera and the target object seems to be a catch 22 issue as the ship just flickers around sometimes. I would also have to apply similar physics to all missiles and projectiles so I’m thinking I may not be able to achieve this effect.

Any thoughts on that?

Yeah, same math, just a little diffrently used.

I set up an entire controller… it will make you dizzy in a heartbeat though.

var shipPower=10.0;
var worldRadius=50.0;
var maxSpeed=150.0;
private var speed=0.0;

function Start(){
	if(!rigidbody)
		gameObject.AddComponent(Rigidbody);
	rigidbody.useGravity=false;
	transform.position=Vector3(0,-worldRadius,0);
	transform.eulerAngles=Vector3.zero;
}


function FixedUpdate () {
	var rotate=Input.GetAxis("Horizontal") * 10.0;
	transform.Rotate(0,rotate,0);
	speed += Input.GetAxis("Vertical");
	speed=Mathf.Clamp(speed,-maxSpeed * 0.5, maxSpeed);
	rigidbody.velocity=transform.forward * speed;
	rigidbody.AddForce(transform.forward * speed * shipPower);
	
	
	var planet=Vector3.zero;
	var cam=Camera.main.transform;
	
	var upVector=transform.position.normalized;
	transform.position=upVector * worldRadius;
	if(rigidbody.velocity.magnitude > 0.0){
		var lookAtVector=(transform.position + transform.forward).normalized;
		transform.LookAt(lookAtVector * worldRadius, upVector);
	}
	//cam.position=planet + upVector * (personDistance + 10);
	cam.position=planet;
	cam.LookAt(transform, transform.forward);
}

BigMisterB - thank you once again. You may be right, maybe it makes you too dizzy, not good for someone like me who gets seasick playing some FPS games =)

I keep getting mixed up in the world of vectors and translating between different objects - very frustrating. When I try your code it works quite well apart from my ship pointing it’s nose at me instead of the roof :slight_smile: Tried to rotate it and also multiply the LookAt angle but with little effect.

Apart from the dizziness factor, would you say this is a bad idea? One way would be to lock the rotation of the camera around the “spinning” axis so it just follows the ship looking up, down, left, right.

Thank you once again - I spend every minute trying to work this out and you seem to be able to just slap down some code… impressive.

If your ship is pointing towards you, that means that the model is not pointing forward in your 3d modeling software. You can adjust it by simply tacking it onto an empty game object and letting the empty game object do all the movement. Just orientate it correctly.

Were you able to get this script to compile? I keep getting compile errors, doesn’t like the “vars” and “function”.