# How to move on the surface of a sphere using spherical math? (Without Rigidbodies or raycasts)

Discussion in 'Scripting' started by aidinz, May 8, 2017.

1. ### aidinz

Joined:
Apr 17, 2016
Posts:
63
Hi,

I want to be able to walk inside a imaginary sphere and facing the center of it. I know I can create the sphere, cast RayCast and inverse normal of the collision point or use Rigidbodies but I want to do it with math but I can't get my head around the math, specially the moving part.

To put it in another word, imagine super mario galaxy but inverse, being inside the sphere while still walking on the surface, like mario.

I've been searching for this for hours but can't find what I want. Thanks.

2. ### hpjohn

Joined:
Aug 14, 2012
Posts:
2,190
Your up direction is always = the surface normal = (sphere center pos - your position)

3. ### aidinz

Joined:
Apr 17, 2016
Posts:
63
Perhaps I wasn't clear enough, I don't want to have an sphere at all. That's why I said "imaginary" in the first sentence.

4. ### laxbrookes

Joined:
Jan 9, 2015
Posts:
235
With this math, you won't need an actual "physical" sphere. The math is still the same.

lordofduct likes this.

Joined:
Apr 17, 2016
Posts:
63

6. ### lordofduct

Joined:
Oct 3, 2011
Posts:
8,405
Your surface is spherical, you're saying.

This means that you can define your surface as the inner side of a sphere.

You don't need a 'Sphere' GameObject... but there's still a mathematical concept of a sphere that could be used to represent the surface.

That sphere is defined as a center point, with a radius.

When you're at a given point... your distance from the center is the radius... if you take a step forward, take that new point relative to the center and just ensure it's magnitude is equal to the radius.

Code (csharp):
1.
2. var pos = transform.position; //get position
3. pos += direction * speed * Time.deltaTime; //move forward
4. var v = pos - center; //get new position relative to center of sphere
5. pos = center + v.normalized * radius; //constrain position to surface of sphere
6. transform.position = pos; //set position
7.

laxbrookes likes this.
7. ### steego

Joined:
Jul 15, 2010
Posts:
968
What you want is probably a spherical coordinate system. In your movement code, just update your azimuth and elevation values, radius stays constant. Then convert back to cartesian coordinates, and assign those to your gameobjects.

camilo_amihan likes this.
8. ### aidinz

Joined:
Apr 17, 2016
Posts:
63

I'm posting the conversion from Cartesian to/from Polar (Spherical) here:

Code (CSharp):
1. public static class SphericalMathHelpers
2. {
3.
4.     public static Vector3 SphericalToCartesian (Vector3 sphericalCoord)
5.     {
6.         return SphericalToCartesian(sphericalCoord.x, sphericalCoord.y, sphericalCoord.z);
7.     }
8.
9.     public static Vector3 SphericalToCartesian (float radius, float azimuth, float elevation)
10.     {
11.
12.         float a = radius * Mathf.Cos (elevation);
13.
14.         Vector3 result = new Vector3();
15.         result.x = a * Mathf.Cos(azimuth);
16.         result.y = radius * Mathf.Sin(elevation);
17.         result.z = a * Mathf.Sin(azimuth);
18.
19.         return result;
20.     }
21.
22.     /// <summary>
23.     /// Return Vector3 is x = radius, y = polar, z = elevation.
24.     /// </summary>
25.     /// <param name="cartCoords"></param>
26.     /// <returns></returns>
27.     public static Vector3 CartesianToSpherical (Vector3 cartCoords)
28.     {
30.
31.         if (cartCoords.x == 0)
32.             cartCoords.x = Mathf.Epsilon;
33.
34.         _radius = Mathf.Sqrt((cartCoords.x * cartCoords.x)
35.         + (cartCoords.y * cartCoords.y)
36.         + (cartCoords.z * cartCoords.z));
37.
38.         _azimuth = Mathf.Atan(cartCoords.z / cartCoords.x);
39.
40.         if (cartCoords.x < 0)
41.             _azimuth += Mathf.PI;
42.         _elevation = Mathf.Asin(cartCoords.y / _radius);
43.
44.         Vector3 result = new Vector3(_radius, _azimuth, _elevation);
45.         return result;
46.     }
47. }
and how I'm using it:

Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;
3. using UnityEngine;
4.
5. public class PlayerController : MonoBehaviour
6. {
7.     public Transform Target;
8.     public float speed = 50;
9.     public float radius = 10;
10.
11.
12.     #region UnityCallbacks
13.
14.     void Start ()
15.     {
16.
17.     }
18.
19.     void Update ()
20.     {
21.         if (Input.GetKey(KeyCode.W))
22.         {
23.             Vector3 mySphericalCoord = SphericalMathHelpers.CartesianToSpherical( transform.position );
24.             mySphericalCoord.z += speed * Time.deltaTime;
25.             transform.position = SphericalMathHelpers.SphericalToCartesian(mySphericalCoord);
26.         }
27.
28.         if (Input.GetKey(KeyCode.S))
29.         {
30.             Vector3 mySphericalCoord = SphericalMathHelpers.CartesianToSpherical(transform.position);
31.             mySphericalCoord.z -= speed * Time.deltaTime;
32.             transform.position = SphericalMathHelpers.SphericalToCartesian(mySphericalCoord);
33.         }
34.
35.         if (Input.GetKey(KeyCode.A))
36.         {
37.             Vector3 mySphericalCoord = SphericalMathHelpers.CartesianToSpherical(transform.position);
38.             mySphericalCoord.y -= speed * Time.deltaTime;
39.             transform.position = SphericalMathHelpers.SphericalToCartesian(mySphericalCoord);
40.         }
41.
42.         if (Input.GetKey(KeyCode.D))
43.         {
44.             Vector3 mySphericalCoord = SphericalMathHelpers.CartesianToSpherical(transform.position);
45.             mySphericalCoord.y += speed * Time.deltaTime;
46.             transform.position = SphericalMathHelpers.SphericalToCartesian(mySphericalCoord);
47.         }
48.
49.         transform.LookAt(Target);
50.     }
51.
52.     #endregion
53. }
54.
I'm wondering if there is a shorter way without these conversions.

Also is it possible not to "new" vector3's?

9. ### aidinz

Joined:
Apr 17, 2016
Posts:
63
In my calculations I'm rotating around the (0, 0, 0), how can I do it over the target?

Last edited: May 9, 2017
10. ### steego

Joined:
Jul 15, 2010
Posts:
968
I would just cache the azimuth and elevation in your PlayerController, instead of converting the position to spherical every time.

Just add an offset to the transform.position when you set it.

Or you can create two empty gameobjects, one as the child of the other, have your PlayerController on the child, and then use transform.localPosition instead. You can then move the child anywhere in your scene.

11. ### aidinz

Joined:
Apr 17, 2016
Posts:
63
Great suggestion, did it.

I changed the Start() to this:
Code (csharp):
1.
2. cachedSphericalCoord = SphericalMathHelpers.CartesianToSpherical(Target.position + transform.position);
3.
But doesn't seem to work.

12. ### steego

Joined:
Jul 15, 2010
Posts:
968
I mean when you update the position, do something like this

Code (csharp):
1. transform.position = offset + SphericalMathHelpers.SphericalToCartesian(mySphericalCoord);

13. ### aidinz

Joined:
Apr 17, 2016
Posts:
63
Thanks.
I can't get this work nor the child/parent way you described previously.

14. ### steego

Joined:
Jul 15, 2010
Posts:
968
I attached an example showing both ways, hope this helps.

File size:
7.3 KB
Views:
1,186