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


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.

one cheapo- way to achieve this sort of stuff has two parts which you would do every movement update (ie in Update()):

  1. move your player like normal. forget about the sphere.
    eg, if your player currently has velocity of (1, 2, 3), just move the player by (1, 2, 3).
  2. constrain the player’s position back to the surface, and re-calculate velocity.

let’s talk about step 2. in step 1, it’s likely that the player will move off the surface of the sphere. so step 2 is to put them back on the sphere.

the math looks something like this:

// unity-ish psuedo-code.
// this assumes that the player is:
// 1) originally on the surface of the sphere
// 2) moving tangentially to the surface of the sphere. (not jumping/falling)
// we'll use this value after constraining position to the surface.
void updatePlayerMotion(Player player, float dT, Sphere world) {
	// stash the original player position & velocity
	Vector3 originalPos = player.position;
	Vector3 originalVel = player.velocity;

	// ordinary linear motion.
	// maybe this is done w/ unity physics instead. up to you.
	player.position += player.velocity * dt;

	// vector from the center of the world to the player position.
	// if the world is centered at 0, 0, 0, then this is just player.position.
	Vector3 worldCenterToPlayer = player.position -;

	// scale that vector so that its the size of the sphere
	Vector3 constrainedPosition = worldCenterToPlayer.normalized * world.radius;

	// and offset it by the center of the world again.
	// if the world is at 0,0,0 this can be skipped.
	player.position = + constrainedPosition;

	// okay, now the player position is on the surface.
	// let's deal with conserving surface velocity.
	// the goal of this section of code is to create a new velocity vector which:
	// 1. has the same magnitude as the original velocity
	// 2. is tangent to the sphere
	// 3. is going in the same direction, more or less, as the original velocity.

	// create a vector which is perpendicular to both the vector from the center of the world to the player
	// and also to the original velocity:
	Vector3 perpAxis = Vector3.Cross(worldCenterToPlayer, originalVel);

	// create a vector which is perpendicular to both our 'axis' vector and the radius vector.
	// this vector is going in the direction we want, but is likely the wrong size.
	Vector3 tangent = Vector3.Cross(perpAxis, worldCenterToPlayer);

	// re-scale the tangent vector so it's the same magnitude as the original.
	player.Vector3 = tangent.normalized * originalVel.magnitude;

This question was waiting so long that I went to forums and it got solved there. Full code: