- Home /
Constrain rigid body movement to a sphere
I want to have a number of rigid objects moving around a sphere at a fixed distance like this:
The objects should be able to move freely around the sphere, and interact, collide, etc, but with the distance to the sphere restricted.
How would you do this? Using gravity in some way seems to be the most natural way, but I don't know how. The objects needs to float in the air at orbit distance, or at least look like they do. The objects have different size and mass.
Edit: I edited the question since I used the word "orbit" which is not what I want. I want free movement restricted to a sphere, but using physics to handle collisions, rotations, etc.
Answer by aldonaletto · Jan 12, 2012 at 12:27 PM
Make the sphere a rigidbody, turn Use Gravity off and freeze everything in Constraints. Add HingeJoints (menu Components/Physics/Hinge Joint) to each orbiting object, and set their Connected Rigidbody field to the central sphere. Set the Hinge Joint Axis to 0,0,0 if you don't want any particular axis, or set it to the axis you want (0,1,0 for Y axis, for instance). Apply forces or set the velocities of the orbiting objects and they will orbit forever.
EDITED: I found a better alternative: the configurable joint. Add a configurable joint and the script below to all "orbiting" objects and child them to a center object (the sphere or an empty object). You can also define an initial 2D velocity startvel that's "mapped" over the sphere.
var startVel: Vector2;
function Start(){ var joint = GetComponent(ConfigurableJoint); joint.xMotion = ConfigurableJointMotion.Locked; joint.yMotion = ConfigurableJointMotion.Locked; joint.zMotion = ConfigurableJointMotion.Locked; var toCenter = -transform.localPosition; // center position relative to object joint.anchor = transform.InverseTransformDirection(toCenter);
// map velocity to sphere surface rigidbody.velocity = Quaternion.LookRotation(toCenter) * startVel; } Each object keeps its original distance to the center, but is free to move to any direction. It can also rotate around itself when colliding with other objects - but only around the axis object-center (that's the only restriction).
Thanks for the answer. Using that setup with the anchor positioned in the center of the sphere I can get an orbit. However that's not really what I want. I realize I probably shouldn't have used the word orbit as I want the objects to move freely around the surface of the fictual sphere. I will edit the question to reflect that.
Setting the hinge axis to 0,0,0 made my rigidbody move freely in a spherical surface without any restrictions - that's what you want, no?
Yep, that's what i understand. "fixed distance" and "move freely" should be explained a bit more in detail since both is not possible.
aldonaletto: that is indeed whast I want, but I couldn't get that behavior using your description. I will try again. With "fixed distance" I mean that the distance to the center of the sphere (and the surface) should be constant. With "move freely" I mean that the objects should travel freely around the full surface of the sphere, not just orbit in a circular path.
@fitzo, I tested configurable joints with much better results - the objects really moved freely over the sphere, and reacted to collisions as one could expect. I edited my answer to show this alternative.
Answer by MrSkiz · Jan 12, 2012 at 03:52 PM
Here's my pick on the subject (I'm onto something quite similar :) )
var orbitRef : Transform; var push : float = 10; var attractForce : float = 5; var distanceMini : float = 2;
function FixedUpdate () {
// Planetary gravity
var direction = orbitRef.position - transform.position;
direction.Normalize();
rigidbody.AddForce (direction * attractForce);
// Floating above the surface
var dir = transform.TransformDirection (-Vector3.up);
var finRaycast = transform.localPosition;
finRaycast.y = transform.localPosition.y-distanceMini;
Debug.DrawLine (transform.position, finRaycast, Color.red);
if (Physics.Raycast (transform.position, dir, distanceMini)) {
rigidbody.AddForce (0, push, 0);
}
}
With this attached to your objects, and the orbitRef variable pointing towards the center of you planet you have a simple but working planetary gravity. And the 2nd part of the script "counter-attack" the attraction with a opposite force (var push) if your object is under the minimal distance (var distanceMini) to make your object float upon the surface.
Now, it's yours to make your object move with few addForce more. I'm scratching my head with surface alignment.
Better/smarter solutions must exist, but that's what I've done wih few days of Unity practicing and barely any programmation background. :)
Thanks. Your approach kind of worked, but I got strange behavior after letting it run for awhile, e.g. the object bouncing and rolling on the planet surface. It's probably caused by the fixed force added by:
rigidbody.AddForce (0, push, 0);
I came up with the following similar code:
public Transform orbitRef;
private float suspensionForce = 300f;
private float correctionForce = 100f;
private float startDistance;
// Use this for initialization
void Start () {
startDistance = Vector3.$$anonymous$$agnitude(transform.position - orbitRef.position);
}
void FixedUpdate () {
// Suspend object by using opposing forces. Using VelocityChange to elli$$anonymous$$ate
// dependency on mass
Vector3 direction = orbitRef.position - transform.position;
float currentDistance = Vector3.$$anonymous$$agnitude(direction);
direction.Normalize();
rigidbody.AddForce (direction * suspensionForce, Force$$anonymous$$ode.VelocityChange);
rigidbody.AddForce (-direction * suspensionForce, Force$$anonymous$$ode.VelocityChange);
// Compensate for deviation, note that diff can be negative
float diff = currentDistance - startDistance;
rigidbody.AddForce(diff * correctionForce * direction, Force$$anonymous$$ode.VelocityChange);
}
First I suspend the object with opposing forces and then I compensate for any deviations from the original distance. This kind of works... It gives a nice rotation and manages to keep objects in orbit well, but the force compensation makes the objects oscillate in some situations, typically when moving very slowly. I haven't found a way around that yet, which makes it unusable for me.
Answer by Bunny83 · Jan 12, 2012 at 06:55 PM
Well, it's a bit tricky. In the real world when you orbit a planet you have to move at an exact speed around the planet or you will loose your orbit. if you want a true spherical "orbit" with no variations you have to force the distance manually, but the speed might not be kept around the orbit that way.
public Transform center;
public float orbitRadius = 20.0f;
void FixedUpdate()
{
Vector3 dist = transform.position - center.position;
transform.position = center.position + dist.normalized * orbitRadius;
}
You might be able to correct the velocity after that movement. The velocity vector needs to be rotated so it's tangent of the spherical orbit.
Best way:
Save the velocity value (magnitude)
project the velocity vector onto the current tangent plane (the plane normal is the "dist"-variable)
normalize it and multiply with the saved velocity value.
Thanks. I have other objects moving without physics that I manage to move around the sphere without any problems, but for these objects I need realistic collisions, friction, etc. The problem is that any manual adjustments of the position I have tried interferes with the physics, making it all look very unnatural.
Your answer
Follow this Question
Related Questions
How to Click to Move Sphere (With physics)?* 0 Answers
Activate physics when animation ends 0 Answers
Rigidbody sphere slows down quickly 2 Answers
Sphere never sleeps 1 Answer