# Player on rotating sphere 3D

I’m trying to create this player which at collision will stay attached at the impact point on the surface of the sphere. regardless of the size of the sphere or player, this should happen smoothly.

• The player should always be attached from the bottom y-as
• y-as should always face outwards.

I tried many attempts but can’t

``````public class Player : MonoBehaviour
{
private Rigidbody playerRigidbody;
private Renderer objectRenderer;
private bool isAttached = false;

private void Awake()
{
playerRigidbody = GetComponent<Rigidbody>();
objectRenderer = GetComponent<Renderer>();
}

private void FixedUpdate()
{
if (isAttached)
{
Vector3 bottomPosition = transform.position - new Vector3(0, CalculateCenter(), 0);
transform.position = bottomPosition;
}
}

private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Sticky"))
{
// Get the point of impact and surface normal
ContactPoint contact = collision.contacts[0];
Vector3 pointOfImpact = contact.point;
Vector3 surfaceNormal = contact.normal;

// Invert the surface normal if necessary to make sure the y axis faces outwards
if (Vector3.Dot(transform.up, surfaceNormal) < 0)
{
surfaceNormal = -surfaceNormal;
}

// Align the player with the surface normal
transform.up = -surfaceNormal.normalized;

// Calculate how much distance there is between the center of the object and the bottom
float distanceToBottom = CalculateCenter();

// Calculate the position of the bottom of the player relative to the point of impact
Vector3 bottomPosition = pointOfImpact - surfaceNormal * distanceToBottom;

// Move the player to the point of impact
transform.position = bottomPosition;

// Attach the player to the collided object
transform.parent = collision.transform;

// Set the flag to indicate that the player is attached
isAttached = true;
}
}

private void OnCollisionExit(Collision collision)
{
if (collision.gameObject.CompareTag("Sticky"))
{
// Detach the player from the collided object
transform.parent = null;

// Set the flag to indicate that the player is not attached
isAttached = false;
}
}

// Calculate how much distance there is between the center of the object and the bottom
private float CalculateCenter()
{
// Get the bounds of the object
Bounds bounds = objectRenderer.bounds;

// Calculate the distance from the center of the object to its bottom
float distanceToBottom = bounds.extents.y;

return distanceToBottom;
}
}
``````

make it happen. player glitches or teleports or doesn’t stick.

Here’s one such example:

• a character, stuck with his feet to a globe, is trying to get to a target, using a shortest path, and looking forward while moving,
• target can be moved freely in 3D Euclidean space, but the character will attempt to reach its surface projection,
• the globe is assumed to lie in the world origin, and I believe it has a radius of 1.

(I’m ripping this out from a slightly bigger example, so tell me if it doesn’t work)

Place the script on some GameObject that has a model resembling a character (can be a cuboid).

``````using UnityEngine;

public class GlobeMotion : MonoBehaviour {

[SerializeField] [Min(0f)] float _walkingSpeed = 50f;  // in degrees/s
[SerializeField] [Min(0f)] float _facingSpeed = 360f;  // in degrees/s (instant if 0)
[Space]
[SerializeField] GameObject _objToFollow; // can be an empty game object, move this around in the scene
[SerializeField] GameObject _leadMarker; // drag here some graphics (a small cube/sphere) to show you the actual target

Quaternion _lastQ = Quaternion.identity;
Vector3 _lastUp = Vector3.up; // starting position
Quaternion _curQ = Quaternion.identity;

Vector3 _target = Vector3.up; // target position
Vector3 _tangentDir;          // player's tangential direction

void Update() {
_target = _objToFollow == null? Vector3.up : _target = _objToFollow.transform.position.normalized;

var nextQ = Quaternion.FromToRotation(Vector3.up, _target);
var nextUp = nextQ * Vector3.up;

var speed = _walkingSpeed;

//_curQ = Quaternion.RotateTowards(_lastQ, nextQ, _speed * Time.deltaTime); // one way of doing this
smoothlyRotateTowards(ref _curQ, _lastQ, nextQ, _arcAngle, speed * Time.deltaTime); // custom method

var oldPos = transform.localPosition;
var curPos = _curQ * Vector3.up;      // global coordinates (as in not-local and on-a-globe :)
var normal = curPos;                  // the coords coincide with the tangent plane normal (because sphere), so we can reuse this

// by projecting the difference between two positions onto the tangent plane, we get direction in tangent space
var td = (new Plane(normal, oldPos).ClosestPointOnPlane(curPos) - oldPos).normalized;

// if the new tangent direction is invalid (magnitude is not unit), that means we're ON the target
if(td.sqrMagnitude >= 1E-8f) _tangentDir = td; // we don't need square roots to test this

var globalUprightQ = Quaternion.FromToRotation(Vector3.up, normal); // world-space upright rotation will orient the guy properly against the ground
var invUprightQ = Quaternion.Inverse(globalUprightQ);

// finding the yaw is a matter of asking the right questions: what rotation will make me look at tangentDir while having the normal pointing up?
var localFwdQ = invUprightQ * Quaternion.LookRotation(_tangentDir, normal);  // ok, now cancel everything we knew already (subtract the quats)
// ^ this is essentially the same technique as rotating the guy back to      // the only thing that remains is the local Y axis rotation
//   XZ plane (thus we apply inverse of the global upright rotation)

if(_facingSpeed > 0f) { // do this only if facing is not instant (must be great enough speed, otherwise it'll work funny)
var actualFwdQ = invUprightQ * transform.localRotation;
localFwdQ = Quaternion.RotateTowards(actualFwdQ, localFwdQ, _facingSpeed * Time.deltaTime);
}

transform.localPosition = curPos;                     // world space position
transform.localRotation = globalUprightQ * localFwdQ; // get a compound rotation again (apply global to local)
// if we had a child object rotating on local Y we would apply these rotations separately
_lastQ = _curQ;
_lastUp = normal;
}

// very close to actual Unity's RotateToward
void smoothlyRotateTowards(ref Quaternion q, Quaternion from, Quaternion to, float totalAngle, float angleStep) {
if(isZero(angleStep, 1E-7f)) return;               // zero step can be safely ignored
if(isZero(totalAngle, 1E-7f)) { q = to; return; }  // division by zero must be sanitized
var nq = Quaternion.Slerp(from, to, Math.Min(1f, angleStep / totalAngle));
q = Quaternion.Slerp(q, nq, .66f);
}

static public bool isZero(float a, float eps) => Math.Abs(a) < eps;

}
``````

Hopefully it’s all there.

This script has nothing to do with what i want.

I reread this several times and I’m really not sure what else you want.

Ok. In that case you need to find a better way of explaining yourself.

This worked awesomely for me walking on a sphere:

I don’t wanna walk on a sphere. I will have a sphere in my game with difference sizes. I just want when the player collides it will attach it self so it will not fall off.

Dude, sphere size is completely trivial.
Any point on a sphere is defined by `size * vector.normalized`.
You can always change size to whatever, it’s the orientations that are PITA.