Facing an object on a sphere

I have some static game objects attached to a sphere along with moving objects also attached to the sphere. For the static objects, I need them to be able to turn and face the moving objects where ever they are on the sphere.

I made some progress using this:

Vector3 direction  = EnemyTargets[CurrentEnemy].transform.position - transform.position;
float desiredAngle = Mathf.Atan2(direction.z, direction.x);
//float desiredAngle = Mathf.Atan2(direction.y, direction.x);
				
transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles.x, transform.eulerAngles.y, desiredAngle * Mathf.Rad2Deg);

This code works somewhat and the objects rotate, but it does not work correctly and the rotation differs depending where the object is on the sphere. Can anyone help put me in the right direction? Is there a better way to accomplish this?

I’m not exactly sure what you are trying to achieve, but can’t you use transform.LookAt(target.transform) on the moving objects? Like I said, sorry if I’m misunderstanding your goal

When you say “face the moving objects”, do you mean to turn around the normal to the sphere at the static object’s position? i.e. as if the sphere is a tiny planet and the static objects are people who are trying to look at the dynamic objects as they move along the surface?

Sorry, yes, I do mean as if they were people on a planet and rotating relative to the sphere’s normal. Regarding transform.lookat, I also tried that and it also demonstrated incorrect behaviour where it would not rotate correctly and this behaviour would act differently depending where on the sphere it was placed.

are you placing each object in an emty gameObject? It is always better for rotation to make your object a child of an empty game object and them rotate that rather than your imported object

You might want to look at localRotation if they are childed of the same object.

I changed the code to use the local rotation and this still produces the same result, it will point at the object correctly on one side, but at the opposite side of the sphere, the rear end of the object is pointing at it. As the others have stated, yes, the object is the child of a parent object.

I’m still having problems with this… Can anyone help ?

Why don’t you use Quaternion.SetLookRotation or Transform.LookAt with the up vector set as the planet’s normal where your person is placed on the planet?

I did this so long ago… even posted in here in the forums.

There are a few methods you can use. The LookAt with the up vector is decent, it almost gets you what you want.

Your poblem is actually 2 fold. 1, how to get the person “on” the planet then 2, how to get them to go and move in the right direction.

  1. you can use 1 of 3 methods here.

You can either use a raycast from the where the person is or outside the planet to teh center of the planet to get the current position of where your feet should be. (this can be done using layers, layering the planet and all the objects on it. This is ideal for games like Mario World where you can have peaks and valleys. Using the person’s locations towards teh center will get you bridges and tunnels as well.

You can use a static distance. (Player.position - World.position).normalized * radius. This gets you the exact point on the sphere where things are supposed to be. No valleys, no hills.

Physics. Lock the rotation and handle that mathematically and let the physics engine handle gravity. Every FixedUpdate you should add force in the direction of the core. (World.position - Player.position).normalized * gravityAmount

  1. Headings are fairly simple to control. Everything is “local” Press A or D and rotate locally on Y axis. No muss. To set your “vertical” you will need to still use LookAt as suggested, but once you have the current position and heading, this is incredibly easy.

You need to be aware of how you are using collision control though. Physics do really good here. Mathematical positioning such as the first option above will require you to use CapsuleCast or something similar to figure collosion.

UpVector = (player.position - world.position); // this gives you a vector measuring where gravity should be coming from
PlayerRadius = UpVector.magnitude; // this gives you the current amount of distance from the center that the player is

LookAtPoint = (player.position + player.forward) - world.position; // very similar to UpVector, but measures 1 unit in front of the player
LookAtPoint = LookAtPoint.normalized * PlayerRadius; // this sets the look at point to the same distance from the center as the player

player.LookAt(LookAtPoint, UpVector); // this simply makes the player look at his forward and keeps that look point even with the world.

Now this code is not “exact” it simulates the look at point by using the world radius as a key. This is not normal. This would give very poor results on spheres less than like 20 units or so. It would give great results on large spheres as you can’t actually see the difference in the game.

To do the “correctly” I believe that you would need to use Vector3.Cross() to get the proper offset from the world center, then add the palyer’s radius to that in the player direction. Since I am not running Unity right now, I can’t test that. :wink:

Edit:
Oh yeah, Cross works perfectly.

Vector3 up = transform.position - world.position;
Vector3 LookAt = Vector3.Cross(up, -transform.right) + up;
transform.LookAt(LookAt, up);
1 Like

Fantastic! It is now working thanks to this, I can’t believe I didn’t think of this before. Thank you ever so much to everyone who replied.

1 Like